<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Tanaike</title>
    <description>The latest articles on DEV Community by Tanaike (@tanaike).</description>
    <link>https://dev.to/tanaike</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3701430%2F3d517296-9b96-418b-ad29-24e735edd1df.png</url>
      <title>DEV Community: Tanaike</title>
      <link>https://dev.to/tanaike</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tanaike"/>
    <language>en</language>
    <item>
      <title>Beyond Passive AI Rewrites and Vibe Coding in Google Docs</title>
      <dc:creator>Tanaike</dc:creator>
      <pubDate>Tue, 27 Oct 2026 05:37:22 +0000</pubDate>
      <link>https://dev.to/gde/beyond-passive-ai-rewrites-and-vibe-coding-in-google-docs-3db6</link>
      <guid>https://dev.to/gde/beyond-passive-ai-rewrites-and-vibe-coding-in-google-docs-3db6</guid>
      <description>&lt;h3&gt;
  
  
  Scaling a Multimodal AI Writing and Code Review Coach Across Classrooms and Research Labs with Gemini and Google Apps Script
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fx04hy6p80fa7zgkcfzkl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fx04hy6p80fa7zgkcfzkl.png" alt="DocuMentor AI Overview Infographic" width="800" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In my previous article—&lt;strong&gt;&lt;a href="https://dev.to/gde/building-a-multimodal-suggestion-based-ai-writing-coach-with-gemini-and-google-docs-ea4"&gt;Building a Multimodal, Suggestion-Based AI Writing Coach with Gemini and Google Docs&lt;/a&gt;&lt;/strong&gt;—I introduced &lt;strong&gt;DocuMentor AI&lt;/strong&gt;, an open-source Google Docs add-on built with &lt;strong&gt;Google Apps Script&lt;/strong&gt;, the &lt;strong&gt;Google Docs API (&lt;code&gt;v1&lt;/code&gt;)&lt;/strong&gt;, and the &lt;strong&gt;Gemini API&lt;/strong&gt; (&lt;code&gt;gemini-3.8-flash&lt;/code&gt; / &lt;code&gt;gemini-4-argon&lt;/code&gt;). Instead of overwriting a writer's draft directly, DocuMentor AI uses the newest &lt;a href="https://developers.google.com/workspace/docs/api/how-tos/suggestions" rel="noopener noreferrer"&gt;Google Docs API Suggestion &amp;amp; Comment Management features&lt;/a&gt; (&lt;code&gt;WriteControl.writeMode = "SUGGEST"&lt;/code&gt;, &lt;code&gt;addCommentReply&lt;/code&gt;, &lt;code&gt;insertComment&lt;/code&gt;, &lt;code&gt;acceptSuggestion&lt;/code&gt;, &lt;code&gt;rejectSuggestion&lt;/code&gt;, and &lt;code&gt;deleteComment&lt;/code&gt;) to turn Google Docs into an interactive, multimodal writing coach.&lt;/p&gt;

&lt;p&gt;Shortly after publishing that first article, I received enthusiastic feedback from educators, Google Developer Experts (GDEs), academic researchers, and enterprise practitioners. While they loved how the tool preserves author agency—especially through &lt;strong&gt;💡 Hint-Only Mode&lt;/strong&gt; and the &lt;strong&gt;🎓 Writing Coach &amp;amp; Learning Summary&lt;/strong&gt; dialog—they raised a crucial practical question for schools, universities, and organizations:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"How can we deploy DocuMentor AI across a school, university lab, or company domain where students and end users are not allowed to access Google AI Studio to create their own Gemini API keys—without risking an API key leak in a shared Google Doc, and without asking non-coder IT administrators to rewrite JavaScript code?"&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;At the same time, while reflecting on how DocuMentor AI's underlying engine works, &lt;strong&gt;I came up with another new idea of my own&lt;/strong&gt;: because DocuMentor AI extracts every line in a Google Doc as an independent block and applies non-destructive inline diffs (&lt;code&gt;WriteMode.SUGGEST&lt;/code&gt;), Socratic hints (&lt;code&gt;insertComment&lt;/code&gt;), and educational replies (&lt;code&gt;addCommentReply&lt;/code&gt;), &lt;strong&gt;what if we use it not only for natural-language manuscripts, but also as an interactive Code Review &amp;amp; Programming Coach for software engineers and computer science students?&lt;/strong&gt; Rather than blindly copy-pasting AI-generated scripts ("vibe coding"), learners could paste source code written in &lt;strong&gt;any programming language&lt;/strong&gt; (Python, JavaScript, TypeScript, Go, Java, C++, Rust, SQL, Google Apps Script, etc.) into Google Docs and learn &lt;em&gt;what&lt;/em&gt; is wrong in their code, &lt;em&gt;why&lt;/em&gt; it fails, and &lt;em&gt;how&lt;/em&gt; to fix it line by line.&lt;/p&gt;

&lt;p&gt;In this follow-up article (Part 2), I present &lt;strong&gt;DocuMentor AI &lt;code&gt;v1.0.3&lt;/code&gt;&lt;/strong&gt; alongside this new multi-language code coaching workflow:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;1. Unified Dual-Mode Deployment (&lt;code&gt;Individual Mode&lt;/code&gt; + &lt;code&gt;School, Research Lab &amp;amp; Organization Mode&lt;/code&gt;):&lt;/strong&gt; With a &lt;strong&gt;single unified codebase&lt;/strong&gt;, DocuMentor AI now seamlessly supports both &lt;strong&gt;👤 Individual Personal Setup (&lt;code&gt;UserProperties&lt;/code&gt;)&lt;/strong&gt; and &lt;strong&gt;🏫 Zero-Code School, Organization &amp;amp; Academic Lab Deployment (&lt;code&gt;ScriptProperties&lt;/code&gt; + Private Google Workspace Marketplace Add-on)&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;2. Multi-Language Interactive Code Review &amp;amp; Script Coaching (My New Use Case for Engineer Training):&lt;/strong&gt; Without changing a single line of the add-on's code, DocuMentor AI serves as a &lt;strong&gt;language-agnostic, GitHub-style inline code reviewer and programming tutor inside Google Docs&lt;/strong&gt;—capable of coaching learners across Python, JavaScript, TypeScript, C++, Go, SQL, Google Apps Script, and more.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Below, I explain the dual-deployment architecture, why this paradigm is transformative for &lt;strong&gt;academia, classrooms, and multi-language software engineer training&lt;/strong&gt;, and walk through three live execution demonstrations: &lt;strong&gt;💡 Hint-Only Mode&lt;/strong&gt;, &lt;strong&gt;🚀 Suggest &amp;amp; Coach Mode&lt;/strong&gt; with resolved review history, and &lt;strong&gt;💻 Interactive Code Review &amp;amp; Script Learning&lt;/strong&gt; (demonstrated with a beginner Google Apps Script example).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub Repository:&lt;/strong&gt; &lt;a href="https://github.com/tanaikech/DocuMentorAI" rel="noopener noreferrer"&gt;https://github.com/tanaikech/DocuMentorAI&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Previous Article (Part 1):&lt;/strong&gt; &lt;a href="https://medium.com/google-cloud/building-a-multimodal-suggestion-based-ai-writing-coach-with-gemini-and-google-docs-ea07244fed99" rel="noopener noreferrer"&gt;Building a Multimodal, Suggestion-Based AI Writing Coach with Gemini and Google Docs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;👤 Individual Installation Guide:&lt;/strong&gt; &lt;a href="https://github.com/tanaikech/DocuMentorAI/blob/main/INSTALL_INDIVIDUAL.md" rel="noopener noreferrer"&gt;INSTALL_INDIVIDUAL.md&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;🏫 School, University &amp;amp; Organization Deployment Guide:&lt;/strong&gt; &lt;a href="https://github.com/tanaikech/DocuMentorAI/blob/main/INSTALL_ORGANIZATION.md" rel="noopener noreferrer"&gt;INSTALL_ORGANIZATION.md&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  1. The Deployment Challenge in Education, Research Labs &amp;amp; Organizations
&lt;/h2&gt;

&lt;p&gt;When an individual developer or writer uses &lt;strong&gt;DocuMentor AI&lt;/strong&gt; on their personal Google account, pasting a personal Gemini API key into the sidebar (stored in &lt;code&gt;PropertiesService.getUserProperties()&lt;/code&gt;) is quick and straightforward.&lt;/p&gt;

&lt;p&gt;However, in &lt;strong&gt;Google Workspace for Education&lt;/strong&gt;, &lt;strong&gt;university research laboratories&lt;/strong&gt;, and &lt;strong&gt;corporate environments&lt;/strong&gt;, three structural barriers arise:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Google AI Studio Is Restricted for Students &amp;amp; Non-Technical Users:&lt;/strong&gt; In most K–12 schools and universities using Google Workspace for Education, access to Google AI Studio is disabled for student Organizational Units (OUs)—especially for under-18 students—and requiring every student, researcher, or staff member to generate and manage their own API key is a major adoption bottleneck.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sharing a Container-Bound Google Doc Risks API Key Exposure &amp;amp; Wipes &lt;code&gt;ScriptProperties&lt;/code&gt; on Copy:&lt;/strong&gt; If a teacher or administrator simply pastes the script into a shared Google Docs template, any student with &lt;strong&gt;Editor&lt;/strong&gt; access to that document can open &lt;strong&gt;Extensions &amp;gt; Apps Script&lt;/strong&gt; and inspect the project settings. Furthermore, when Google Classroom creates individual student copies of an assignment document (&lt;em&gt;"Make a copy for each student"&lt;/em&gt;), Google Apps Script intentionally strips all &lt;code&gt;ScriptProperties&lt;/code&gt; from the copied files for security reasons.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Most School &amp;amp; Department IT Administrators Are Non-Coders:&lt;/strong&gt; If deploying an internal tool requires editing source code to hardcode credentials or rewrite authentication flows, many school and departmental IT staff will be unable to maintain it.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  2. How &lt;code&gt;v1.0.3&lt;/code&gt; Solves This with a Single Unified Script
&lt;/h2&gt;

&lt;p&gt;To address all three barriers without splitting the project into separate "Personal" and "Enterprise/Edu" editions, &lt;strong&gt;DocuMentor AI &lt;code&gt;v1.0.3&lt;/code&gt;&lt;/strong&gt; introduces two core architectural upgrades in &lt;a href="https://github.com/tanaikech/DocuMentorAI/blob/main/src/code.js" rel="noopener noreferrer"&gt;&lt;code&gt;src/code.js&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://github.com/tanaikech/DocuMentorAI/blob/main/src/index.html" rel="noopener noreferrer"&gt;&lt;code&gt;src/index.html&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  A. Priority-Locked Dual API Key Resolution (&lt;code&gt;ScriptProperties&lt;/code&gt; ➔ &lt;code&gt;UserProperties&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;In &lt;code&gt;v1.0.3&lt;/code&gt;, the backend helper &lt;code&gt;getEffectiveApiKeyInfo_()&lt;/code&gt; checks two storage scopes in strict priority order:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getEffectiveApiKeyInfo_&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;scriptProps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;PropertiesService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getScriptProperties&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;scriptKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;scriptProps&lt;/span&gt;
      &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;scriptProps&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GEMINI_API_KEY&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scriptKey&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;scriptKey&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;scriptKey&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="na"&gt;hasApiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;apiKeySource&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;script&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userProps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;PropertiesService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getUserProperties&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;userProps&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;userProps&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GEMINI_API_KEY&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userKey&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;userKey&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;userKey&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="na"&gt;hasApiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;apiKeySource&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;hasApiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;apiKeySource&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;none&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is how this single function behaves across both environments without a single line of code modification:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;🏫 Admin-Managed Mode (&lt;code&gt;apiKeySource === 'script'&lt;/code&gt;):&lt;/strong&gt; The IT administrator opens the Apps Script visual interface (&lt;strong&gt;Project Settings ⚙️ &amp;gt; Script Properties&lt;/strong&gt;), clicks &lt;strong&gt;Add script property&lt;/strong&gt;, and registers &lt;code&gt;GEMINI_API_KEY&lt;/code&gt; once. Because &lt;code&gt;ScriptProperties&lt;/code&gt; takes top priority, DocuMentor AI automatically locks into Admin-Managed Mode.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;🔒 Locked Student / Organization Sidebar UI:&lt;/strong&gt; When any student, faculty member, or employee opens the sidebar under Admin-Managed Mode, the API key input field and the &lt;strong&gt;Reset&lt;/strong&gt; button are completely hidden and replaced with a locked status badge (&lt;code&gt;🔒 API Key Configured (School / Organization Admin)&lt;/code&gt;). Furthermore, both &lt;code&gt;saveApiKey()&lt;/code&gt; and &lt;code&gt;deleteApiKey()&lt;/code&gt; ignore client-side overrides when &lt;code&gt;apiKeySource === 'script'&lt;/code&gt;—meaning students cannot clear, overwrite, or inspect the organization's API key even from browser developer tools.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;👤 Individual User Mode (&lt;code&gt;apiKeySource === 'user'&lt;/code&gt; or &lt;code&gt;'none'&lt;/code&gt;):&lt;/strong&gt; Individual users do not touch &lt;strong&gt;Project Settings ⚙️ &amp;gt; Script Properties&lt;/strong&gt;, so &lt;code&gt;ScriptProperties&lt;/code&gt; remains empty. The sidebar behaves 100% identically to previous versions: displaying the API key input box on first launch, saving the user's personal key into &lt;code&gt;UserProperties&lt;/code&gt;, and showing &lt;strong&gt;&lt;code&gt;🔒 API Key Configured (Saved)&lt;/code&gt;&lt;/strong&gt; with a &lt;strong&gt;Reset&lt;/strong&gt; button (as shown at the top of the &lt;code&gt;v1.0.3&lt;/code&gt; sidebar in Section 4 below).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  B. Dual Menu Registration for Both Private Marketplace Add-ons and Container-Bound Scripts (&lt;code&gt;onOpen&lt;/code&gt; &amp;amp; &lt;code&gt;onInstall&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;When a script is published as a &lt;strong&gt;Google Workspace Marketplace Editor Add-on&lt;/strong&gt;, Google Docs executes &lt;code&gt;onOpen(e)&lt;/code&gt; in &lt;code&gt;ScriptApp.AuthMode.NONE&lt;/code&gt; until the user interacts with the add-on, and requires menus to be registered via &lt;code&gt;DocumentApp.getUi().createAddonMenu()&lt;/code&gt; (which places the menu under &lt;strong&gt;Extensions &amp;gt; DocuMentor AI&lt;/strong&gt;). Conversely, personal container-bound scripts typically use &lt;code&gt;createMenu('✦ DocuMentor AI')&lt;/code&gt; to display a top-level menu in the Google Docs toolbar.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;v1.0.3&lt;/code&gt;, &lt;code&gt;onOpen(e)&lt;/code&gt; and &lt;code&gt;onInstall(e)&lt;/code&gt; seamlessly support both execution environments in one place:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;onInstall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;onOpen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;onOpen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ui&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;DocumentApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getUi&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createAddonMenu&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;addItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Open Dashboard&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;showSidebar&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;addToUi&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;authMode&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;ScriptApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AuthMode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NONE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createMenu&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;✦ DocuMentor AI&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Open Dashboard&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;showSidebar&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addToUi&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  3. Why DocuMentor AI Is Transformative for Academia, Scientific Research, Higher Education &amp;amp; Engineering Training
&lt;/h2&gt;

&lt;p&gt;While DocuMentor AI is a versatile writing coach for everyday business documents and K–12 classrooms, its architecture—combining &lt;strong&gt;Google Docs API &lt;code&gt;v1&lt;/code&gt; Suggestion/Comment management&lt;/strong&gt; with &lt;strong&gt;Gemini's multimodal reasoning&lt;/strong&gt;—makes it uniquely powerful for &lt;strong&gt;academic researchers, university laboratories, graduate students, scientific authors, and software engineering learners&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fy1nzzbg3k1ipazct27gr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fy1nzzbg3k1ipazct27gr.png" alt="Multimodal Academic and Classroom Coaching Workflow" width="800" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As illustrated in the workflow infographic above, &lt;strong&gt;DocuMentor AI&lt;/strong&gt; operates through a three-stage multimodal pipeline designed specifically for active learning, scholarly rigor, and engineering code review:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Stage 1 — Manuscript, Code &amp;amp; Review History Extraction:&lt;/strong&gt; When a researcher, student, or developer runs the tool on a Google Doc (or a highlighted section via &lt;code&gt;🎯 Proofread Selection Only&lt;/code&gt;), the backend inspects the active document tab via &lt;code&gt;documents.get&lt;/code&gt; with &lt;code&gt;suggestionsViewMode=PREVIEW_SUGGESTIONS_ACCEPTED&lt;/code&gt; and &lt;code&gt;commentsViewMode=COMMENTS_VIEW_MODE_INCLUDED&lt;/code&gt;. It simultaneously extracts &lt;strong&gt;body text and research claims&lt;/strong&gt;, &lt;strong&gt;source code and scripts in any programming language&lt;/strong&gt; (Python, JavaScript, TypeScript, Go, Java, C++, SQL, Google Apps Script, etc., line by line), &lt;strong&gt;structured data tables&lt;/strong&gt; (&lt;code&gt;TABLE_1&lt;/code&gt;, &lt;code&gt;TABLE_2&lt;/code&gt;, ...), &lt;strong&gt;embedded charts and figures&lt;/strong&gt; as binary images (&lt;code&gt;VISUAL_1&lt;/code&gt;, &lt;code&gt;VISUAL_2&lt;/code&gt;, ...), and &lt;strong&gt;both active and resolved review threads&lt;/strong&gt; (including previously accepted or rejected inline edits and resolved comments from advisors, co-authors, or code reviewers).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stage 2 — Gemini File API &amp;amp; Docs API v1 Engine:&lt;/strong&gt; The extracted Markdown context and binary figure images are uploaded in parallel to the &lt;strong&gt;Gemini File API&lt;/strong&gt; (&lt;code&gt;fileData.fileUri&lt;/code&gt;) and analyzed by &lt;code&gt;gemini-4-argon&lt;/code&gt; or &lt;code&gt;gemini-3.8-flash&lt;/code&gt;. During this phase, the engine performs &lt;strong&gt;cross-modal and spec-to-code verification&lt;/strong&gt; (checking whether claims, baselines, or implementation scripts match the numbers, units, and architecture diagrams in tables and figures), conducts &lt;strong&gt;rejection-aware history analysis&lt;/strong&gt; (adapting its edit granularity based on what the author previously accepted or rejected), and prepares &lt;strong&gt;reverse-index line and phrase batch requests&lt;/strong&gt; (&lt;code&gt;startIndex&lt;/code&gt; descending) so multiple edits never shift downstream character offsets.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stage 3 — Interactive Coaching in Google Docs:&lt;/strong&gt; Rather than silently overwriting the manuscript or script, the engine delivers three complementary outputs directly inside Google Docs: &lt;strong&gt;💡 Hint-Only Mode&lt;/strong&gt; (&lt;code&gt;insertComment&lt;/code&gt; writing and debugging hints without spoiling the rewrite), &lt;strong&gt;🚀 Suggest &amp;amp; Coach Mode&lt;/strong&gt; (&lt;code&gt;WriteMode.SUGGEST&lt;/code&gt; surgical prose and code diffs paired with &lt;code&gt;addCommentReply&lt;/code&gt; explanations), and the &lt;strong&gt;🎓 Learning Summary Dialog&lt;/strong&gt; synthesizing overall evaluation, recurring writing and coding habits, and actionable next steps.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This architecture directly addresses five critical challenges across academic research, classroom education, and software engineer training:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Combating "Cognitive Debt" and "Cognitive Surrender" in Academic &amp;amp; Student Writing
&lt;/h3&gt;

&lt;p&gt;Recent cognitive science research has sounded the alarm on passive AI reliance:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;MIT Media Lab Brain Study (June 2025):&lt;/strong&gt; The study &lt;em&gt;"Your Brain on ChatGPT"&lt;/em&gt; demonstrated that passively accepting AI-generated text induces &lt;strong&gt;"cognitive debt,"&lt;/strong&gt; measurably reducing neural connectivity and mental engagement (&lt;a href="https://www.media.mit.edu/projects/your-brain-on-chatgpt/overview/" rel="noopener noreferrer"&gt;Ref&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CMU, Wharton &amp;amp; MIT Studies (2026):&lt;/strong&gt; Subsequent research highlighted the &lt;strong&gt;"shortcut trap"&lt;/strong&gt;—where even 10 minutes of AI reliance reduces subsequent independent problem-solving performance by 20% (&lt;a href="https://japan.cnet.com/article/35247336/" rel="noopener noreferrer"&gt;Ref&lt;/a&gt;)—and &lt;strong&gt;"cognitive surrender,"&lt;/strong&gt; where fluent AI outputs create an illusion of competence that stops students from thinking critically (&lt;a href="https://www.asahi.com/articles/ASVB146BHVB1UHBI03JM.html" rel="noopener noreferrer"&gt;Ref&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nikkei XTECH Survey (October 2026):&lt;/strong&gt; In this survey, &lt;strong&gt;42.1%&lt;/strong&gt; of generative AI users cited &lt;strong&gt;"decline in thinking ability due to AI dependency"&lt;/strong&gt; as their #1 concern (&lt;a href="https://xtech.nikkei.com/atcl/nxt/mag/nc/18/020600010/100500235/" rel="noopener noreferrer"&gt;Ref&lt;/a&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In universities and schools, faculty often hesitate to permit AI tools because conventional rewriters turn students into passive copy-pasters. &lt;strong&gt;DocuMentor AI flips this dynamic&lt;/strong&gt; by requiring authors to either formulate their own revisions from anchored coaching hints (&lt;strong&gt;💡 Hint-Only Mode&lt;/strong&gt;) or critically evaluate each transparent diff paired with its grammatical/logical reason (&lt;strong&gt;🚀 Suggest &amp;amp; Coach Mode&lt;/strong&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  2. True Cross-Modal Consistency Verification (&lt;code&gt;Prose ⇄ Tables ⇄ Figures&lt;/code&gt;) for Research Papers
&lt;/h3&gt;

&lt;p&gt;Academic manuscripts—such as journal articles, conference papers, theses, and grant proposals—never consist of prose alone. One of the most common reasons peer reviewers flag scientific drafts is &lt;strong&gt;cross-modal inconsistency&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Mismatched Baselines &amp;amp; Metrics:&lt;/strong&gt; Numbers or percentages cited in the &lt;em&gt;Results&lt;/em&gt; text do not match the values or baselines in &lt;code&gt;Table 1&lt;/code&gt; or &lt;code&gt;Figure 1&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inconsistent Units &amp;amp; Labels:&lt;/strong&gt; Axis labels, units, or terminology in a bar chart or architecture diagram (&lt;code&gt;Execution Time (Minutes)&lt;/code&gt;) differ from the column headers in a table (&lt;code&gt;Task Time&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Uncited Visuals:&lt;/strong&gt; Figures and tables are inserted into the manuscript without being explicitly cited or interpreted in the narrative.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because &lt;strong&gt;DocuMentor AI&lt;/strong&gt; uploads the manuscript's structured Markdown and binary figure images in parallel to the &lt;strong&gt;Gemini File API&lt;/strong&gt;, &lt;code&gt;gemini-4-argon&lt;/code&gt; inspects the actual visual pixels of your plots alongside your tables and prose—proposing inline cell edits (&lt;code&gt;WriteMode.SUGGEST&lt;/code&gt;) and attaching anchored review comments (&lt;code&gt;insertComment&lt;/code&gt;) directly onto every figure and table.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Distilling Co-Author, Advisor &amp;amp; Peer-Review History into Long-Term Learning (&lt;code&gt;INCLUDE_RESOLVED_HISTORY&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;In academic labs and graduate programs, a Google Doc often accumulates dozens of inline suggestions and comment threads from a principal investigator (PI), thesis advisor, or co-authors. Once a student clicks &lt;strong&gt;Accept&lt;/strong&gt; on the advisor's edits or &lt;strong&gt;Resolve&lt;/strong&gt; on comment threads, that valuable pedagogical feedback normally disappears into the document's history.&lt;/p&gt;

&lt;p&gt;With &lt;strong&gt;DocuMentor AI&lt;/strong&gt;, calling &lt;code&gt;documents.get&lt;/code&gt; with &lt;code&gt;commentsViewMode=COMMENTS_VIEW_MODE_INCLUDED&lt;/code&gt; extracts both &lt;strong&gt;open review threads&lt;/strong&gt; and &lt;strong&gt;resolved review history (&lt;code&gt;RESOLVED&lt;/code&gt;, &lt;code&gt;ACCEPTED&lt;/code&gt;, &lt;code&gt;REJECTED&lt;/code&gt;)&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Step 1 History Ledger:&lt;/strong&gt; Renders an expandable &lt;strong&gt;&lt;code&gt;📜 View Resolved Review History&lt;/code&gt;&lt;/strong&gt; ledger right in the sidebar showing every accepted/rejected edit and resolved comment thread.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rejection-Aware Adaptive Proofreading:&lt;/strong&gt; When &lt;code&gt;📜 Include Resolved Review History&lt;/code&gt; is enabled during proofreading, Gemini respects the author's prior rejections (for instance, switching from full-sentence rewrites to surgical phrase-level edits if the author previously rejected broad rewrites).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Retrospective Learning Summary:&lt;/strong&gt; By turning &lt;strong&gt;ON&lt;/strong&gt; &lt;strong&gt;📜 Include Resolved Review History&lt;/strong&gt; and clicking &lt;strong&gt;🎓 Generate Learning Summary from Suggestions &amp;amp; Comments&lt;/strong&gt;, graduate students and researchers can synthesize their advisor's or co-authors' entire review pass—even after all threads are resolved—into a structured &lt;strong&gt;Writing Coach &amp;amp; Learning Summary&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Protecting Unpublished Research Data &amp;amp; Scaling Across Classrooms
&lt;/h3&gt;

&lt;p&gt;For universities and schools deploying DocuMentor AI via &lt;code&gt;ScriptProperties&lt;/code&gt;, linking the institution's Google Cloud Billing account (Paid Tier 1+) to the Gemini API project provides two essential guarantees:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Strict Academic &amp;amp; Student Data Privacy:&lt;/strong&gt; Under Google's Gemini API Terms of Service for paid/billed projects, submitted manuscripts, tables, and unpublished research figures are &lt;strong&gt;never used to train or improve Google models&lt;/strong&gt;—preserving academic originality and student privacy. Additionally, DocuMentor AI automatically deletes all temporary files from the Gemini File API inside a &lt;code&gt;finally&lt;/code&gt; block (&lt;code&gt;deleteGeminiFiles_&lt;/code&gt;) immediately after each request.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;High Concurrent Throughput &amp;amp; Distributed GAS Quotas:&lt;/strong&gt; Paid Tier rate limits allow an entire classroom of 30–40 students or a full research department to run multimodal proofreading simultaneously without hitting Free Tier RPM bottlenecks, while Google Apps Script &lt;code&gt;UrlFetchApp&lt;/code&gt; quotas are counted per active student/researcher account (20,000–100,000 calls/day per user) rather than sharing a single user's quota.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  5. Beyond Prose: Why DocuMentor AI Is Ideal for Multi-Language Script Learning, Code Review &amp;amp; Engineer Training
&lt;/h3&gt;

&lt;p&gt;While designing and testing DocuMentor AI, I realized that its core mechanism is not limited to natural-language essays or academic papers—it is equally powerful for &lt;strong&gt;coaching programming scripts across any language (Python, JavaScript, TypeScript, Go, Java, C++, Rust, SQL, Bash, Google Apps Script, and more)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Just as passive AI text rewriting leads to "cognitive surrender" in essay writing, software engineering education, university computer science courses, and corporate DX (citizen-developer) training face an identical crisis: &lt;strong&gt;"vibe coding"&lt;/strong&gt;—where beginners and junior engineers blindly copy and paste AI-generated code without understanding &lt;em&gt;what&lt;/em&gt; was wrong in their original script, &lt;em&gt;why&lt;/em&gt; it failed at runtime, or &lt;em&gt;how&lt;/em&gt; the underlying language or API actually works.&lt;/p&gt;

&lt;p&gt;When we examine both the pedagogical workflow and the internal implementation of &lt;a href="https://github.com/tanaikech/DocuMentorAI/blob/main/src/code.js" rel="noopener noreferrer"&gt;&lt;code&gt;src/code.js&lt;/code&gt;&lt;/a&gt;, three reasons make &lt;strong&gt;DocuMentor AI&lt;/strong&gt; uniquely suited for &lt;strong&gt;multi-language script learning and educational code reviews inside Google Docs&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;1. Transforming Passive Copy-Pasting into Active, Language-Agnostic Engineering Mentorship:&lt;/strong&gt; Instead of handing over a rewritten file that replaces the learner's entire script at once, DocuMentor AI supports two active-learning code review modes for any programming language. In &lt;strong&gt;💡 Hint-Only Mode (&lt;code&gt;insertComment&lt;/code&gt;)&lt;/strong&gt;, it acts like a &lt;strong&gt;Socratic senior engineer mentor&lt;/strong&gt;—highlighting the buggy, inefficient, or insecure line of code and explaining the root cause (e.g., an off-by-one error, SQL injection risk, memory leak, unclosed resource, or API misuse) so the learner can debug and refactor the line themselves first. In &lt;strong&gt;🚀 Suggest &amp;amp; Coach Mode (&lt;code&gt;WriteMode.SUGGEST&lt;/code&gt; + &lt;code&gt;addCommentReply&lt;/code&gt;)&lt;/strong&gt;, it acts like an &lt;strong&gt;educational GitHub Pull Request inline diff&lt;/strong&gt;—crossing out the problematic line, proposing the idiomatic replacement in that target language inline, and attaching a reply with the &lt;strong&gt;&lt;code&gt;💡 Reason for Revision&lt;/code&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;code&gt;🎓 Learning Point&lt;/code&gt;&lt;/strong&gt;, followed by a &lt;strong&gt;&lt;code&gt;🎓 Writing Coach &amp;amp; Learning Summary&lt;/code&gt;&lt;/strong&gt; that diagnoses recurring coding habits across the entire script.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;2. High Technical Affinity with DocuMentor AI's Line-Block &amp;amp; Reverse-Index Engine (&lt;code&gt;src/code.js&lt;/code&gt;):&lt;/strong&gt; Why does Google Docs inline suggestion mode work so cleanly on source code regardless of the programming language? When any script is pasted into Google Docs with normal line breaks (&lt;code&gt;Enter&lt;/code&gt;), Step 1 extracts each line of code as an independent paragraph block in &lt;code&gt;editableBlocks&lt;/code&gt; (&lt;code&gt;[Block N (Body)]&lt;/code&gt;). In Step 2, Gemini's strict schema rule (&lt;code&gt;each "originalText" MUST be an exact, character-for-character substring found within "Target Editable Settled Text Blocks" without newlines&lt;/code&gt;) naturally guides the model to target &lt;strong&gt;self-contained, single-line bugs and anti-patterns&lt;/strong&gt;. Then, in Step 3 (&lt;code&gt;step3_buildRequestBody()&lt;/code&gt;), all matched code lines are sorted in &lt;strong&gt;reverse character-index order (&lt;code&gt;b.startIndex - a.startIndex&lt;/code&gt;)&lt;/strong&gt; and updated via &lt;code&gt;deleteContentRange&lt;/code&gt; + &lt;code&gt;insertText&lt;/code&gt; under &lt;code&gt;WriteControl.writeMode = "SUGGEST"&lt;/code&gt;. As a result, indentation (even in whitespace-sensitive languages like Python!) and character offsets never shift, and clicking &lt;strong&gt;&lt;code&gt;✅ Accept All Suggestions&lt;/code&gt;&lt;/strong&gt; immediately produces a &lt;strong&gt;100% syntactically valid, runnable script&lt;/strong&gt; that can be copied straight into an IDE or code editor!&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;3. Multimodal Specification-to-Code Verification (&lt;code&gt;Architecture Diagram + API Spec Table + Script&lt;/code&gt;):&lt;/strong&gt; Traditional code linters and IDE extensions only see raw text files. Because DocuMentor AI is powered by the &lt;strong&gt;Gemini File API's multimodal pipeline&lt;/strong&gt;, an instructor, tech lead, or engineer can place a &lt;strong&gt;system architecture diagram (&lt;code&gt;Figure 1&lt;/code&gt;)&lt;/strong&gt;, an &lt;strong&gt;API specification or database schema table (&lt;code&gt;Table 1&lt;/code&gt;)&lt;/strong&gt;, and &lt;strong&gt;the implementation code (in Python, TypeScript, Go, GAS, etc.)&lt;/strong&gt; inside a single Google Doc—allowing Gemini to simultaneously review the code for bugs &lt;em&gt;and&lt;/em&gt; verify whether the implementation accurately matches the architecture diagram and specification table!&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  4. Live Demonstration (&lt;code&gt;v1.0.3&lt;/code&gt;): Inside the Sidebar Dashboard, Prose Modes &amp;amp; Interactive Code Review
&lt;/h2&gt;

&lt;p&gt;To see how &lt;strong&gt;DocuMentor AI &lt;code&gt;v1.0.3&lt;/code&gt;&lt;/strong&gt; works in practice across both &lt;strong&gt;multimodal technical manuscripts&lt;/strong&gt; (body text, &lt;code&gt;Table 1&lt;/code&gt;, and &lt;code&gt;Figure 1&lt;/code&gt;) and &lt;strong&gt;programming scripts&lt;/strong&gt; (using a beginner Google Apps Script as our concrete code review example), let's first look at the &lt;strong&gt;&lt;code&gt;v1.0.3&lt;/code&gt; Sidebar Control Center&lt;/strong&gt;, and then walk through three live execution cases.&lt;/p&gt;

&lt;h3&gt;
  
  
  Inside the &lt;code&gt;v1.0.3&lt;/code&gt; Sidebar Dashboard (&lt;code&gt;sidebar_v103.jpg&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdimr304zlik4344bbvcb.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdimr304zlik4344bbvcb.jpg" alt="DocuMentor AI v1.0.3 Sidebar Dashboard" width="472" height="2014"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As shown in &lt;a href="https://tanaikech.github.io/image-storage/20261027a/sidebar_v103.jpg" rel="noopener noreferrer"&gt;&lt;code&gt;sidebar_v103.jpg&lt;/code&gt;&lt;/a&gt; above, the entire workflow is controlled from a clean, four-part sidebar inside Google Docs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;1. Header &amp;amp; Bilingual UI Switcher (&lt;code&gt;EN&lt;/code&gt; / &lt;code&gt;JA&lt;/code&gt;):&lt;/strong&gt; Displays the version badge (&lt;code&gt;v1.0.3 • AI Proofreading Coach&lt;/code&gt;), an info modal trigger (&lt;code&gt;ⓘ&lt;/code&gt;), and a one-click language toggle (&lt;code&gt;EN&lt;/code&gt; / &lt;code&gt;JA&lt;/code&gt;) that switches both the sidebar labels and the language of Gemini's coaching explanations and learning summaries.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;2. &lt;code&gt;⚙️ AI Engine &amp;amp; API Settings&lt;/code&gt;:&lt;/strong&gt; Displays the API key status badge and model selector (&lt;code&gt;gemini-4-argon (High Precision)&lt;/code&gt; vs. &lt;code&gt;gemini-3.8-flash (Fast &amp;amp; Balanced)&lt;/code&gt;). In Individual Mode (&lt;code&gt;UserProperties&lt;/code&gt;), this card shows &lt;code&gt;🔒 API Key Configured (Saved)&lt;/code&gt; with a &lt;code&gt;Reset&lt;/code&gt; link as pictured above; in School/Organization Mode (&lt;code&gt;ScriptProperties&lt;/code&gt;), it automatically locks to &lt;code&gt;🔒 API Key Configured (School / Organization Admin)&lt;/code&gt; and hides the &lt;code&gt;Reset&lt;/code&gt; link.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;3. &lt;code&gt;✍️ Proofreading Scope &amp;amp; Controls&lt;/code&gt;:&lt;/strong&gt; Houses the three independent pedagogical toggles—&lt;code&gt;🎯 Proofread Selection Only&lt;/code&gt; (here &lt;strong&gt;OFF&lt;/strong&gt; to inspect the entire active tab), &lt;code&gt;💡 Hint-Only Mode (Comment Without Rewrite)&lt;/code&gt; (here turned &lt;strong&gt;ON&lt;/strong&gt; with an amber border), and &lt;code&gt;📜 Include Resolved Review History&lt;/code&gt; (here turned &lt;strong&gt;ON&lt;/strong&gt; with an emerald border)—followed by custom instructions, quick preset chips (&lt;code&gt;#Concise &amp;amp; Logical&lt;/code&gt;, &lt;code&gt;#Business Tone&lt;/code&gt;, &lt;code&gt;#Tables &amp;amp; Charts&lt;/code&gt;, &lt;code&gt;#Grammar &amp;amp; Flow&lt;/code&gt;), and action buttons (&lt;code&gt;💡 Run Proofreading (Hint-Only Comments)&lt;/code&gt;, &lt;code&gt;🎓 Generate Learning Summary from Suggestions &amp;amp; Comments&lt;/code&gt;, &lt;code&gt;📝 Generate Initial Text from Prompt&lt;/code&gt;, &lt;code&gt;✅ Accept All Suggestions&lt;/code&gt;, &lt;code&gt;🧹 Clear All Suggestions&lt;/code&gt;, and &lt;code&gt;🗑️ Clear All Comments&lt;/code&gt;). Notice how toggling &lt;strong&gt;ON&lt;/strong&gt; &lt;code&gt;💡 Hint-Only Mode&lt;/code&gt; dynamically updates the primary button label from &lt;code&gt;🚀 Run Proofreading (Suggest &amp;amp; Coach)&lt;/code&gt; to &lt;code&gt;💡 Run Proofreading (Hint-Only Comments)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;4. &lt;code&gt;📊 PROCESS DASHBOARD&lt;/code&gt; (Steps 1–4):&lt;/strong&gt; Tracks the real-time 4-stage pipeline (&lt;code&gt;1 Fetch Text, Tables &amp;amp; Figures&lt;/code&gt;, &lt;code&gt;2 Generate AI Proofreading &amp;amp; Coaching&lt;/code&gt;, &lt;code&gt;3 Build Suggestion Request Body&lt;/code&gt;, and &lt;code&gt;4 Apply Suggestions &amp;amp; Comments&lt;/code&gt;), shown here in its initial &lt;code&gt;Waiting&lt;/code&gt; state right before clicking run.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Choosing the Right Model: &lt;code&gt;Gemini 3.8 Flash&lt;/code&gt; vs. &lt;code&gt;Gemini 4 Argon&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;In the &lt;strong&gt;&lt;code&gt;⚙️ AI Engine &amp;amp; API Settings&lt;/code&gt;&lt;/strong&gt; card of the sidebar, you can switch between &lt;strong&gt;&lt;code&gt;gemini-3.8-flash (Fast &amp;amp; Balanced)&lt;/code&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;code&gt;gemini-4-argon (High Precision)&lt;/code&gt;&lt;/strong&gt; at any time. Because DocuMentor AI supports both &lt;strong&gt;natural-language manuscript proofreading&lt;/strong&gt; and &lt;strong&gt;multi-language code review&lt;/strong&gt;, choosing the right model for your current stage of work maximizes both speed and analytical depth.&lt;/p&gt;

&lt;p&gt;The table below summarizes the recommended decision criteria across writing, research, and software engineering workflows:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario / Use Case&lt;/th&gt;
&lt;th&gt;Domain&lt;/th&gt;
&lt;th&gt;Recommended Model&lt;/th&gt;
&lt;th&gt;Why &amp;amp; Key Selection Criteria&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;1. Typo, Spelling &amp;amp; Grammar Check&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✍️ Prose&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;⚡ &lt;code&gt;Gemini 3.8 Flash&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;&lt;em&gt;(Fast &amp;amp; Balanced)&lt;/em&gt;
&lt;/td&gt;
&lt;td&gt;Fixing typos, spelling mistakes, and clear grammatical errors relies primarily on pattern recognition—making Flash's high speed and low token cost the optimal choice.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;2. Routine Code Refactoring&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;💻 Code&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;⚡ &lt;code&gt;Gemini 3.8 Flash&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;&lt;em&gt;(Fast &amp;amp; Balanced)&lt;/em&gt;
&lt;/td&gt;
&lt;td&gt;Standardizing naming conventions, adding comments/docstrings, and rewriting simple functions without complex algorithmic changes complete almost instantaneously with Flash.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;3. Rapid Iterative Drafting &amp;amp; Interactive Checks&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✍️ Prose / 💻 Code&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;⚡ &lt;code&gt;Gemini 3.8 Flash&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;&lt;em&gt;(Fast &amp;amp; Balanced)&lt;/em&gt;
&lt;/td&gt;
&lt;td&gt;When actively drafting or iterating in the editor and wanting fast, rhythmic feedback or brainstorming without breaking your creative flow, Flash's ultra-low latency provides a stress-free experience.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;4. First-Pass Triage of Large Documents or Long Scripts&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✍️ Prose / 💻 Code&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;⚡ &lt;code&gt;Gemini 3.8 Flash&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;&lt;em&gt;(Fast &amp;amp; Balanced)&lt;/em&gt;
&lt;/td&gt;
&lt;td&gt;Ideal for cost-effectively screening long manuscripts or large scripts to surface broad errors and recurring issues in a single initial pass.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;5. Logical Consistency in Academic Papers, Contracts &amp;amp; Multimodal Figures&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✍️ Prose &amp;amp; 🖼️ Visuals&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;🧠 &lt;code&gt;Gemini 4 Argon&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;&lt;em&gt;(High Precision)&lt;/em&gt;
&lt;/td&gt;
&lt;td&gt;Best when deep contextual reasoning is required to detect contradictions across claims, flawed logical progressions, subtle loopholes in legal/contractual phrasing, or cross-modal mismatches between body text, tables, and figures.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;6. Complex Algorithm &amp;amp; Data Structure Review&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;💻 Code&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;🧠 &lt;code&gt;Gemini 4 Argon&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;&lt;em&gt;(High Precision)&lt;/em&gt;
&lt;/td&gt;
&lt;td&gt;Shines in code reviews that require deep analytical thinking—such as optimizing time and space complexity, designing advanced data structures, and verifying subtle edge-case bugs.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;7. Security Auditing &amp;amp; Vulnerability Detection&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;💻 Code&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;🧠 &lt;code&gt;Gemini 4 Argon&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;&lt;em&gt;(High Precision)&lt;/em&gt;
&lt;/td&gt;
&lt;td&gt;Ideal for deeply tracing execution paths to uncover concurrency bugs (race conditions), injection vulnerabilities, credential exposure, or subtle security flaws hidden in source code.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;8. Nuanced Tone, Style &amp;amp; Psychological Audience Adaptation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✍️ Prose&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;🧠 &lt;code&gt;Gemini 4 Argon&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;&lt;em&gt;(High Precision)&lt;/em&gt;
&lt;/td&gt;
&lt;td&gt;Powerful for executing abstract, high-level editorial instructions beyond surface proofreading—such as rewriting text to achieve a specific psychological effect or persuasive tone tailored to a target audience.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 Practical Two-Pass Workflow Tip:&lt;/strong&gt; For long academic papers or complex scripts, start with &lt;strong&gt;&lt;code&gt;gemini-3.8-flash&lt;/code&gt;&lt;/strong&gt; for a fast, low-cost &lt;strong&gt;First-Pass Triage&lt;/strong&gt; (cleaning up typos, basic grammar, and naming conventions), and then switch to &lt;strong&gt;&lt;code&gt;gemini-4-argon&lt;/code&gt;&lt;/strong&gt; for your &lt;strong&gt;Deep Second Pass&lt;/strong&gt; (verifying thesis/contract logic, cross-checking &lt;code&gt;Table 1&lt;/code&gt; and &lt;code&gt;Figure 1&lt;/code&gt;, or auditing algorithm complexity and security vulnerabilities).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Case A: Running "💡 Hint-Only Mode" (Active-Recall Coaching Without Spoiling the Answer)
&lt;/h3&gt;

&lt;p&gt;With the sidebar configured exactly as shown in &lt;a href="https://tanaikech.github.io/image-storage/20261027a/sidebar_v103.jpg" rel="noopener noreferrer"&gt;&lt;code&gt;sidebar_v103.jpg&lt;/code&gt;&lt;/a&gt; above (&lt;code&gt;gemini-4-argon (High Precision)&lt;/code&gt;, &lt;code&gt;💡 Hint-Only Mode&lt;/code&gt; &lt;strong&gt;ON&lt;/strong&gt;, and &lt;code&gt;📜 Include Resolved Review History&lt;/code&gt; &lt;strong&gt;ON&lt;/strong&gt;), we click &lt;strong&gt;&lt;code&gt;💡 Run Proofreading (Hint-Only Comments)&lt;/code&gt;&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjcfvqbaz6ju22pmg8hkn.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjcfvqbaz6ju22pmg8hkn.jpg" alt="Hint-Only Mode Execution Result in DocuMentor AI v1.0.3" width="800" height="625"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Look closely at what happens across the three panes in &lt;a href="https://tanaikech.github.io/image-storage/20261027a/hint_only_mode_result.jpg" rel="noopener noreferrer"&gt;&lt;code&gt;hint_only_mode_result.jpg&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Zero Forced Rewrites on the Document Canvas (Left Pane):&lt;/strong&gt; Not a single word of the author's manuscript is crossed out or overwritten with green suggestion text. Instead, phrases requiring attention—such as &lt;code&gt;"is basically a coding platform that is provided by Google, and it is made based on the JavaScript language."&lt;/code&gt;, &lt;code&gt;"The most biggest advantage of using GAS is because"&lt;/code&gt;, &lt;code&gt;"connect and integrate"&lt;/code&gt;, &lt;code&gt;"boring daily routine works."&lt;/code&gt;, &lt;code&gt;"Target App"&lt;/code&gt; in &lt;code&gt;Table 1&lt;/code&gt;, and &lt;code&gt;Figure 1&lt;/code&gt;—are highlighted in yellow as anchored Google Docs comments (&lt;code&gt;insertComment&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Socratic Coaching Comments in the Comment Stream (Center Pane):&lt;/strong&gt; Looking at the expanded top comment card (&lt;code&gt;[DocuMentor AI | 💡 Hint Mode: Conciseness]&lt;/code&gt;), DocuMentor AI identifies the exact &lt;strong&gt;&lt;code&gt;🎯 Flagged Phrase&lt;/code&gt;&lt;/strong&gt;, explains the &lt;strong&gt;&lt;code&gt;💡 Issue to Check&lt;/code&gt;&lt;/strong&gt; (&lt;em&gt;"The conversational filler word 'basically' and the passive clauses ('that is provided by', 'and it is made based on') make this opening technical definition overly wordy"&lt;/em&gt;), and provides a &lt;strong&gt;&lt;code&gt;🎓 Coaching Hint (Try Rewriting Yourself!)&lt;/code&gt;&lt;/strong&gt; (&lt;em&gt;"Try removing the filler adverb and tightening the two passive clauses into concise modifiers (for example, using a compound adjective for the language and a past participle phrase for the provider)"&lt;/em&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;On-Demand Reference Rewrites &amp;amp; Learning Summary (Right Sidebar):&lt;/strong&gt; Students or researchers can attempt their own revision in the document first, and only click &lt;strong&gt;&lt;code&gt;👁️ Show Reference Rewrite&lt;/code&gt;&lt;/strong&gt; in the sidebar if they get stuck.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Immediately after Hint-Only Mode finishes, DocuMentor AI pops up the &lt;strong&gt;&lt;code&gt;🎓 Writing Coach &amp;amp; Learning Summary&lt;/code&gt;&lt;/strong&gt; modal dialog (which can be re-opened anytime via &lt;strong&gt;&lt;code&gt;📖 Re-open Latest Learning Summary Dialog&lt;/code&gt;&lt;/strong&gt;). Here is the exact summary output produced for this Hint-Only session:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[🎓 Writing Coach &amp;amp; Learning Summary (DocuMentor AI v1.0.3)]

■ 📋 Overall Manuscript Evaluation
This manuscript clearly outlines the core strengths of Google Apps Script (GAS) and how it integrates with Google Workspace and the Gemini AI API. However, several grammatical errors, redundant modifiers, uncountable noun pluralizations, and overly conversational expressions remain in the body text. Additionally, the table and Figure 1 contain valuable quantitative comparisons (such as the 120-minute manual baseline and 96% time savings) that should be better aligned with the surrounding narrative. Because Hint-Only Mode is active, each flagged phrase includes a clear explanation of the underlying issue and a constructive coaching hint so you can practice revising the sentences yourself.

■ 💡 What Was Flagged &amp;amp; Hinted
Flagged grammatical errors (double superlatives, uncountable nouns like 'software' and 'work', and 'the advantage is because'), redundant word pairings ('connect and integrate', 'many various'), and awkward direct translations ('there is a merit that', 'learning hurdle is low', 'very recommended'). Also provided review comments on aligning Table 1 and Figure 1 with the main text.

■ 🔍 Areas for Improvement &amp;amp; Writing Habits
1. Stacking redundant modifiers or intensifiers (e.g., 'most biggest', 'many various', 'super simple', 'totally... at all'). 2. Pluralizing uncountable nouns ('softwares', 'routine works'). 3. Using literal translations of non-English idioms ('there is a merit that', 'learning hurdle is low') instead of natural English collocations.

■ 🌱 Actionable Advice for Future Writing
1. Check noun countability: remember that 'software' and 'work' (meaning labor) are uncountable; use 'software' and 'tasks' instead. 2. Pair 'The advantage is...' with a 'that'-clause or noun phrase rather than 'because'. 3. Use standard collocations like 'gentle learning curve' and 'highly recommended', and explicitly cite your figures and tables in the body text.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice how the Hint-Only Learning Summary does not merely list isolated typos—it diagnoses the writer's underlying &lt;strong&gt;structural habits&lt;/strong&gt; (stacking redundant intensifiers, pluralizing uncountable nouns like &lt;code&gt;'softwares'&lt;/code&gt; and &lt;code&gt;'works'&lt;/code&gt;, and translating non-English idioms literally like &lt;code&gt;'there is a merit that'&lt;/code&gt; or &lt;code&gt;'learning hurdle is low'&lt;/code&gt;) and reminds the author to align the quantitative metrics in &lt;code&gt;Table 1&lt;/code&gt; (120-minute manual baseline) and &lt;code&gt;Figure 1&lt;/code&gt; (96% time savings) with the main narrative.&lt;/p&gt;




&lt;h3&gt;
  
  
  Case B: Running "🚀 Suggest &amp;amp; Coach Mode" with Resolved Review History (Rejection-Aware Surgical Edits)
&lt;/h3&gt;

&lt;p&gt;Next, let's see what happens when we turn &lt;strong&gt;OFF&lt;/strong&gt; &lt;strong&gt;&lt;code&gt;💡 Hint-Only Mode&lt;/code&gt;&lt;/strong&gt;, keep &lt;strong&gt;&lt;code&gt;📜 Include Resolved Review History&lt;/code&gt;&lt;/strong&gt; turned &lt;strong&gt;ON&lt;/strong&gt;, and click &lt;strong&gt;&lt;code&gt;🚀 Run Proofreading (Suggest &amp;amp; Coach)&lt;/code&gt;&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7ifm0xmsqhlml1gldyjh.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7ifm0xmsqhlml1gldyjh.jpg" alt="Suggest &amp;amp; Coach Mode Execution Result in DocuMentor AI v1.0.3" width="800" height="625"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Examining &lt;a href="https://tanaikech.github.io/image-storage/20261027a/suggest_mode_result.jpg" rel="noopener noreferrer"&gt;&lt;code&gt;suggest_mode_result.jpg&lt;/code&gt;&lt;/a&gt; reveals three powerful capabilities working together:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Rejection-Aware Surgical Edits (&lt;code&gt;WriteMode.SUGGEST&lt;/code&gt;) on the Canvas (Left Pane):&lt;/strong&gt; In an earlier review pass on this document, full-sentence rewrites had been rejected by the author (stored in the document's resolved history). Because &lt;strong&gt;&lt;code&gt;📜 Include Resolved Review History&lt;/code&gt;&lt;/strong&gt; is active, DocuMentor AI detects that preference and automatically shifts from broad full-sentence rewrites to &lt;strong&gt;surgical, phrase-level inline suggestions&lt;/strong&gt; that preserve the author's natural voice! Specifically, it proposes targeted replacements for &lt;code&gt;"The most biggest advantage of using GAS is because"&lt;/code&gt; ➔ &lt;code&gt;"The biggest advantage of using GAS is that"&lt;/code&gt;, &lt;code&gt;"connect and integrate with many various"&lt;/code&gt; ➔ &lt;code&gt;"integrate with various"&lt;/code&gt;, &lt;code&gt;"boring daily routine works."&lt;/code&gt; ➔ &lt;code&gt;"repetitive daily tasks."&lt;/code&gt;, &lt;code&gt;"special softwares"&lt;/code&gt; ➔ &lt;code&gt;"special software"&lt;/code&gt;, &lt;code&gt;"There is a merit that you can"&lt;/code&gt; ➔ &lt;code&gt;"A key benefit is that you can"&lt;/code&gt;, &lt;code&gt;"learning hurdle is low,"&lt;/code&gt; ➔ &lt;code&gt;"learning curve is gentle,"&lt;/code&gt;, and &lt;code&gt;"is very recommended."&lt;/code&gt; ➔ &lt;code&gt;"is highly recommended."&lt;/code&gt;, alongside anchored cross-modal review comments (&lt;code&gt;insertComment&lt;/code&gt;) on &lt;code&gt;Table 1&lt;/code&gt; (&lt;code&gt;"Target App"&lt;/code&gt;) and &lt;code&gt;Figure 1&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Native Suggestion Cards Paired with Educational Replies (Center Pane):&lt;/strong&gt; In the Google Docs margin, every native &lt;code&gt;Replace: ... with ...&lt;/code&gt; suggestion card is automatically enriched with an educational reply via &lt;code&gt;addCommentReply&lt;/code&gt;. For example, on the first card (&lt;code&gt;Replace: “The most biggest advantage of using GAS is because” with “The biggest advantage of using GAS is that”&lt;/code&gt;), the reply explains both the &lt;strong&gt;&lt;code&gt;💡 Reason for Revision&lt;/code&gt;&lt;/strong&gt; (&lt;em&gt;"Corrects the double superlative ('most biggest' -&amp;gt; 'biggest') and fixes the ungrammatical construction 'The advantage... is because' by replacing 'because' with 'that'"&lt;/em&gt;) and the transferable &lt;strong&gt;&lt;code&gt;🎓 Learning Point&lt;/code&gt;&lt;/strong&gt; (&lt;em&gt;"Never combine 'most' with an '-est' superlative adjective, and always follow nouns like 'advantage' or 'reason' + 'is' with a 'that'-clause rather than 'because'"&lt;/em&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Granular Accept / Reject Control (Right Sidebar &amp;amp; Native Cards):&lt;/strong&gt; The author can review and accept or reject each suggestion individually or in bulk from the sidebar while reading the exact rationale behind every edit.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is the exact &lt;strong&gt;&lt;code&gt;🎓 Writing Coach &amp;amp; Learning Summary&lt;/code&gt;&lt;/strong&gt; generated after running Suggest &amp;amp; Coach Mode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[🎓 Writing Coach &amp;amp; Learning Summary (DocuMentor AI v1.0.3)]

■ 📋 Overall Manuscript Evaluation
Your manuscript provides a clear, well-structured introduction to Google Apps Script (GAS), effectively combining explanatory text, a summary table (Table 1), and a composite architecture and benchmark figure (Figure 1). Because previous full-sentence rewrites were rejected in favor of targeted, phrase-level improvements, this review focuses on surgical edits that preserve your natural voice while correcting specific grammatical errors (such as double superlatives, uncountable nouns, and redundant pairings) and improving alignment between the text, Table 1, and Figure 1.

■ 🛠️ What Was Revised
Proposed targeted, phrase-level revisions to fix grammatical errors ('most biggest', 'advantage... is because', 'softwares', 'routine works'), eliminate tautologies ('connect and integrate with many various'), refine idiomatic collocations ('gentle learning curve', 'highly recommended'), and improve data consistency across Table 1 and Figure 1.

■ 🔍 Areas for Improvement &amp;amp; Writing Habits
Two recurring habits appear in the draft: (1) stacking redundant modifiers or double intensifiers ('most biggest', 'connect and integrate', 'many various', 'totally... at all'), and (2) pluralizing uncountable English nouns ('softwares', 'works') or translating Japanese idioms literally ('There is a merit that', 'learning hurdle is low', 'very recommended').

■ 🌱 Actionable Advice for Future Writing
1. Treat 'software' and 'work' (meaning labor/duties) as uncountable nouns; use 'software' and 'tasks' instead.
2. Pair 'The advantage is...' with 'that' rather than 'because', and never combine 'most' with '-est' adjectives.
3. Ensure baseline numbers and service labels in your tables and figures (such as the 120-minute manual baseline and Gmail/GmailApp) match across all elements.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look at the second sentence of &lt;code&gt;■ 📋 Overall Manuscript Evaluation&lt;/code&gt; above:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Because previous full-sentence rewrites were rejected in favor of targeted, phrase-level improvements, this review focuses on surgical edits that preserve your natural voice while correcting specific grammatical errors..."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This demonstrates why combining &lt;code&gt;commentsViewMode=COMMENTS_VIEW_MODE_INCLUDED&lt;/code&gt; with Gemini's multimodal reasoning is such a leap forward: &lt;strong&gt;DocuMentor AI learns from what the writer accepts and rejects within the document's history&lt;/strong&gt;, adapting its coaching style to respect the writer's voice while still catching cross-modal discrepancies (such as matching the &lt;code&gt;120-minute&lt;/code&gt; manual baseline and &lt;code&gt;Gmail&lt;/code&gt;/&lt;code&gt;GmailApp&lt;/code&gt; labels across the prose, &lt;code&gt;Table 1&lt;/code&gt;, and &lt;code&gt;Figure 1&lt;/code&gt;).&lt;/p&gt;




&lt;h3&gt;
  
  
  Case C: Beyond Prose — Interactive Multi-Language Code Review &amp;amp; Script Coaching (&lt;code&gt;code_review_mode_result.jpg&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;Finally, let's test the new use case I conceived for &lt;strong&gt;DocuMentor AI&lt;/strong&gt;: &lt;strong&gt;coaching programming scripts and conducting line-by-line code reviews inside Google Docs&lt;/strong&gt;. While this workflow supports &lt;strong&gt;any programming language&lt;/strong&gt; (Python, JavaScript, TypeScript, Go, Java, C++, SQL, etc.), here we demonstrate it using a beginner &lt;strong&gt;Google Apps Script&lt;/strong&gt; snippet (&lt;code&gt;sendDailyReport&lt;/code&gt;) pasted into Google Docs. It represents a common automation task—reading rows from a Google Sheet, summarizing them with the Gemini API, and emailing the summary—written with typical beginner bugs, security risks, and API misuses:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Sample Beginner Google Apps Script: Summarize spreadsheet tasks with Gemini and send an email&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;sendDailyReport&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// 1. Set the Gemini API key and get the target spreadsheet tab&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;apiKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;AIzaSyD-1234567890abcdef_HARDCODED_KEY&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;sheet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;SpreadsheetApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getActiveSpreadsheet&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;getActiveSheet&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="c1"&gt;// 2. Read all task rows from the sheet (Rows 2 to 50, Columns A to C)&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getRange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;A2:C50&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;getValue&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="c1"&gt;// 3. Stop execution if the retrieved task data is empty&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;No task data found.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// 4. Call the Gemini API using UrlFetchApp to summarize today's tasks&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://generativelanguage.googleapis.com/v1beta/models/gemini-3.8-flash:generateContent?key=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="nx"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Summarize these tasks: &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;UrlFetchApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;post&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;payload&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// 5. Send the generated summary to the manager via email&lt;/span&gt;
  &lt;span class="nx"&gt;MailApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sendEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;manager@example.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Daily Task Summary&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;candidates&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;Logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Successfully sent the daily report!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we entered the following prompt into &lt;strong&gt;Additional Proofreading Instructions&lt;/strong&gt; in the sidebar and clicked &lt;strong&gt;&lt;code&gt;🚀 Run Proofreading (Suggest &amp;amp; Coach)&lt;/code&gt;&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Review this Google Apps Script code for bugs, GAS API misuse, security risks, and modern V8 JavaScript best practices, proposing line-by-line code fixes.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is the resulting Google Docs canvas and sidebar after execution (&lt;a href="https://tanaikech.github.io/image-storage/20261027a/code_review_mode_result.jpg" rel="noopener noreferrer"&gt;&lt;code&gt;code_review_mode_result.jpg&lt;/code&gt;&lt;/a&gt;):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F36mnvk94q6k6jvgq9mn4.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F36mnvk94q6k6jvgq9mn4.jpg" alt="Google Apps Script Code Review Result in DocuMentor AI v1.0.3" width="800" height="624"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Look at what &lt;a href="https://tanaikech.github.io/image-storage/20261027a/code_review_mode_result.jpg" rel="noopener noreferrer"&gt;&lt;code&gt;code_review_mode_result.jpg&lt;/code&gt;&lt;/a&gt; achieves across all three panes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;1. Security Fix (&lt;code&gt;PropertiesService&lt;/code&gt; Instead of Hardcoded API Keys):&lt;/strong&gt; On line 4, DocuMentor AI crosses out &lt;code&gt;var apiKey = "AIzaSyD-1234567890abcdef_HARDCODED_KEY";&lt;/code&gt; and suggests &lt;code&gt;const apiKey = PropertiesService.getScriptProperties().getProperty("GEMINI_API_KEY");&lt;/code&gt;—teaching the developer never to hardcode credentials in source files.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;2. GAS &lt;code&gt;Range&lt;/code&gt; API Bug Fix (&lt;code&gt;getValues()&lt;/code&gt; + Blank-Row Filtering Instead of &lt;code&gt;getValue()&lt;/code&gt;):&lt;/strong&gt; On line 8, beginners frequently call &lt;code&gt;.getValue()&lt;/code&gt; (singular) on a multi-cell range like &lt;code&gt;"A2:C50"&lt;/code&gt;, which silently ignores every cell except &lt;code&gt;A2&lt;/code&gt;. DocuMentor AI replaces &lt;code&gt;var data = sheet.getRange("A2:C50").getValue();&lt;/code&gt; with &lt;code&gt;const data = sheet.getRange("A2:C50").getValues().filter(row =&amp;gt; row.some(cell =&amp;gt; cell !== ""));&lt;/code&gt; and updates &lt;code&gt;if (data == "")&lt;/code&gt; on line 11 to &lt;code&gt;if (data.length === 0)&lt;/code&gt;. Look at the expanded comment reply in the center pane (&lt;code&gt;[DocuMentor AI | Bug Fix (GAS API Misuse)]&lt;/code&gt;): it clearly explains &lt;strong&gt;what was wrong and why&lt;/strong&gt; (&lt;em&gt;"&lt;code&gt;getValue()&lt;/code&gt; only returns the value of the top-left cell (&lt;code&gt;A2&lt;/code&gt;), ignoring the rest of &lt;code&gt;A2:C50&lt;/code&gt;. &lt;code&gt;getValues()&lt;/code&gt; retrieves the entire 2D array of rows and columns, and filtering out completely blank rows prevents sending empty rows to the Gemini API"&lt;/em&gt;) and states the &lt;strong&gt;&lt;code&gt;🎓 Learning Point&lt;/code&gt;&lt;/strong&gt; (&lt;em&gt;"Use &lt;code&gt;getValues()&lt;/code&gt; instead of &lt;code&gt;getValue()&lt;/code&gt; when reading a range that spans multiple cells, and filter out empty rows before processing"&lt;/em&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;3. &lt;code&gt;UrlFetchApp.fetch&lt;/code&gt; &amp;amp; &lt;code&gt;HTTPResponse&lt;/code&gt; Bug Fixes:&lt;/strong&gt; On lines 19–20, calling &lt;code&gt;UrlFetchApp.fetch(url, { method: "post", payload: payload })&lt;/code&gt; with a raw object sends form-encoded data instead of JSON, and calling &lt;code&gt;JSON.parse(response)&lt;/code&gt; directly on a GAS &lt;code&gt;HTTPResponse&lt;/code&gt; object fails at runtime. DocuMentor AI suggests adding &lt;code&gt;contentType: "application/json"&lt;/code&gt; with &lt;code&gt;payload: JSON.stringify(payload)&lt;/code&gt; and parsing via &lt;code&gt;JSON.parse(response.getContentText())&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;4. Defensive Optional Chaining (&lt;code&gt;?.&lt;/code&gt;) &amp;amp; Modern V8 Syntax (&lt;code&gt;const&lt;/code&gt;, Template Literals, &lt;code&gt;console.log&lt;/code&gt;):&lt;/strong&gt; On line 23, it replaces unprotected deep property access (&lt;code&gt;result.candidates[0].content.parts[0].text&lt;/code&gt;) with &lt;code&gt;const summary = result.candidates?.[0]?.content?.parts?.[0]?.text || "No summary generated."; MailApp.sendEmail("manager@example.com", "Daily Task Summary", summary);&lt;/code&gt; and upgrades legacy &lt;code&gt;var&lt;/code&gt; and &lt;code&gt;Logger.log()&lt;/code&gt; calls to V8 &lt;code&gt;const&lt;/code&gt;, template literals, and &lt;code&gt;console.log()&lt;/code&gt;. Crucially, if the learner clicks &lt;strong&gt;&lt;code&gt;✅ Accept All Suggestions&lt;/code&gt;&lt;/strong&gt;, the resulting document is a &lt;strong&gt;100% syntactically valid, runnable Google Apps Script&lt;/strong&gt;!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And here is the exact &lt;strong&gt;&lt;code&gt;🎓 Writing Coach &amp;amp; Learning Summary&lt;/code&gt;&lt;/strong&gt; generated for this Google Apps Script review:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[🎓 Writing Coach &amp;amp; Learning Summary (DocuMentor AI v1.0.3)]

■ 📋 Overall Manuscript Evaluation
This Google Apps Script snippet provides a clear high-level workflow for reading spreadsheet tasks, summarizing them with the Gemini API, and sending an email report. However, it contains several critical runtime bugs, a major security vulnerability (hardcoded API key), incorrect Google Apps Script API methods (such as using `getValue()` for a multi-cell range and improperly configuring `UrlFetchApp.fetch` for JSON), and outdated ES5 `var` declarations. Applying the line-by-line fixes below will make the script secure, functional, and compliant with modern V8 JavaScript standards.

■ 🛠️ What Was Revised
Replaced the hardcoded API key with `PropertiesService`, fixed `getValue()` to `getValues()` for multi-row range reading, updated the empty-data check to inspect the 2D array properly, corrected the Gemini model name and `UrlFetchApp` JSON payload configuration, fixed `HTTPResponse` parsing with `getContentText()`, added optional chaining for safer response access, and modernized variable declarations from `var` to `const`.

■ 🔍 Areas for Improvement &amp;amp; Writing Habits
The script shows common beginner pitfalls in Google Apps Script: confusing single-cell and multi-cell `Range` methods (`getValue` vs. `getValues`), assuming `UrlFetchApp.fetch` automatically serializes JavaScript objects as JSON and returns a string directly, hardcoding sensitive credentials in source code, and using legacy `var` syntax instead of V8's `const`/`let`.

■ 🌱 Actionable Advice for Future Writing
1. Always store API keys in Script Properties (`PropertiesService.getScriptProperties()`). 2. When working with multi-cell ranges, use `getValues()` and remember it returns a 2D array (`[][]`) even for empty cells. 3. When calling REST APIs with `UrlFetchApp`, always set `contentType: 'application/json'`, stringify the payload (`JSON.stringify()`), and call `.getContentText()` on the response before parsing.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of blindly copying a rewritten block of code from a chatbot without knowing what changed, an aspiring engineer or computer science student gets an &lt;strong&gt;interactive line-by-line code review right inside Google Docs&lt;/strong&gt;—learning &lt;em&gt;what&lt;/em&gt; was broken, &lt;em&gt;why&lt;/em&gt; it failed, and &lt;em&gt;how&lt;/em&gt; to write production-grade Google Apps Script in the future.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Comparison: Individual Mode vs. School, Organization &amp;amp; Academic Lab Mode
&lt;/h2&gt;

&lt;p&gt;Both modes run from the &lt;strong&gt;exact same three files&lt;/strong&gt; in &lt;a href="https://github.com/tanaikech/DocuMentorAI/tree/main/src" rel="noopener noreferrer"&gt;&lt;code&gt;src/&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;👤 Individual Mode (Personal Setup)&lt;/th&gt;
&lt;th&gt;🏫 School, Organization &amp;amp; Academic Lab Mode&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Target Users&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Individual writers, independent researchers, developers, bloggers.&lt;/td&gt;
&lt;td&gt;K–12 schools, university classes, academic research labs, corporate teams.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Deployment Type&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Container-bound script inside a Google Doc (&lt;code&gt;Extensions &amp;gt; Apps Script&lt;/code&gt;).&lt;/td&gt;
&lt;td&gt;Standalone script published as a &lt;strong&gt;Private (Domain-Internal)&lt;/strong&gt; Google Workspace Marketplace Editor Add-on.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Code Changes Required&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;None&lt;/strong&gt; (copy &amp;amp; paste &lt;a href="https://github.com/tanaikech/DocuMentorAI/tree/main/src" rel="noopener noreferrer"&gt;&lt;code&gt;src/&lt;/code&gt;&lt;/a&gt; as-is).&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;None&lt;/strong&gt; (copy &amp;amp; paste the exact same &lt;a href="https://github.com/tanaikech/DocuMentorAI/tree/main/src" rel="noopener noreferrer"&gt;&lt;code&gt;src/&lt;/code&gt;&lt;/a&gt; files as-is).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Where the Gemini API Key Is Stored&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;PropertiesService.getUserProperties()&lt;/code&gt; (entered by the user in the sidebar UI).&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;PropertiesService.getScriptProperties()&lt;/code&gt; (entered once by the IT Admin in &lt;strong&gt;Project Settings ⚙️ &amp;gt; Script Properties&lt;/strong&gt;).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Sidebar API Key UI&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Shows input box on first run; once saved, shows &lt;code&gt;🔒 API Key Configured (Saved)&lt;/code&gt; + &lt;strong&gt;Reset&lt;/strong&gt; button.&lt;/td&gt;
&lt;td&gt;Automatically locked: shows &lt;code&gt;🔒 API Key Configured (School / Organization Admin)&lt;/code&gt; with &lt;strong&gt;no input box and no Reset button&lt;/strong&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;API Key Security&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Private to the individual user's Google account.&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;100% isolated:&lt;/strong&gt; Students/end users cannot open &lt;code&gt;Apps Script&lt;/code&gt;, cannot view &lt;code&gt;ScriptProperties&lt;/code&gt;, and never need Google AI Studio.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Document Availability&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Available in the Google Doc where the script was pasted.&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Available in every Google Doc&lt;/strong&gt; across the school/lab domain (installed automatically via Google Admin Console for OUs).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Step-by-Step Guide&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;👉 &lt;strong&gt;&lt;a href="https://github.com/tanaikech/DocuMentorAI/blob/main/INSTALL_INDIVIDUAL.md" rel="noopener noreferrer"&gt;&lt;code&gt;INSTALL_INDIVIDUAL.md&lt;/code&gt;&lt;/a&gt;&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;👉 &lt;strong&gt;&lt;a href="https://github.com/tanaikech/DocuMentorAI/blob/main/INSTALL_ORGANIZATION.md" rel="noopener noreferrer"&gt;&lt;code&gt;INSTALL_ORGANIZATION.md&lt;/code&gt;&lt;/a&gt;&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  6. Quick Walkthrough of the Two Setup Workflows
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Workflow A: Individual Setup (3 Minutes)
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Open Apps Script:&lt;/strong&gt; Open your Google Document and click &lt;strong&gt;Extensions &amp;gt; Apps Script&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Paste the Source Files:&lt;/strong&gt; Enable &lt;code&gt;appsscript.json&lt;/code&gt; in &lt;strong&gt;Project Settings (⚙️)&lt;/strong&gt;, and copy &lt;a href="https://github.com/tanaikech/DocuMentorAI/blob/main/src/appsscript.json" rel="noopener noreferrer"&gt;&lt;code&gt;src/appsscript.json&lt;/code&gt;&lt;/a&gt;, &lt;a href="https://github.com/tanaikech/DocuMentorAI/blob/main/src/code.js" rel="noopener noreferrer"&gt;&lt;code&gt;src/code.js&lt;/code&gt;&lt;/a&gt; (into &lt;code&gt;Code.gs&lt;/code&gt;), and &lt;a href="https://github.com/tanaikech/DocuMentorAI/blob/main/src/index.html" rel="noopener noreferrer"&gt;&lt;code&gt;src/index.html&lt;/code&gt;&lt;/a&gt; into the editor.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Save Your Personal Key:&lt;/strong&gt; Reload the Google Doc, click &lt;strong&gt;&lt;code&gt;✦ DocuMentor AI&lt;/code&gt; &amp;gt; &lt;code&gt;Open Dashboard&lt;/code&gt;&lt;/strong&gt;, paste your Gemini API key into the sidebar, and click &lt;strong&gt;Save&lt;/strong&gt;. For detailed instructions, see &lt;strong&gt;&lt;a href="https://github.com/tanaikech/DocuMentorAI/blob/main/INSTALL_INDIVIDUAL.md" rel="noopener noreferrer"&gt;&lt;code&gt;INSTALL_INDIVIDUAL.md&lt;/code&gt;&lt;/a&gt;&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Workflow B: Zero-Code School, University &amp;amp; Organization Rollout (10 Minutes for IT Admins)
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Create a Standalone Script &amp;amp; Set &lt;code&gt;ScriptProperties&lt;/code&gt;:&lt;/strong&gt; Open &lt;a href="https://script.google.com/" rel="noopener noreferrer"&gt;script.google.com&lt;/a&gt;, create a new project, and paste &lt;a href="https://github.com/tanaikech/DocuMentorAI/blob/main/src/appsscript.json" rel="noopener noreferrer"&gt;&lt;code&gt;src/appsscript.json&lt;/code&gt;&lt;/a&gt;, &lt;a href="https://github.com/tanaikech/DocuMentorAI/blob/main/src/code.js" rel="noopener noreferrer"&gt;&lt;code&gt;src/code.js&lt;/code&gt;&lt;/a&gt;, and &lt;a href="https://github.com/tanaikech/DocuMentorAI/blob/main/src/index.html" rel="noopener noreferrer"&gt;&lt;code&gt;src/index.html&lt;/code&gt;&lt;/a&gt;. Then open &lt;strong&gt;Project Settings (⚙️) &amp;gt; Script Properties&lt;/strong&gt;, click &lt;strong&gt;Add script property&lt;/strong&gt;, set Property = &lt;code&gt;GEMINI_API_KEY&lt;/code&gt; and Value = your organization's Gemini API key, and click &lt;strong&gt;Save&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Publish as a Private (Internal) Marketplace Add-on:&lt;/strong&gt; Link a standard GCP project number in Apps Script &lt;strong&gt;Project Settings (⚙️)&lt;/strong&gt; and create an &lt;strong&gt;Add-on&lt;/strong&gt; deployment (&lt;strong&gt;Deploy &amp;gt; New deployment&lt;/strong&gt;). In the GCP Console, enable &lt;strong&gt;&lt;code&gt;Google Docs API&lt;/code&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;code&gt;Google Workspace Marketplace SDK&lt;/code&gt;&lt;/strong&gt;, set the &lt;strong&gt;OAuth Consent Screen&lt;/strong&gt; to &lt;strong&gt;&lt;code&gt;Internal&lt;/code&gt;&lt;/strong&gt;, and configure the &lt;strong&gt;Google Workspace Marketplace SDK&lt;/strong&gt; with &lt;strong&gt;Visibility: &lt;code&gt;Private&lt;/code&gt;&lt;/strong&gt; (domain-only, requiring zero public review wait time).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Install for Student / Researcher Organizational Units:&lt;/strong&gt; In the &lt;a href="https://admin.google.com/" rel="noopener noreferrer"&gt;Google Workspace Admin Console (&lt;code&gt;admin.google.com&lt;/code&gt;)&lt;/a&gt;, go to &lt;strong&gt;Apps &amp;gt; Google Workspace Marketplace apps &amp;gt; Apps list &amp;gt; Install app &amp;gt; Internal apps&lt;/strong&gt;, select &lt;strong&gt;DocuMentor AI&lt;/strong&gt;, and perform an &lt;strong&gt;Admin install&lt;/strong&gt; for your target Organizational Units (e.g., &lt;code&gt;/Students&lt;/code&gt;, &lt;code&gt;/GraduateLab&lt;/code&gt;, or domain-wide). Students and researchers can immediately open any Google Doc and launch &lt;strong&gt;Extensions &amp;gt; DocuMentor AI &amp;gt; Open Dashboard&lt;/strong&gt; with the organization's API key pre-configured and locked. For the complete IT Administrator checklist and prerequisites, see &lt;strong&gt;&lt;a href="https://github.com/tanaikech/DocuMentorAI/blob/main/INSTALL_ORGANIZATION.md" rel="noopener noreferrer"&gt;&lt;code&gt;INSTALL_ORGANIZATION.md&lt;/code&gt;&lt;/a&gt;&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;By evolving &lt;strong&gt;DocuMentor AI (&lt;code&gt;v1.0.3&lt;/code&gt;)&lt;/strong&gt; to support both &lt;strong&gt;Individual Mode (&lt;code&gt;UserProperties&lt;/code&gt;)&lt;/strong&gt; and &lt;strong&gt;School, Organization &amp;amp; Academic Mode (&lt;code&gt;ScriptProperties&lt;/code&gt; + Private Google Workspace Marketplace Add-on)&lt;/strong&gt; within a single codebase—and combining &lt;strong&gt;💡 Hint-Only Mode&lt;/strong&gt;, &lt;strong&gt;🚀 Rejection-Aware Suggest &amp;amp; Coach Mode&lt;/strong&gt;, &lt;strong&gt;💻 Interactive Code Review &amp;amp; Script Coaching&lt;/strong&gt;, and &lt;strong&gt;🎓 Retrospective Learning Summaries&lt;/strong&gt;—anyone from an independent writer, software engineer, or academic researcher to a school IT administrator can deploy a safe, multimodal, pedagogical writing and coding coach inside Google Docs without modifying a single line of code.&lt;/p&gt;

&lt;p&gt;All updated source code, sample multimodal manuscripts, and separated installation guides are available on GitHub:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub Repository:&lt;/strong&gt; &lt;a href="https://github.com/tanaikech/DocuMentorAI" rel="noopener noreferrer"&gt;https://github.com/tanaikech/DocuMentorAI&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Previous Article (Part 1):&lt;/strong&gt; &lt;a href="https://medium.com/google-cloud/building-a-multimodal-suggestion-based-ai-writing-coach-with-gemini-and-google-docs-ea07244fed99" rel="noopener noreferrer"&gt;Building a Multimodal, Suggestion-Based AI Writing Coach with Gemini and Google Docs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;👤 Individual Setup Guide:&lt;/strong&gt; &lt;a href="https://github.com/tanaikech/DocuMentorAI/blob/main/INSTALL_INDIVIDUAL.md" rel="noopener noreferrer"&gt;INSTALL_INDIVIDUAL.md&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;🏫 School, University &amp;amp; Organization Setup Guide:&lt;/strong&gt; &lt;a href="https://github.com/tanaikech/DocuMentorAI/blob/main/INSTALL_ORGANIZATION.md" rel="noopener noreferrer"&gt;INSTALL_ORGANIZATION.md&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>gemini</category>
      <category>googleappsscript</category>
      <category>googleworkspace</category>
    </item>
    <item>
      <title>Building a Multimodal, Suggestion-Based AI Writing Coach with Gemini and Google Docs</title>
      <dc:creator>Tanaike</dc:creator>
      <pubDate>Fri, 23 Oct 2026 05:52:48 +0000</pubDate>
      <link>https://dev.to/gde/building-a-multimodal-suggestion-based-ai-writing-coach-with-gemini-and-google-docs-ea4</link>
      <guid>https://dev.to/gde/building-a-multimodal-suggestion-based-ai-writing-coach-with-gemini-and-google-docs-ea4</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmc3508cggj1t64kwwz2u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmc3508cggj1t64kwwz2u.png" alt="DocuMentor AI Overview Infographic" width="800" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When we use generative AI to proofread our daily writing—whether technical reports, business proposals, or blog posts—it is easy to fall into the habit of simply copying and pasting AI-rewritten text without reflecting on &lt;em&gt;what&lt;/em&gt; was changed or &lt;em&gt;why&lt;/em&gt;. While that fixes the immediate draft, it rarely helps us grow as writers.&lt;/p&gt;

&lt;p&gt;Recently, Google introduced powerful new capabilities to programmatically manage comments and suggestions across Google Docs, Sheets, and Slides via the Google Workspace APIs (see the official guide: &lt;a href="https://developers.google.com/workspace/docs/api/how-tos/suggestions" rel="noopener noreferrer"&gt;Work with suggestions and comments in the Google Docs API&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;In this article, I introduce &lt;strong&gt;DocuMentor AI&lt;/strong&gt;, an open-source Google Docs add-on built with &lt;strong&gt;Google Apps Script&lt;/strong&gt;, the &lt;strong&gt;Google Docs API (&lt;code&gt;v1&lt;/code&gt;)&lt;/strong&gt;, and the &lt;strong&gt;Gemini API&lt;/strong&gt; (&lt;code&gt;gemini-3.8-flash&lt;/code&gt; / &lt;code&gt;gemini-4-argon&lt;/code&gt;). Instead of overwriting your manuscript directly, DocuMentor AI uses the new &lt;code&gt;WriteControl.writeMode = "SUGGEST"&lt;/code&gt; and comment management APIs (&lt;code&gt;addCommentReply&lt;/code&gt;, &lt;code&gt;insertComment&lt;/code&gt;, &lt;code&gt;acceptSuggestion&lt;/code&gt;, &lt;code&gt;rejectSuggestion&lt;/code&gt;, and &lt;code&gt;deleteComment&lt;/code&gt;) to turn Google Docs into an interactive, multimodal writing coach for continuous self-learning. As shown in the overview infographic above, the system connects three cohesive stages: &lt;strong&gt;1. Scope &amp;amp; Multimodal Input&lt;/strong&gt; (analyzing the entire active tab or a highlighted selection across body text, tables, and embedded figures/charts), &lt;strong&gt;2. Gemini AI &amp;amp; Docs API Engine&lt;/strong&gt; (combining &lt;code&gt;gemini-3.8-flash&lt;/code&gt; / &lt;code&gt;gemini-4-argon&lt;/code&gt; multimodal reasoning with temporary Gemini File API uploads and Google Docs API &lt;code&gt;v1&lt;/code&gt; batch operations), and &lt;strong&gt;3. Interactive Self-Learning Output&lt;/strong&gt; (delivering inline suggested edits with attached &lt;code&gt;Reason&lt;/code&gt; and &lt;code&gt;Learning Point&lt;/code&gt; replies, anchored figure/table review comments, and a pop-up &lt;strong&gt;Writing Coach &amp;amp; Learning Summary&lt;/strong&gt; dialog). Furthermore, because I also wanted to verify the true capabilities of &lt;strong&gt;Gemini 4 Argon&lt;/strong&gt;, the &lt;strong&gt;DocuMentor AI application itself was built using Gemini 4 Argon&lt;/strong&gt;, while also integrating &lt;code&gt;gemini-4-argon&lt;/code&gt; as its high-precision multimodal proofreading and coaching engine.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub Repository:&lt;/strong&gt; &lt;a href="https://github.com/tanaikech/DocuMentorAI" rel="noopener noreferrer"&gt;https://github.com/tanaikech/DocuMentorAI&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Purpose, Motivation &amp;amp; Addressing the "AI-Induced Cognitive Offloading" Problem
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Purpose of Development &amp;amp; Effectiveness Against AI-Induced "Thought Stopping"
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;DocuMentor AI&lt;/strong&gt; was developed to break away from the conventional paradigm of generative AI proofreading—which focuses solely on "efficiency by directly replacing the final text"—and instead achieve &lt;strong&gt;"preserving user agency and maximizing learning outcomes."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In recent years, "thought stopping" (cognitive offloading) and the decline of critical thinking abilities caused by over-reliance on AI have become pressing societal challenges. For example, a June 2025 brain study by the MIT Media Lab (&lt;em&gt;"Your Brain on ChatGPT"&lt;/em&gt;) demonstrated the risk of &lt;strong&gt;"cognitive debt,"&lt;/strong&gt; showing that continuously and passively accepting AI-generated outputs reduces brain activity and neural connectivity (&lt;a href="https://www.media.mit.edu/projects/your-brain-on-chatgpt/overview/" rel="noopener noreferrer"&gt;Ref&lt;/a&gt;). Furthermore, entering 2026, research from institutions such as Carnegie Mellon University, the Wharton School (University of Pennsylvania), and MIT, along with related reports, have highlighted phenomena across education and business including the &lt;strong&gt;"shortcut trap"&lt;/strong&gt;—where even 10 minutes of AI reliance reduces subsequent independent problem-solving and logical thinking performance by 20% (&lt;a href="https://japan.cnet.com/article/35247336/" rel="noopener noreferrer"&gt;Ref&lt;/a&gt;)—and &lt;strong&gt;"cognitive surrender,"&lt;/strong&gt; where reading fluent AI responses creates a false illusion of understanding and leads students and professionals to stop thinking critically for themselves (&lt;a href="https://www.asahi.com/articles/ASVB146BHVB1UHBI03JM.html" rel="noopener noreferrer"&gt;Ref&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DocuMentor AI&lt;/strong&gt; fundamentally solves these challenges by leveraging Google Docs' native &lt;strong&gt;Suggestion&lt;/strong&gt; and &lt;strong&gt;Comment&lt;/strong&gt; features to &lt;strong&gt;visualize the thought process and rationale (&lt;code&gt;Why&lt;/code&gt;—which part was revised, how, and why)&lt;/strong&gt; rather than overwriting the author's text. Because the user must actively judge whether to &lt;strong&gt;accept or reject&lt;/strong&gt; each proposed revision, critical thinking is naturally triggered instead of blindly accepting AI output. According to a survey reported by Nikkei XTECH in October 2026, the #1 concern among generative AI users (42.1%) is the &lt;strong&gt;"decline in thinking ability due to AI dependency"&lt;/strong&gt; (&lt;a href="https://xtech.nikkei.com/atcl/nxt/mag/nc/18/020600010/100500235/" rel="noopener noreferrer"&gt;Ref&lt;/a&gt;). By transforming AI from a mere "ghostwriter" into a &lt;strong&gt;"side-by-side personal tutor,"&lt;/strong&gt; DocuMentor AI directly addresses this concern and empowers users to cultivate autonomous writing skills and a continuous growth mindset.&lt;/p&gt;

&lt;h3&gt;
  
  
  Technical Inspiration
&lt;/h3&gt;

&lt;p&gt;When I read the following three insightful articles recently published by &lt;strong&gt;Romin Irani&lt;/strong&gt; demonstrating the new Google Docs API comment and suggestion features, I realized that these capabilities could be applied not only to automated document review, but also as a &lt;strong&gt;self-directed learning environment&lt;/strong&gt; where writers can inspect every suggested revision alongside its underlying rationale:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://medium.com/google-cloud/building-an-ai-document-reviewer-with-google-adk-gemini-and-the-new-google-docs-comments-1f79fb6cb86f" rel="noopener noreferrer"&gt;Building an AI Document Reviewer with Google ADK, Gemini, and the New Google Docs Comments&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/google-cloud/grounding-google-docs-suggested-edits-with-developer-knowledge-mcp-and-google-search-9e2a3bf2800e" rel="noopener noreferrer"&gt;Grounding Google Docs Suggested Edits with Developer Knowledge MCP and Google Search&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/google-cloud/building-an-ai-comment-assistant-in-google-docs-with-adk-and-the-new-docs-api-v1-comment-b293aecaa92c" rel="noopener noreferrer"&gt;Building an AI Comment Assistant in Google Docs with ADK and the New Docs API v1 Comment&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;At the same time, I wanted to evaluate and confirm the real-world power of &lt;strong&gt;Gemini 4 Argon&lt;/strong&gt;. Therefore, I &lt;strong&gt;built the entire DocuMentor AI application using Gemini 4 Argon&lt;/strong&gt;—from designing the 4-stage pipeline, resolving bottom-to-top UTF-16 character indices across unsettled suggestions, and orchestrating the new Google Docs API &lt;code&gt;v1&lt;/code&gt; suggestion/comment endpoints, to crafting the interactive sidebar dashboard (&lt;code&gt;src/code.js&lt;/code&gt; and &lt;code&gt;src/index.html&lt;/code&gt;)—and also incorporated &lt;code&gt;gemini-4-argon&lt;/code&gt; directly into the add-on as the high-precision model for multimodal proofreading and coaching.&lt;/p&gt;

&lt;p&gt;Inspired by these articles and powered by Gemini 4 Argon, I designed DocuMentor AI around four core pillars:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Non-Destructive, Transparent AI Editing (&lt;code&gt;WriteMode.SUGGEST&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;Traditional AI writing tools either overwrite your text directly or force you to switch back and forth between an external chat window and your document. By programmatically submitting edits in Google Docs' native &lt;strong&gt;Suggest Mode&lt;/strong&gt; (&lt;code&gt;writeControl: { writeMode: "SUGGEST" }&lt;/code&gt;) and attaching educational replies (&lt;code&gt;addCommentReply&lt;/code&gt;) directly to each suggestion thread, authors retain 100% editorial control to accept or reject every proposed revision.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. True Multimodal Manuscript Understanding (Text, Tables &amp;amp; Figures)
&lt;/h3&gt;

&lt;p&gt;Real-world documents—especially academic papers and technical reports—are rarely plain text alone; they combine prose, structured tables, and numerous visual charts or diagrams. DocuMentor AI extracts document tables as structured Markdown, fetches embedded figures and charts as binary images, temporarily uploads them via the &lt;strong&gt;Gemini File API&lt;/strong&gt;, passes their file URIs (&lt;code&gt;fileData.fileUri&lt;/code&gt;) to Gemini, and automatically deletes the uploaded files immediately after content generation to keep the environment completely clean. As a result, Gemini can propose inline phrasing edits inside table cells, verify numerical alignment between body text and charts, and attach anchored improvement comments (&lt;code&gt;insertComment&lt;/code&gt;) directly to tables (&lt;code&gt;TABLE_1&lt;/code&gt;, etc.) and figures (&lt;code&gt;VISUAL_1&lt;/code&gt;, etc.).&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Flexible Scope Control (Entire Active Tab vs. Selection Only)
&lt;/h3&gt;

&lt;p&gt;Sometimes you want a holistic review of an entire document tab; other times you only want to polish a single paragraph, table, or figure you just drafted. DocuMentor AI provides a &lt;strong&gt;🎯 Proofread Selection Only&lt;/strong&gt; toggle switch in the sidebar:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;OFF (Default):&lt;/strong&gt; Proofreads the &lt;strong&gt;entire active tab&lt;/strong&gt; (body text, tables, and figures/charts).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ON:&lt;/strong&gt; Proofreads &lt;strong&gt;only your highlighted selection&lt;/strong&gt; (&lt;code&gt;doc.getSelection()&lt;/code&gt;), while still passing the full active tab's context to Gemini so that local revisions remain consistent with the overall manuscript.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Pedagogical Writing Coach &amp;amp; Learning Summary Dialog (Learning from Both AI and Other Users' Reviews)
&lt;/h3&gt;

&lt;p&gt;Rather than acting as a black-box proofreader, DocuMentor AI explains &lt;strong&gt;why&lt;/strong&gt; each change was suggested (&lt;code&gt;💡 Reason&lt;/code&gt;) and provides a concise takeaway (&lt;code&gt;🎓 Learning Point&lt;/code&gt;) in every suggestion card. Furthermore, as soon as AI proofreading finishes, it automatically displays a custom &lt;strong&gt;🎓 Writing Coach &amp;amp; Learning Summary&lt;/strong&gt; modal dialog that synthesizes four structured sections:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;📋 Overall Manuscript Evaluation (&lt;code&gt;overallFeedback&lt;/code&gt;):&lt;/strong&gt; A holistic assessment of the manuscript's strengths, clarity, and cross-modal consistency across text, tables, and figures.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;🛠️ What Was Revised / Pointed Out (&lt;code&gt;revisionsDigest&lt;/code&gt;):&lt;/strong&gt; A clear digest of the primary improvements and review points made.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;🔍 Areas for Improvement &amp;amp; Writing Habits (&lt;code&gt;weakPointsAndHabits&lt;/code&gt;):&lt;/strong&gt; Recurring tendencies, grammatical habits, or structural weak points observed in the author's draft.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;🌱 Actionable Advice for Future Writing (&lt;code&gt;actionableTips&lt;/code&gt;):&lt;/strong&gt; Simple, practical tips the author can apply in future drafts, complete with a one-click &lt;strong&gt;📋 Copy to Clipboard&lt;/strong&gt; button.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In addition to generating this summary during AI proofreading (&lt;strong&gt;🚀 Run Proofreading&lt;/strong&gt;), DocuMentor AI includes a dedicated &lt;strong&gt;🎓 Generate Learning Summary from Suggestions &amp;amp; Comments&lt;/strong&gt; button (&lt;code&gt;step2_generateLearningSummaryFromExisting&lt;/code&gt;). When you open a Google Document that already contains inline suggestions or comments from &lt;strong&gt;other users&lt;/strong&gt; (such as colleagues, supervisors, or human editors), clicking this button analyzes those existing suggestions and comments in context and generates the &lt;strong&gt;Writing Coach &amp;amp; Learning Summary&lt;/strong&gt;—enabling you to learn from human peer reviews just as effectively as from AI proofreading (and allowing you to re-run the summary separately at any time after proofreading).&lt;/p&gt;




&lt;h2&gt;
  
  
  From Black-Box Rewriting to an Active Self-Learning Loop: Architecture &amp;amp; 4-Stage Workflow
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fy6mlk3fkwb7sahcslqx3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fy6mlk3fkwb7sahcslqx3.png" alt="From Black-Box AI Rewriting to Interactive Self-Learning in Google Docs" width="800" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As illustrated in the diagram above, &lt;strong&gt;DocuMentor AI&lt;/strong&gt; shifts AI proofreading away from &lt;strong&gt;passive external copy-paste rewrites&lt;/strong&gt;—which overwrite the author's voice, ignore tables/charts, and offer zero explanation—toward an &lt;strong&gt;Active 3-Step Self-Learning Loop&lt;/strong&gt; (&lt;code&gt;1. Inspect&lt;/code&gt; ➔ &lt;code&gt;2. Understand&lt;/code&gt; ➔ &lt;code&gt;3. Internalize&lt;/code&gt;) powered by cross-modal alignment (&lt;code&gt;Text ⇄ Tables ⇄ Charts&lt;/code&gt;) and a bottom-to-top UTF-16 index engine.&lt;/p&gt;

&lt;p&gt;When the user clicks &lt;strong&gt;🚀 Run Proofreading (Suggest &amp;amp; Coach)&lt;/strong&gt; in the Google Docs sidebar, &lt;strong&gt;DocuMentor AI&lt;/strong&gt; executes a structured 4-stage pipeline, updating its real-time Process Dashboard at each step:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;STEP 1: Fetch Document &amp;amp; Parse Structure (&lt;code&gt;step1_fetchDocument&lt;/code&gt;)&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Calls &lt;code&gt;documents.get&lt;/code&gt; with &lt;code&gt;includeTabsContent=true&lt;/code&gt;, &lt;code&gt;suggestionsViewMode=SUGGESTIONS_INLINE&lt;/code&gt;, and &lt;code&gt;commentsViewMode=COMMENTS_VIEW_MODE_INCLUDED&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Identifies the user's currently active tab (&lt;code&gt;doc.getActiveTab()&lt;/code&gt;) and extracts settled text blocks, structured tables (converted to Markdown), visual figures/charts (&lt;code&gt;contentUri&lt;/code&gt;), and any existing unresolved suggestions and active comment threads (&lt;code&gt;existingComments&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;If the &lt;strong&gt;Proofread Selection Only&lt;/strong&gt; toggle is enabled, maps &lt;code&gt;doc.getSelection()&lt;/code&gt; to exact UTF-16 character ranges and target element IDs within the active tab.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;STEP 2: Multimodal Analysis &amp;amp; Coaching via Gemini (&lt;code&gt;step2_generateProofreading&lt;/code&gt;)&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Fetches binary image data for embedded figures/charts in parallel via &lt;code&gt;UrlFetchApp.fetchAll()&lt;/code&gt;, uploads them to the &lt;strong&gt;Gemini File API&lt;/strong&gt; (&lt;code&gt;upload/v1beta/files&lt;/code&gt;), and passes their file URIs (&lt;code&gt;fileData.fileUri&lt;/code&gt;) in the prompt so that even long manuscripts with many figures remain lightweight.&lt;/li&gt;
&lt;li&gt;Sends the full active tab context, target editable blocks/tables/figures, and optional custom prompt instructions to Gemini (&lt;code&gt;gemini-3.8-flash&lt;/code&gt; or &lt;code&gt;gemini-4-argon&lt;/code&gt;) using Structured Outputs (&lt;code&gt;responseSchema&lt;/code&gt;), and immediately deletes all uploaded temporary files from the Gemini File API (&lt;code&gt;DELETE v1beta/files/...&lt;/code&gt; inside a &lt;code&gt;finally&lt;/code&gt; block) once the response is received.&lt;/li&gt;
&lt;li&gt;Enforces 100% single-language output matching the selected UI mode (&lt;strong&gt;English &lt;code&gt;EN&lt;/code&gt;&lt;/strong&gt; or &lt;strong&gt;Japanese &lt;code&gt;JA&lt;/code&gt;&lt;/strong&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;STEP 3: Build Suggest &amp;amp; Comment Payloads (&lt;code&gt;step3_buildRequestBody&lt;/code&gt;)&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Matches each proposed &lt;code&gt;originalText&lt;/code&gt; against the settled text blocks to resolve exact &lt;code&gt;startIndex&lt;/code&gt; and &lt;code&gt;endIndex&lt;/code&gt; coordinates.&lt;/li&gt;
&lt;li&gt;Sorts all text replacement ranges in &lt;strong&gt;descending index order (from the bottom of the document to the top)&lt;/strong&gt; so that earlier character indices never shift during batch execution.&lt;/li&gt;
&lt;li&gt;Constructs &lt;code&gt;deleteContentRange&lt;/code&gt; and &lt;code&gt;insertText&lt;/code&gt; requests scoped with the active tab's &lt;code&gt;tabId&lt;/code&gt; under &lt;code&gt;writeControl: { writeMode: "SUGGEST" }&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;STEP 4: Apply Suggestions &amp;amp; Educational Comments (&lt;code&gt;step4_applySuggestions&lt;/code&gt;)&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Executes the &lt;code&gt;SUGGEST&lt;/code&gt; batch update via &lt;code&gt;documents.batchUpdate&lt;/code&gt;, extracts &lt;code&gt;createdSuggestionIds&lt;/code&gt; from &lt;code&gt;suggestionResponses&lt;/code&gt;, and attaches &lt;code&gt;addCommentReply&lt;/code&gt; requests (&lt;code&gt;Reason&lt;/code&gt; and &lt;code&gt;Learning Point&lt;/code&gt;) to each inline suggestion card.&lt;/li&gt;
&lt;li&gt;Because direct inline edit suggestions (&lt;code&gt;WriteMode.SUGGEST&lt;/code&gt;) cannot be applied to figures, re-fetches the updated post-suggestion indices and executes &lt;code&gt;insertComment&lt;/code&gt; requests—with an automatic 3-tier anchor range fallback (exact element range ➔ containing paragraph range ➔ nearest settled text block)—to reliably attach review comments directly to figures/charts (&lt;code&gt;VISUAL_1&lt;/code&gt;, etc.) and overall tables (&lt;code&gt;TABLE_1&lt;/code&gt;, etc.).&lt;/li&gt;
&lt;li&gt;Opens the &lt;strong&gt;🎓 Writing Coach &amp;amp; Learning Summary&lt;/strong&gt; custom modal dialog.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Step-by-Step Installation &amp;amp; Usage Guide
&lt;/h2&gt;

&lt;p&gt;You can install and run &lt;strong&gt;DocuMentor AI&lt;/strong&gt; in any Google Document by following these steps:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Create or Open a Google Document
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Open &lt;a href="https://docs.google.com/" rel="noopener noreferrer"&gt;Google Docs&lt;/a&gt; and create a new document or open an existing manuscript you wish to proofread.&lt;/li&gt;
&lt;li&gt;In the top menu bar, click &lt;strong&gt;Extensions&lt;/strong&gt; &amp;gt; &lt;strong&gt;Apps Script&lt;/strong&gt; to open the Google Apps Script editor.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  2. Copy and Paste the Script Files (&lt;code&gt;src/&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;In the Apps Script editor, set up the following three files from the &lt;a href="https://github.com/tanaikech/DocuMentorAI/tree/main/src" rel="noopener noreferrer"&gt;&lt;code&gt;src/&lt;/code&gt;&lt;/a&gt; directory of the repository:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Enable &lt;code&gt;appsscript.json&lt;/code&gt; (Manifest File):&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;In the Apps Script editor, click &lt;strong&gt;Project Settings&lt;/strong&gt; (the gear icon ⚙️ on the left sidebar) and check the box &lt;strong&gt;"Show 'appsscript.json' manifest file in editor"&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Return to the &lt;strong&gt;Editor&lt;/strong&gt; (&lt;code&gt;&amp;lt; &amp;gt;&lt;/code&gt; icon), select &lt;code&gt;appsscript.json&lt;/code&gt;, and replace its contents with &lt;a href="https://github.com/tanaikech/DocuMentorAI/blob/main/src/appsscript.json" rel="noopener noreferrer"&gt;&lt;code&gt;src/appsscript.json&lt;/code&gt;&lt;/a&gt; (this enables the &lt;code&gt;Docs&lt;/code&gt; Advanced Service &lt;code&gt;v1&lt;/code&gt; and configures the required OAuth scopes so &lt;code&gt;UrlFetchApp&lt;/code&gt; can call the latest Google Docs REST API &lt;code&gt;v1&lt;/code&gt; suggestion and comment endpoints directly via &lt;code&gt;ScriptApp.getOAuthToken()&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Copy &lt;code&gt;code.js&lt;/code&gt; into &lt;code&gt;Code.gs&lt;/code&gt;:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Select the default &lt;code&gt;Code.gs&lt;/code&gt; file in the editor and replace its entire contents with &lt;a href="https://github.com/tanaikech/DocuMentorAI/blob/main/src/code.js" rel="noopener noreferrer"&gt;&lt;code&gt;src/code.js&lt;/code&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Create &lt;code&gt;index.html&lt;/code&gt;:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Click the &lt;strong&gt;&lt;code&gt;+&lt;/code&gt;&lt;/strong&gt; button next to &lt;strong&gt;Files&lt;/strong&gt; in the left panel and select &lt;strong&gt;HTML&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Name the file &lt;strong&gt;&lt;code&gt;index&lt;/code&gt;&lt;/strong&gt; (it will automatically appear as &lt;code&gt;index.html&lt;/code&gt;—do not type &lt;code&gt;.html&lt;/code&gt; in the name prompt).&lt;/li&gt;
&lt;li&gt;Paste the entire contents of &lt;a href="https://github.com/tanaikech/DocuMentorAI/blob/main/src/index.html" rel="noopener noreferrer"&gt;&lt;code&gt;src/index.html&lt;/code&gt;&lt;/a&gt; into this file.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Save the Project:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Press &lt;code&gt;Ctrl + S&lt;/code&gt; (or &lt;code&gt;Cmd + S&lt;/code&gt; on macOS) or click the &lt;strong&gt;Save project&lt;/strong&gt; (floppy disk 💾) icon.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  3. Reload Google Docs &amp;amp; Launch from the Custom Menu
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Close the Apps Script tab and &lt;strong&gt;reload (refresh) your Google Docs browser tab&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;After a few seconds, a new custom menu &lt;strong&gt;&lt;code&gt;✦ DocuMentor AI&lt;/code&gt;&lt;/strong&gt; will appear in the Google Docs menu bar.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;&lt;code&gt;✦ DocuMentor AI&lt;/code&gt;&lt;/strong&gt; &amp;gt; &lt;strong&gt;&lt;code&gt;Open Dashboard&lt;/code&gt;&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;(First-time run only)&lt;/em&gt; Google will prompt you to authorize the script permissions (&lt;code&gt;documents&lt;/code&gt;, &lt;code&gt;script.container.ui&lt;/code&gt;, &lt;code&gt;script.external_request&lt;/code&gt;). Complete the authorization flow and click &lt;strong&gt;&lt;code&gt;✦ DocuMentor AI&lt;/code&gt;&lt;/strong&gt; &amp;gt; &lt;strong&gt;&lt;code&gt;Open Dashboard&lt;/code&gt;&lt;/strong&gt; again.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  4. Configure Settings &amp;amp; Run Proofreading (How to Use the Sidebar)
&lt;/h3&gt;

&lt;p&gt;Once launched from &lt;strong&gt;&lt;code&gt;✦ DocuMentor AI&lt;/code&gt;&lt;/strong&gt; &amp;gt; &lt;strong&gt;&lt;code&gt;Open Dashboard&lt;/code&gt;&lt;/strong&gt;, the &lt;strong&gt;DocuMentor AI&lt;/strong&gt; sidebar appears on the right side of Google Docs, organized into intuitive control cards and a live process dashboard:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fiyckwnpoh1lhasi12ugf.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fiyckwnpoh1lhasi12ugf.jpg" alt="DocuMentor AI Sidebar Dashboard" width="435" height="1689"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;First, Register Your Gemini API Key (&lt;code&gt;⚙️ AI Engine &amp;amp; API Settings&lt;/code&gt;):&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;When you launch the application and the sidebar opens, &lt;strong&gt;first register your API key for using Gemini&lt;/strong&gt;. Enter your &lt;strong&gt;Gemini API Key&lt;/strong&gt; (obtainable from &lt;a href="https://aistudio.google.com/" rel="noopener noreferrer"&gt;Google AI Studio&lt;/a&gt;) in the &lt;strong&gt;AI Engine &amp;amp; API Settings&lt;/strong&gt; card and click &lt;strong&gt;Save&lt;/strong&gt;. Once saved in &lt;code&gt;PropertiesService.getUserProperties()&lt;/code&gt;, the card displays &lt;strong&gt;&lt;code&gt;🔒 API Key Configured (Saved)&lt;/code&gt;&lt;/strong&gt; with a &lt;strong&gt;Reset&lt;/strong&gt; link so you do not need to re-enter it in future sessions.&lt;/li&gt;
&lt;li&gt;Under &lt;strong&gt;Gemini Model&lt;/strong&gt;, select either &lt;code&gt;gemini-3.8-flash (Default / Fast)&lt;/code&gt; for rapid daily proofreading or &lt;code&gt;gemini-4-argon (High Precision)&lt;/code&gt; for deep multimodal and structural analysis.&lt;/li&gt;
&lt;li&gt;At the top-right of the sidebar header, toggle between &lt;strong&gt;&lt;code&gt;EN&lt;/code&gt;&lt;/strong&gt; (English) and &lt;strong&gt;&lt;code&gt;JA&lt;/code&gt;&lt;/strong&gt; (Japanese) to switch both the UI language and 100% of Gemini's output language. Clicking &lt;strong&gt;&lt;code&gt;DocuMentor AI ⓘ&lt;/code&gt;&lt;/strong&gt; in the header (or &lt;strong&gt;&lt;code&gt;DocuMentor AI&lt;/code&gt;&lt;/strong&gt; in the footer) opens the &lt;strong&gt;About DocuMentor AI&lt;/strong&gt; modal dialog (&lt;code&gt;Developer: Kanshi Tanaike&lt;/code&gt;, &lt;code&gt;Version: v1.0.0&lt;/code&gt;, and GitHub repository link).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Switch the Proofreading Scope (&lt;code&gt;"Proofread Selection Only"&lt;/code&gt; Toggle):&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Inside the &lt;strong&gt;✍️ Proofreading Scope &amp;amp; Controls&lt;/strong&gt; card, turning &lt;strong&gt;ON&lt;/strong&gt; the &lt;strong&gt;"🎯 Proofread Selection Only"&lt;/strong&gt; toggle switch proofreads &lt;strong&gt;only the selected part&lt;/strong&gt; (any specific paragraph, sentence, table cell, table, or figure you highlight in the document).&lt;/li&gt;
&lt;li&gt;Leaving the &lt;strong&gt;"🎯 Proofread Selection Only"&lt;/strong&gt; toggle switch &lt;strong&gt;OFF (Default)&lt;/strong&gt; proofreads the &lt;strong&gt;entire active tab (text, tables &amp;amp; figures)&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Add Additional Instructions via Custom Prompt (Optional):&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;When proofreading, it is also possible to add additional instructions as a &lt;strong&gt;Custom Prompt&lt;/strong&gt;. You can type specific requests into the &lt;strong&gt;Additional Proofreading Instructions&lt;/strong&gt; text box (e.g., &lt;em&gt;"Refine the text, tables, and charts for a professional business report."&lt;/em&gt;) or click one or more of the preset prompt chips (&lt;code&gt;#Concise &amp;amp; Logical&lt;/code&gt;, &lt;code&gt;#Business Tone&lt;/code&gt;, &lt;code&gt;#Tables &amp;amp; Charts&lt;/code&gt;, &lt;code&gt;#Grammar &amp;amp; Flow&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Execute Proofreading (&lt;code&gt;"Run Proofreading"&lt;/code&gt;) &amp;amp; Automatic Learning Summary:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Click the primary &lt;strong&gt;"🚀 Run Proofreading (Suggest &amp;amp; Coach)"&lt;/strong&gt; button (or &lt;strong&gt;"📝 Generate Initial Text from Prompt"&lt;/strong&gt; if the active tab is currently empty).&lt;/li&gt;
&lt;li&gt;As the 4-stage pipeline runs, each step in the &lt;strong&gt;📊 Process Dashboard&lt;/strong&gt; (&lt;code&gt;1 Fetch Text, Tables &amp;amp; Figures&lt;/code&gt; ➔ &lt;code&gt;2 Generate AI Proofreading &amp;amp; Coaching&lt;/code&gt; ➔ &lt;code&gt;3 Build Suggestion Request Body&lt;/code&gt; ➔ &lt;code&gt;4 Apply Suggestions &amp;amp; Comments&lt;/code&gt;) updates in real time.&lt;/li&gt;
&lt;li&gt;When the pipeline finishes, the &lt;strong&gt;🎓 Writing Coach &amp;amp; Learning Summary&lt;/strong&gt; custom modal dialog automatically pops up with a &lt;strong&gt;📋 Copy to Clipboard&lt;/strong&gt; button, and a &lt;strong&gt;📖 Re-open Latest Learning Summary Dialog&lt;/strong&gt; button appears in the sidebar.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Generate a Learning Summary from Existing Suggestions &amp;amp; Comments (Learn from Other Users' Feedback too):&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Click the &lt;strong&gt;"🎓 Generate Learning Summary from Suggestions &amp;amp; Comments"&lt;/strong&gt; button right below the Run Proofreading button to generate a &lt;strong&gt;Writing Coach &amp;amp; Learning Summary&lt;/strong&gt; directly from &lt;strong&gt;existing suggestions and comments&lt;/strong&gt; found in the Google Document when you open it (without adding or modifying any suggestions or comments in the document). This enables you to learn effectively from edits and review comments made by other users (colleagues, supervisors, or human editors).&lt;/li&gt;
&lt;li&gt;Note that while pressing &lt;strong&gt;"🚀 Run Proofreading"&lt;/strong&gt; automatically generates the &lt;strong&gt;Writing Coach &amp;amp; Learning Summary&lt;/strong&gt;, you can also press &lt;strong&gt;"🎓 Generate Learning Summary from Suggestions &amp;amp; Comments"&lt;/strong&gt; separately at any time afterward to re-analyze and generate the summary from the current suggestions and comments in the document.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Accept or Delete All Suggestions &amp;amp; Comments with One Click:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Clicking the &lt;strong&gt;"✅ Accept All Suggestions"&lt;/strong&gt; button accepts all pending inline suggestions in the active tab at once.&lt;/li&gt;
&lt;li&gt;Clicking the &lt;strong&gt;"🧹 Clear All Suggestions"&lt;/strong&gt; button rejects and clears all pending inline suggestions in the active tab at once.&lt;/li&gt;
&lt;li&gt;Clicking the &lt;strong&gt;"🗑️ Clear All Comments"&lt;/strong&gt; button deletes all active comments (including figure/table review comments created via &lt;code&gt;insertComment&lt;/code&gt;) in the active tab at once.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Testing with the Sample Manuscript (&lt;code&gt;sample/&lt;/code&gt;) &amp;amp; Live Demonstration
&lt;/h2&gt;

&lt;p&gt;To help you immediately test how &lt;strong&gt;DocuMentor AI&lt;/strong&gt; works across body text, a structured table, and an embedded figure/chart, a ready-to-use sample manuscript is provided in the &lt;a href="https://github.com/tanaikech/DocuMentorAI/tree/main/sample" rel="noopener noreferrer"&gt;&lt;code&gt;sample/&lt;/code&gt;&lt;/a&gt; directory of the repository. &lt;strong&gt;Both screenshots below were captured using this exact sample manuscript:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/tanaikech/DocuMentorAI/blob/main/sample/sample_article.md" rel="noopener noreferrer"&gt;&lt;code&gt;sample/sample_article.md&lt;/code&gt;&lt;/a&gt;:&lt;/strong&gt; Contains an introductory draft about Google Apps Script (GAS) and Gemini AI integration that intentionally includes redundant phrasing, colloquialisms, and grammatical awkwardness (e.g., &lt;em&gt;"most biggest advantage"&lt;/em&gt;, &lt;em&gt;"boring daily routine works"&lt;/em&gt;, &lt;em&gt;"special softwares"&lt;/em&gt;), along with a compact 2-data-row (3 rows including the header) Markdown comparison table and a reference to &lt;a href="https://github.com/tanaikech/DocuMentorAI/blob/main/sample/sample_figure.png" rel="noopener noreferrer"&gt;&lt;code&gt;sample/sample_figure.png&lt;/code&gt;&lt;/a&gt; sized to fit neatly on a single page.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/tanaikech/DocuMentorAI/blob/main/sample/sample_figure.png" rel="noopener noreferrer"&gt;&lt;code&gt;sample/sample_figure.png&lt;/code&gt;&lt;/a&gt;:&lt;/strong&gt; A composite diagram and bar chart (&lt;em&gt;"Figure 1: GAS Automation Architecture &amp;amp; Daily Task Time Comparison"&lt;/em&gt;) designed for multimodal visual inspection.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  How to Load the &lt;code&gt;sample/&lt;/code&gt; Files into Google Docs
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Import or Paste the Markdown Content:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Option A (Paste from Markdown):&lt;/strong&gt; Copy the contents of &lt;a href="https://github.com/tanaikech/DocuMentorAI/blob/main/sample/sample_article.md" rel="noopener noreferrer"&gt;&lt;code&gt;sample/sample_article.md&lt;/code&gt;&lt;/a&gt;, right-click inside your Google Doc, and select &lt;strong&gt;Paste from Markdown&lt;/strong&gt; (available in Google Docs when &lt;em&gt;"Enable Markdown"&lt;/em&gt; is turned on under &lt;strong&gt;Tools&lt;/strong&gt; &amp;gt; &lt;strong&gt;Preferences&lt;/strong&gt;), or paste the text and create the table directly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Option B (Open in Google Drive):&lt;/strong&gt; Upload &lt;a href="https://github.com/tanaikech/DocuMentorAI/blob/main/sample/sample_article.md" rel="noopener noreferrer"&gt;&lt;code&gt;sample/sample_article.md&lt;/code&gt;&lt;/a&gt; to Google Drive and open it with Google Docs.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Insert the Sample Figure:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Place your cursor right below the table, click &lt;strong&gt;Insert&lt;/strong&gt; &amp;gt; &lt;strong&gt;Image&lt;/strong&gt; &amp;gt; &lt;strong&gt;Upload from computer&lt;/strong&gt;, and select &lt;a href="https://github.com/tanaikech/DocuMentorAI/blob/main/sample/sample_figure.png" rel="noopener noreferrer"&gt;&lt;code&gt;sample/sample_figure.png&lt;/code&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test Both Proofreading Modes:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Full Active Tab Mode (Toggle OFF):&lt;/strong&gt; Leave &lt;strong&gt;🎯 Proofread Selection Only&lt;/strong&gt; turned &lt;strong&gt;OFF&lt;/strong&gt; and click &lt;strong&gt;🚀 Run Proofreading (Suggest &amp;amp; Coach)&lt;/strong&gt; to see inline text edits, table cell refinements, anchored comments on &lt;code&gt;TABLE_1&lt;/code&gt; and &lt;code&gt;VISUAL_1&lt;/code&gt;, and the pop-up &lt;strong&gt;🎓 Writing Coach &amp;amp; Learning Summary&lt;/strong&gt; dialog.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Selection-Only Mode (Toggle ON):&lt;/strong&gt; Click &lt;strong&gt;🧹 Clear All Suggestions&lt;/strong&gt; (and &lt;strong&gt;🗑️ Clear All Comments&lt;/strong&gt;), highlight a single paragraph, table cell, or the figure, switch &lt;strong&gt;🎯 Proofread Selection Only&lt;/strong&gt; &lt;strong&gt;ON&lt;/strong&gt;, and run proofreading again to verify scoped suggestions and comments.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Before Proofreading: The &lt;code&gt;sample/&lt;/code&gt; Manuscript Loaded into Google Docs
&lt;/h3&gt;

&lt;p&gt;Below is the result of loading &lt;a href="https://github.com/tanaikech/DocuMentorAI/blob/main/sample/sample_article.md" rel="noopener noreferrer"&gt;&lt;code&gt;sample/sample_article.md&lt;/code&gt;&lt;/a&gt; and inserting &lt;a href="https://github.com/tanaikech/DocuMentorAI/blob/main/sample/sample_figure.png" rel="noopener noreferrer"&gt;&lt;code&gt;sample/sample_figure.png&lt;/code&gt;&lt;/a&gt; into Google Docs. It introduces Google Apps Script (GAS) and Gemini AI automation, accompanied by the 2-data-row (3 rows including the header) comparison table and the composite figure (&lt;code&gt;Figure 1: GAS Automation Architecture &amp;amp; Daily Task Time Comparison&lt;/code&gt;). Notice that the draft intentionally contains several common writing habits and awkward expressions—such as conversational fillers (&lt;em&gt;"is basically a coding platform that is provided by"&lt;/em&gt;), a double superlative (&lt;em&gt;"The most biggest advantage"&lt;/em&gt;), uncountable noun errors (&lt;em&gt;"any special softwares"&lt;/em&gt;), and wordy phrasing:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgz9jvzokvgkcj1p5nuwq.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgz9jvzokvgkcj1p5nuwq.jpg" alt="Sample Manuscript Before Proofreading in Google Docs" width="800" height="971"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  After Running DocuMentor AI on the &lt;code&gt;sample/&lt;/code&gt; Manuscript
&lt;/h3&gt;

&lt;p&gt;Next, we open the &lt;strong&gt;DocuMentor AI&lt;/strong&gt; sidebar from the custom menu (&lt;code&gt;✦ DocuMentor AI&lt;/code&gt; &amp;gt; &lt;code&gt;Open Dashboard&lt;/code&gt;), select &lt;code&gt;gemini-4-argon (High Precision)&lt;/code&gt;, and click &lt;strong&gt;🚀 Run Proofreading (Suggest &amp;amp; Coach)&lt;/strong&gt; on our loaded &lt;code&gt;sample&lt;/code&gt; document. Within seconds, the 4-stage pipeline completes:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqowk6x5tzweitr093hub.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqowk6x5tzweitr093hub.jpg" alt="DocuMentor AI Proofreading Results in Google Docs" width="800" height="621"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Looking closely at the result of proofreading the &lt;code&gt;sample&lt;/code&gt; manuscript above, we can observe several key capabilities working together seamlessly:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Precise Inline Suggested Edits &amp;amp; Anchored Comments Across Paragraphs, Table Cells &amp;amp; Figures:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;In the first paragraph, &lt;em&gt;"Google Apps Script (GAS) is basically a coding platform that is provided by Google, and it is made based on the JavaScript language."&lt;/em&gt; is cleanly suggested to be replaced with &lt;em&gt;"Google Apps Script (GAS) is a JavaScript-based coding platform provided by Google."&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;The second sentence (&lt;em&gt;"The most biggest advantage of using GAS is because you can very easily connect and integrate with many various Google apps like Sheets, Docs, and Gmail that we are using every single day to automate boring daily routine works."&lt;/em&gt;) is refined into &lt;em&gt;"The biggest advantage of using GAS is that it easily integrates with everyday Google apps—such as Sheets, Docs, and Gmail—to automate repetitive daily tasks."&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;In the second paragraph, &lt;em&gt;"First of all, the way to start using GAS is super simple and you totally do not need to install any special softwares on your computer at all."&lt;/em&gt; is tightened to &lt;em&gt;"Getting started with GAS is straightforward and requires no special software installation."&lt;/em&gt; and &lt;em&gt;"There is a merit that you can immediately start writing code just by opening your web browser."&lt;/em&gt; is refined to &lt;em&gt;"You can immediately start writing code directly in your web browser."&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Inside the table header (&lt;code&gt;TABLE_1&lt;/code&gt;), &lt;code&gt;Task Time&lt;/code&gt; is suggested to be updated to &lt;code&gt;Execution Time&lt;/code&gt; to match the vertical axis label (&lt;code&gt;Execution Time (Minutes)&lt;/code&gt;) in &lt;code&gt;Figure 1&lt;/code&gt;. Furthermore, notice the yellow comment highlight on &lt;code&gt;Target App&lt;/code&gt; in &lt;code&gt;TABLE_1&lt;/code&gt; and the yellow highlight border directly around &lt;code&gt;Figure 1&lt;/code&gt; (&lt;code&gt;VISUAL_1&lt;/code&gt;)—confirming that &lt;strong&gt;DocuMentor AI&lt;/strong&gt; has also attached anchored Google Docs review comments (&lt;code&gt;insertComment&lt;/code&gt;) directly onto both the table and the figure.&lt;/li&gt;
&lt;li&gt;In the final paragraph below &lt;code&gt;Figure 1&lt;/code&gt;, the first sentence (&lt;em&gt;"Also, recently it has become possible to call generative AI APIs like Gemini by using UrlFetchApp, so you are able to create your own custom AI tools for your business tasks."&lt;/em&gt;) is refined into &lt;em&gt;"Additionally, you can now call generative AI APIs like Gemini using UrlFetchApp to build custom AI tools for your business workflows (see Table 1 and Figure 1)."&lt;/em&gt;—explicitly citing both &lt;code&gt;Table 1&lt;/code&gt; and &lt;code&gt;Figure 1&lt;/code&gt; in the narrative—and the concluding sentence (&lt;em&gt;"Even for beginners of programming, since the learning hurdle is low, trying to use it as a first step of workflow improvement is very recommended."&lt;/em&gt;) is polished into &lt;em&gt;"Because of its low barrier to entry, GAS is highly recommended for programming beginners as a first step toward workflow automation."&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Educational Coaching Replies Attached to Every Suggestion Card:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;In the Google Docs comment stream in the center, each suggestion card automatically includes a reply from &lt;strong&gt;DocuMentor AI&lt;/strong&gt; detailing the &lt;strong&gt;Category&lt;/strong&gt; (e.g., &lt;code&gt;[DocuMentor AI | Conciseness]&lt;/code&gt;, &lt;code&gt;[DocuMentor AI | Grammar &amp;amp; Conciseness]&lt;/code&gt;), the specific &lt;strong&gt;💡 Reason for Revision&lt;/strong&gt; (e.g., &lt;em&gt;"Removes filler words ("basically") and wordy passive phrases ("that is provided by", "it is made based on") to create a crisp, professional opening sentence."&lt;/em&gt;), and an actionable &lt;strong&gt;🎓 Learning Point&lt;/strong&gt; (e.g., &lt;em&gt;"Avoid conversational fillers like "basically" in technical writing, and convert relative clauses into concise modifiers where possible."&lt;/em&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Interactive Sidebar Controls &amp;amp; Real-Time Process Dashboard:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;On the right side, the sidebar displays all action buttons—including &lt;strong&gt;🚀 Run Proofreading (Suggest &amp;amp; Coach)&lt;/strong&gt;, &lt;strong&gt;🎓 Generate Learning Summary from Suggestions &amp;amp; Comments&lt;/strong&gt;, &lt;strong&gt;📝 Generate Initial Text from Prompt&lt;/strong&gt;, &lt;strong&gt;✅ Accept All Suggestions&lt;/strong&gt;, &lt;strong&gt;🧹 Clear All Suggestions&lt;/strong&gt;, &lt;strong&gt;🗑️ Clear All Comments&lt;/strong&gt;, and &lt;strong&gt;📖 Re-open Latest Learning Summary Dialog&lt;/strong&gt;—alongside the completed Step 1 metrics (&lt;code&gt;📑 Scope: Entire Active Tab (Tab 1)&lt;/code&gt;, &lt;code&gt;1267 Target Chars&lt;/code&gt;, &lt;code&gt;0/60 Existing&lt;/code&gt;, &lt;code&gt;1 Tables&lt;/code&gt;, &lt;code&gt;1 Figures&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Holistic Self-Learning: The "Writing Coach &amp;amp; Learning Summary" Generated from the &lt;code&gt;sample/&lt;/code&gt; Manuscript
&lt;/h3&gt;

&lt;p&gt;Immediately upon completing the 4-stage pipeline on the &lt;code&gt;sample&lt;/code&gt; manuscript (and also executable anytime on existing suggestions and comments via &lt;strong&gt;🎓 Generate Learning Summary from Suggestions &amp;amp; Comments&lt;/strong&gt; or viewable via &lt;strong&gt;📖 Re-open Latest Learning Summary Dialog&lt;/strong&gt;), &lt;strong&gt;DocuMentor AI&lt;/strong&gt; pops up a custom modal dialog with a one-click &lt;strong&gt;📋 Copy to Clipboard&lt;/strong&gt; button. Below is the exact evaluation and learning summary generated when proofreading the &lt;code&gt;sample/&lt;/code&gt; text, table, and figure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[🎓 Writing Coach &amp;amp; Learning Summary (DocuMentor AI v1.0.0)]

■ 📋 Overall Manuscript Evaluation
This introductory guide to Google Apps Script (GAS) clearly communicates the core value proposition of the platform, including its ease of setup, integration with Google Workspace, and connection to the Gemini AI API. However, the body text currently contains several grammatical errors, redundant modifiers, and overly conversational phrases (e.g., "most biggest," "many various," "softwares"). Furthermore, while Figure 1 and Table 1 present compelling time-saving metrics (reducing manual work from 120 minutes to 30 and 5 minutes), the body text never explicitly references the table or figure, nor does Table 1 include the 120-minute manual baseline shown in the chart. Aligning your text, table, and visual data while tightening your prose will significantly elevate the professionalism of this document.

■ 🛠️ What Was Revised
Revisions focused on eliminating redundant modifiers and conversational filler, correcting grammatical errors (such as double superlatives and uncountable nouns), improving sentence flow, and aligning the data terminology across the body text, Table 1, and Figure 1.

■ 🔍 Areas for Improvement &amp;amp; Writing Habits
The draft exhibits a habit of stacking redundant intensifiers ("most biggest," "many various," "totally do not... at all"), using uncountable nouns in the plural form ("softwares," "routine works"), and translating idiomatic structures directly into English ("There is a merit that...", "is very recommended"). Additionally, visual and tabular data are dropped into the document without being cited in the narrative or fully synchronized with one another.

■ 🌱 Actionable Advice for Future Writing
1. Avoid pairing superlatives or similar adjectives together (use "biggest" instead of "most biggest," and "various" instead of "many various"). 2. Remember that nouns like "software" and "work" (when meaning labor/tasks) are uncountable in English and do not take an "-s." 3. Always introduce and interpret your tables and figures in the body text, ensuring that baselines and labels match across all elements.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  How This Summary Actively Promotes Self-Learning and Skill Growth
&lt;/h4&gt;

&lt;p&gt;While the inline suggestions and comment replies in Google Docs show &lt;strong&gt;where&lt;/strong&gt; individual sentences, table cells, and figures were reviewed, inspecting line-by-line edits alone can sometimes make it hard to see the bigger picture. Combining inline suggestions and anchored comments with this structured &lt;strong&gt;Writing Coach &amp;amp; Learning Summary&lt;/strong&gt; creates a powerful pedagogical effect:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Balanced Macro-Level Diagnostic (&lt;code&gt;📋 Overall Manuscript Evaluation&lt;/code&gt; &amp;amp; &lt;code&gt;🛠️ What Was Revised&lt;/code&gt;):&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;The coach first affirms the strengths of the draft (&lt;em&gt;"clearly communicates the core value proposition of the platform, including its ease of setup, integration with Google Workspace, and connection to the Gemini AI API"&lt;/em&gt;) before categorizing the core areas for improvement. This gives the author a bird's-eye view of how eliminating redundant modifiers and conversational filler, fixing grammar, and aligning the body text, &lt;code&gt;Table 1&lt;/code&gt;, and &lt;code&gt;Figure 1&lt;/code&gt; work together to elevate the entire manuscript.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Metacognitive Awareness of Personal Writing Habits (&lt;code&gt;🔍 Areas for Improvement &amp;amp; Writing Habits&lt;/code&gt;):&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Instead of treating mistakes as isolated typos, DocuMentor AI surfaces &lt;strong&gt;systemic patterns and unconscious habits&lt;/strong&gt; in the author's writing—such as stacking redundant intensifiers (&lt;em&gt;&lt;code&gt;"most biggest"&lt;/code&gt;, &lt;code&gt;"many various"&lt;/code&gt;, &lt;code&gt;"totally do not... at all"&lt;/code&gt;&lt;/em&gt;), using uncountable nouns in the plural form (&lt;em&gt;&lt;code&gt;"softwares"&lt;/code&gt;, &lt;code&gt;"routine works"&lt;/code&gt;&lt;/em&gt;), and translating idiomatic Japanese structures directly into English (&lt;em&gt;&lt;code&gt;"There is a merit that..."&lt;/code&gt;, &lt;code&gt;"is very recommended"&lt;/code&gt;&lt;/em&gt;). Becoming consciously aware of one's own recurring linguistic habits is the fastest way to stop repeating them.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multimodal Document Design &amp;amp; Data Consistency Literacy (&lt;code&gt;Text ⇄ Table 1 ⇄ Figure 1&lt;/code&gt;):&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Notice that the evaluation goes far beyond English grammar: by visually inspecting &lt;code&gt;Figure 1&lt;/code&gt; (&lt;code&gt;reducing manual work from 120 minutes to 30 and 5 minutes&lt;/code&gt;), it points out both that the body text never explicitly references &lt;code&gt;Table 1&lt;/code&gt; or &lt;code&gt;Figure 1&lt;/code&gt; and that &lt;code&gt;Table 1&lt;/code&gt; omits the &lt;code&gt;120-minute&lt;/code&gt; manual baseline shown in the bar chart. Authors learn a vital rule of technical and academic communication—&lt;strong&gt;tables and figures must be explicitly cited in the narrative, and baselines and labels must be synchronized across prose, tables, and charts&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Actionable, Portable Takeaways for the Next Draft (&lt;code&gt;🌱 Actionable Advice for Future Writing&lt;/code&gt;):&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;By distilling the entire review into three concrete rules (1. avoiding paired superlatives or duplicate adjectives like &lt;code&gt;"most biggest"&lt;/code&gt; and &lt;code&gt;"many various"&lt;/code&gt;, 2. remembering that nouns like &lt;code&gt;"software"&lt;/code&gt; and &lt;code&gt;"work"&lt;/code&gt; are uncountable and do not take an &lt;code&gt;"-s"&lt;/code&gt;, and 3. always introducing and interpreting tables and figures in the body text while ensuring baselines and labels match across all elements), the author receives a clear checklist they can immediately apply when writing their next article. With the &lt;strong&gt;📋 Copy to Clipboard&lt;/strong&gt; button, writers can easily log these summaries—whether generated from AI proofreading or from colleagues' existing suggestions and comments—into a personal learning notebook to track their growth over time.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Expected Impact &amp;amp; Practical Use Cases
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Expected Benefits
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Transforming AI Proofreading into Active Learning:&lt;/strong&gt; Instead of passively accepting rewrites, authors see their original phrasing side-by-side with the suggested improvement, the specific reason, and a high-level summary of their writing habits.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Learning from Human Reviewers &amp;amp; Colleagues' Feedback:&lt;/strong&gt; By clicking &lt;strong&gt;🎓 Generate Learning Summary from Suggestions &amp;amp; Comments&lt;/strong&gt; on a document reviewed by colleagues, supervisors, or editors, authors can synthesize scattered human suggestions and comments into a structured, actionable learning summary.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Accelerated Review Cycles:&lt;/strong&gt; One-click &lt;strong&gt;Accept All Suggestions&lt;/strong&gt; (&lt;code&gt;acceptSuggestion&lt;/code&gt;), &lt;strong&gt;Clear All Suggestions&lt;/strong&gt; (&lt;code&gt;rejectSuggestion&lt;/code&gt;), and &lt;strong&gt;Clear All Comments&lt;/strong&gt; (&lt;code&gt;deleteComment&lt;/code&gt;) buttons allow you to accept or reset dozens of suggestions and comments across the active tab in a single batch call.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Respecting Existing Pending Suggestions:&lt;/strong&gt; Because DocuMentor AI fetches the document with &lt;code&gt;SUGGESTIONS_INLINE&lt;/code&gt;, it detects existing unresolved suggestions and proposes additional edits only on settled text blocks without colliding with pending changes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Complete Bilingual Support (&lt;code&gt;EN&lt;/code&gt; / &lt;code&gt;JA&lt;/code&gt;):&lt;/strong&gt; Switching between &lt;code&gt;EN&lt;/code&gt; and &lt;code&gt;JA&lt;/code&gt; in the sidebar header switches both the UI labels and 100% of Gemini's generated categories, reasons, and learning summaries.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Ideal Scenarios
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Technical Documentation &amp;amp; Blog Posts:&lt;/strong&gt; Ensuring terminology, tables, and architecture diagrams are clear, concise, and mutually consistent before publication.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Business Proposals &amp;amp; Executive Reports:&lt;/strong&gt; Polishing tone and verifying that summary tables match embedded charts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Language Learning &amp;amp; Non-Native English/Japanese Writing:&lt;/strong&gt; Identifying personal recurring grammar or phrasing habits (such as redundant modifiers or uncountable noun mistakes) and copying the &lt;strong&gt;Writing Coach &amp;amp; Learning Summary&lt;/strong&gt; into a personal study log.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Drafting from Scratch:&lt;/strong&gt; Entering a topic into the Custom Prompt box on an empty tab and clicking &lt;strong&gt;📝 Generate Initial Text from Prompt&lt;/strong&gt; to create a structured starting draft, followed by iterative coaching passes.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Continuous Evolution &amp;amp; Feature Updates: Performance Optimization and Pedagogical Enhancements
&lt;/h2&gt;

&lt;p&gt;Following the initial release (&lt;code&gt;v1.0.0&lt;/code&gt;), &lt;strong&gt;DocuMentor AI&lt;/strong&gt; is continuously evolving to streamline large-manuscript performance and deepen active-recall learning based on real-world usage and community feedback. Below is a breakdown of the architectural and pedagogical enhancements introduced across each release (&lt;code&gt;v1.0.1&lt;/code&gt; and &lt;code&gt;v1.0.2&lt;/code&gt;), along with a version-by-version comparison table.&lt;/p&gt;

&lt;h3&gt;
  
  
  Streamlining Large-Manuscript &amp;amp; Selection-Scoped Proofreading via the Gemini File API
&lt;/h3&gt;

&lt;p&gt;In the initial release (&lt;strong&gt;&lt;code&gt;v1.0.0&lt;/code&gt;&lt;/strong&gt;), while embedded figures and charts (&lt;code&gt;VISUAL_1&lt;/code&gt;, etc.) were uploaded as binary files to the &lt;strong&gt;Gemini File API&lt;/strong&gt; and referenced via &lt;code&gt;fileData.fileUri&lt;/code&gt;, the manuscript's body text, Markdown tables (&lt;code&gt;TABLE_1&lt;/code&gt;, etc.), and target editable text blocks were embedded directly inside the &lt;code&gt;generateContent&lt;/code&gt; JSON prompt string. Moreover, when &lt;strong&gt;"🎯 Proofread Selection Only"&lt;/strong&gt; was turned &lt;strong&gt;ON&lt;/strong&gt;, &lt;code&gt;v1.0.0&lt;/code&gt; still included the entire active tab's text inside the prompt alongside the selected blocks.&lt;/p&gt;

&lt;p&gt;To minimize JSON serialization overhead, request payload size, and token costs on long documents, &lt;strong&gt;&lt;code&gt;v1.0.1&lt;/code&gt;&lt;/strong&gt; introduced three architectural improvements:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Uploading the Manuscript Text &amp;amp; Tables as a Markdown File via the Gemini File API (&lt;code&gt;fileData.fileUri&lt;/code&gt;)&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Step 2 (&lt;code&gt;step2_generateProofreading&lt;/code&gt; and &lt;code&gt;step2_generateLearningSummaryFromExisting&lt;/code&gt;) packages the target manuscript data—including &lt;strong&gt;Target Editable Settled Text Blocks&lt;/strong&gt;, &lt;strong&gt;Target Tables&lt;/strong&gt;, &lt;strong&gt;Target Figure Metadata&lt;/strong&gt;, and &lt;strong&gt;Review History&lt;/strong&gt;—into a structured UTF-8 Markdown file (&lt;code&gt;manuscript_context.md&lt;/code&gt;, &lt;code&gt;mimeType: "text/markdown"&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;This Markdown file is uploaded to the &lt;strong&gt;Gemini File API&lt;/strong&gt; (&lt;code&gt;upload/v1beta/files&lt;/code&gt; using the resumable upload protocol) &lt;strong&gt;in the exact same parallel &lt;code&gt;UrlFetchApp.fetchAll()&lt;/code&gt; batch&lt;/strong&gt; as any embedded figure images, referenced in &lt;code&gt;contents[].parts&lt;/code&gt; via &lt;code&gt;fileData.fileUri&lt;/code&gt;, and automatically deleted alongside all image files in the &lt;code&gt;finally&lt;/code&gt; block (&lt;code&gt;deleteGeminiFiles_&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;True Selection-Scoped Extraction &amp;amp; Upload (&lt;code&gt;selected_excerpt.md&lt;/code&gt;)&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;When &lt;strong&gt;"🎯 Proofread Selection Only"&lt;/strong&gt; (&lt;code&gt;selectionOnly&lt;/code&gt;) is turned &lt;strong&gt;ON&lt;/strong&gt;, Step 1 (&lt;code&gt;parseDocumentStructure_&lt;/code&gt;) and Step 2 (&lt;code&gt;buildProofreadingPromptAndSchema_&lt;/code&gt;) extract, package, and upload &lt;strong&gt;strictly the selected portion&lt;/strong&gt; (&lt;code&gt;selected_excerpt.md&lt;/code&gt;—containing only the highlighted text blocks, selected tables, and selected figures) with zero unselected text shipped.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Eliminating Redundant Text Duplication &amp;amp; Minimizing Client–Server IPC Payloads&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Redundant duplication between full-tab text and target editable text blocks was removed from the uploaded context, and Step 3 (&lt;code&gt;step3_buildRequestBody&lt;/code&gt;) transfers only the compact target block coordinates between the sidebar UI and the Apps Script server.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Active-Recall Hint-Only Coaching Mode &amp;amp; Resolved Review History Extraction
&lt;/h3&gt;

&lt;p&gt;As writers and reviewers tested the workflow across iterative review cycles, two important pedagogical and architectural requirements emerged—both of which were implemented in &lt;strong&gt;&lt;code&gt;v1.0.2&lt;/code&gt;&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;💡 Hint-Only Mode (&lt;code&gt;PROOFREAD_HINT_ONLY&lt;/code&gt; — Comment Without Inline Rewrite)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Motivation:&lt;/strong&gt; Even when &lt;code&gt;WriteMode.SUGGEST&lt;/code&gt; displays the &lt;code&gt;Reason&lt;/code&gt; and &lt;code&gt;Learning Point&lt;/code&gt; alongside a proposed edit, having the finished rewrite already sitting in the document makes it tempting to passively click &lt;strong&gt;Accept&lt;/strong&gt; without trying to fix the sentence yourself first.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Implementation in &lt;code&gt;v1.0.2&lt;/code&gt;:&lt;/strong&gt; A &lt;strong&gt;"💡 Hint-Only Mode (Comment Without Rewrite)"&lt;/strong&gt; toggle switch was added to the &lt;strong&gt;Proofreading Scope &amp;amp; Controls&lt;/strong&gt; card in the sidebar:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;When OFF (Default):&lt;/strong&gt; Operates in standard Suggest &amp;amp; Coach mode (&lt;code&gt;deleteContentRange&lt;/code&gt; + &lt;code&gt;insertText&lt;/code&gt; with &lt;code&gt;writeControl: { writeMode: "SUGGEST" }&lt;/code&gt; + &lt;code&gt;addCommentReply&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;When ON:&lt;/strong&gt; Step 3 (&lt;code&gt;step3_buildRequestBody&lt;/code&gt;) and Step 4 (&lt;code&gt;step4_applySuggestions&lt;/code&gt;) do not propose inline text replacements on the canvas. Instead, they anchor a native Google Docs comment (&lt;code&gt;insertComment&lt;/code&gt;) directly onto the exact UTF-16 character range of each flagged phrase, displaying only the &lt;strong&gt;🎯 Flagged Phrase (&lt;code&gt;originalText&lt;/code&gt;)&lt;/strong&gt;, &lt;strong&gt;💡 Issue (&lt;code&gt;reason&lt;/code&gt;)&lt;/strong&gt;, and &lt;strong&gt;🎓 Coaching Hint (&lt;code&gt;learningPoint&lt;/code&gt;)&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Collapsible Reference Rewrite in the Sidebar:&lt;/strong&gt; Each card in Step 2 of the sidebar dashboard places the AI's reference rewrite (&lt;code&gt;suggestedText&lt;/code&gt;) inside a collapsible &lt;code&gt;&amp;lt;details&amp;gt;&lt;/code&gt; accordion labeled &lt;strong&gt;&lt;code&gt;👁️ Show Reference Rewrite (After Trying Yourself)&lt;/code&gt;&lt;/strong&gt;, allowing the author to revise the sentence on their own first and compare against Gemini's reference rewrite afterward.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero-Shift Phase 2 Optimization:&lt;/strong&gt; Because &lt;code&gt;insertComment&lt;/code&gt; does not insert or delete characters in the document body, character indices do not shift during Phase 1 of Step 4 when Hint-Only Mode is active—allowing Phase 2 (anchoring multimodal comments on &lt;code&gt;VISUAL_N&lt;/code&gt; and &lt;code&gt;TABLE_N&lt;/code&gt;) to execute immediately without an extra &lt;code&gt;documents.get&lt;/code&gt; re-fetch round-trip.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;📜 Resolved Review History Extraction &amp;amp; Inclusion (&lt;code&gt;INCLUDE_RESOLVED_HISTORY&lt;/code&gt;)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Motivation:&lt;/strong&gt; In Google Docs, once an author or reviewer accepts/rejects inline suggestions or clicks &lt;strong&gt;Resolve&lt;/strong&gt; on comment threads, those threads disappear from the main document canvas. Previously (&lt;code&gt;v1.0.0&lt;/code&gt; and &lt;code&gt;v1.0.1&lt;/code&gt;), this prevented generating a cumulative &lt;strong&gt;🎓 Writing Coach &amp;amp; Learning Summary&lt;/strong&gt; after finishing a review pass, or informing the AI about edits the author had already intentionally rejected.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Implementation in &lt;code&gt;v1.0.2&lt;/code&gt;:&lt;/strong&gt; Calling the Google Docs API (&lt;code&gt;v1&lt;/code&gt; &lt;code&gt;documents.get&lt;/code&gt;) with &lt;code&gt;commentsViewMode=COMMENTS_VIEW_MODE_INCLUDED&lt;/code&gt; returns both open and resolved threads in the raw JSON response. Step 1 (&lt;code&gt;extractCommentAndSuggestionThreads_&lt;/code&gt;) now inspects &lt;code&gt;docJson.comments&lt;/code&gt;, &lt;code&gt;docJson.suggestions&lt;/code&gt;, &lt;code&gt;documentTab.comments&lt;/code&gt;, and &lt;code&gt;documentTab.suggestions&lt;/code&gt; to separate &lt;strong&gt;active open threads (&lt;code&gt;existingComments&lt;/code&gt;)&lt;/strong&gt; from &lt;strong&gt;resolved review history (&lt;code&gt;resolvedReviews&lt;/code&gt;—classified as &lt;code&gt;RESOLVED&lt;/code&gt;, &lt;code&gt;ACCEPTED&lt;/code&gt;, &lt;code&gt;REJECTED&lt;/code&gt;, or &lt;code&gt;RESOLVED_SUGGESTION&lt;/code&gt;)&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Collapsible Resolved Review History Ledger in Step 1:&lt;/strong&gt; Whenever resolved comments or settled suggestions exist in the active tab, Step 1 displays the metric as &lt;code&gt;Active / Resolved&lt;/code&gt; and renders an expandable &lt;strong&gt;&lt;code&gt;📜 View Resolved Review History&lt;/code&gt;&lt;/strong&gt; ledger showing each historical thread's status badge, reviewer name, anchor quote, and comment/reply text.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cumulative Learning Summary &amp;amp; Rejection-Aware Proofreading (&lt;code&gt;📜 Include Resolved Review History&lt;/code&gt; Toggle):&lt;/strong&gt; Turning &lt;strong&gt;ON&lt;/strong&gt; the &lt;strong&gt;"📜 Include Resolved Review History"&lt;/strong&gt; toggle switch includes all extracted resolved reviews in the Markdown context file uploaded to the Gemini File API—enabling &lt;strong&gt;🎓 Generate Learning Summary from Suggestions &amp;amp; Comments&lt;/strong&gt; to synthesize a complete study guide even when &lt;code&gt;0&lt;/code&gt; active threads remain on the canvas, and preventing &lt;strong&gt;🚀 Run Proofreading&lt;/strong&gt; from re-suggesting previously rejected edits.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Version-by-Version Feature Comparison
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature / Aspect&lt;/th&gt;
&lt;th&gt;
&lt;code&gt;v1.0.0&lt;/code&gt; (Initial Release)&lt;/th&gt;
&lt;th&gt;&lt;code&gt;v1.0.1&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;&lt;code&gt;v1.0.2&lt;/code&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Manuscript Text &amp;amp; Tables Delivery&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Embedded directly inside the &lt;code&gt;generateContent&lt;/code&gt; JSON prompt string.&lt;/td&gt;
&lt;td&gt;Uploaded as a temporary Markdown file (&lt;code&gt;text/markdown&lt;/code&gt;) to the &lt;strong&gt;Gemini File API&lt;/strong&gt; (&lt;code&gt;fileData.fileUri&lt;/code&gt;).&lt;/td&gt;
&lt;td&gt;Same parallel &lt;strong&gt;Gemini File API&lt;/strong&gt; Markdown upload, enriched with optional &lt;strong&gt;Resolved Review History&lt;/strong&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Embedded Figures (&lt;code&gt;VISUAL_N&lt;/code&gt;) Delivery&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Uploaded to the &lt;strong&gt;Gemini File API&lt;/strong&gt; (&lt;code&gt;image/png&lt;/code&gt;) and referenced via &lt;code&gt;fileData.fileUri&lt;/code&gt;.&lt;/td&gt;
&lt;td&gt;Uploaded &lt;strong&gt;in parallel with the manuscript Markdown file&lt;/strong&gt; in a single &lt;code&gt;UrlFetchApp.fetchAll()&lt;/code&gt; batch.&lt;/td&gt;
&lt;td&gt;Same parallel upload + automatic &lt;code&gt;finally&lt;/code&gt; cleanup.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Proofread Selection Only (&lt;code&gt;ON&lt;/code&gt;)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Restricted editable blocks to the selection, but still sent full-tab text in the prompt.&lt;/td&gt;
&lt;td&gt;Extracts and uploads &lt;strong&gt;strictly the selected portion (&lt;code&gt;selected_excerpt.md&lt;/code&gt;)&lt;/strong&gt;—zero unselected text shipped.&lt;/td&gt;
&lt;td&gt;Preserves strict selection-scoped upload and supports both &lt;strong&gt;Suggest Mode&lt;/strong&gt; and &lt;strong&gt;Hint-Only Mode&lt;/strong&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Text Coaching Mode on Canvas&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Inline replacement (&lt;code&gt;WriteMode.SUGGEST&lt;/code&gt;) + &lt;code&gt;addCommentReply&lt;/code&gt; only.&lt;/td&gt;
&lt;td&gt;Inline replacement (&lt;code&gt;WriteMode.SUGGEST&lt;/code&gt;) + &lt;code&gt;addCommentReply&lt;/code&gt; only.&lt;/td&gt;
&lt;td&gt;Switchable between &lt;strong&gt;Standard Suggest Mode&lt;/strong&gt; (&lt;code&gt;WriteMode.SUGGEST&lt;/code&gt;) and &lt;strong&gt;💡 Hint-Only Mode&lt;/strong&gt; (&lt;code&gt;insertComment&lt;/code&gt; without inline rewrite + collapsible reference rewrite in sidebar).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Resolved Comments &amp;amp; Accepted/Rejected Suggestions&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Ignored (&lt;code&gt;status === "RESOLVED"&lt;/code&gt; filtered out; only pending inline suggestions inspected).&lt;/td&gt;
&lt;td&gt;Ignored (only active suggestions and open comments extracted).&lt;/td&gt;
&lt;td&gt;Extracted via &lt;code&gt;extractCommentAndSuggestionThreads_&lt;/code&gt;, viewable in the &lt;strong&gt;Step 1 History Ledger&lt;/strong&gt;, and toggleable via &lt;strong&gt;📜 Include Resolved Review History&lt;/strong&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Learning Summary after Accepting/Resolving All Reviews&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Could not run once active suggestions and open comments reached &lt;code&gt;0&lt;/code&gt;.&lt;/td&gt;
&lt;td&gt;Could not run once active suggestions and open comments reached &lt;code&gt;0&lt;/code&gt;.&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Fully supported&lt;/strong&gt; when &lt;strong&gt;📜 Include Resolved Review History&lt;/strong&gt; is ON—synthesizes lessons from resolved comments and accepted/rejected suggestions.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  How These Updates Enhance Practical Workflows
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Compact Prompts &amp;amp; Fast Selection Polishing (&lt;code&gt;v1.0.1&lt;/code&gt;+):&lt;/strong&gt; Whether your Google Doc is 2 pages or 50 pages long, uploading &lt;code&gt;manuscript_context.md&lt;/code&gt; (or &lt;code&gt;selected_excerpt.md&lt;/code&gt; in Selection-Only mode) in parallel with figure images keeps the &lt;code&gt;generateContent&lt;/code&gt; prompt tiny and fast with zero extra sequential HTTP round-trips.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;True Active-Recall Writing Practice (&lt;code&gt;v1.0.2&lt;/code&gt;+ 💡 Hint-Only Mode):&lt;/strong&gt; Writers, students, and language learners who want to avoid passive AI reliance can toggle &lt;strong&gt;💡 Hint-Only Mode&lt;/strong&gt; ON. Flagged sentences are highlighted with diagnostic coaching hints in Google Docs comments, letting the author try rewriting the sentence themselves before expanding the reference rewrite in the sidebar.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;End-of-Review Retrospective Learning (&lt;code&gt;v1.0.2&lt;/code&gt;+ 📜 Include Resolved Review History):&lt;/strong&gt; You no longer have to remember to run the Learning Summary &lt;em&gt;before&lt;/em&gt; resolving comments or accepting/rejecting suggestions. Even after a collaborative review cycle is completely finished and all threads are resolved, turning ON &lt;strong&gt;📜 Include Resolved Review History&lt;/strong&gt; and clicking &lt;strong&gt;🎓 Generate Learning Summary from Suggestions &amp;amp; Comments&lt;/strong&gt; distills the entire review history into a permanent, copyable study guide.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;By combining the new &lt;strong&gt;Google Docs API Suggestion &amp;amp; Comment Management&lt;/strong&gt; features (&lt;code&gt;WriteMode.SUGGEST&lt;/code&gt;, &lt;code&gt;addCommentReply&lt;/code&gt;, &lt;code&gt;insertComment&lt;/code&gt;, &lt;code&gt;acceptSuggestion&lt;/code&gt;, &lt;code&gt;rejectSuggestion&lt;/code&gt;, &lt;code&gt;deleteComment&lt;/code&gt;) with &lt;strong&gt;Gemini's multimodal reasoning&lt;/strong&gt;, &lt;strong&gt;DocuMentor AI&lt;/strong&gt; bridges the gap between automated proofreading and human skill development. Moreover, building &lt;strong&gt;DocuMentor AI&lt;/strong&gt; with &lt;strong&gt;Gemini 4 Argon&lt;/strong&gt;—and running &lt;code&gt;gemini-4-argon&lt;/code&gt; as its high-precision proofreading and coaching engine—demonstrated the remarkable power of Gemini 4 Argon in both architecting complex Google Workspace applications and performing deep, cross-modal manuscript analysis across text, tables, and charts.&lt;/p&gt;

&lt;p&gt;All source code and sample files are available on GitHub:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Repository:&lt;/strong&gt; &lt;a href="https://github.com/tanaikech/DocuMentorAI" rel="noopener noreferrer"&gt;https://github.com/tanaikech/DocuMentorAI&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Reflections on the Power of Gemini 4 Argon
&lt;/h2&gt;

&lt;p&gt;Throughout the development and testing of &lt;strong&gt;DocuMentor AI&lt;/strong&gt;, I had the opportunity to experience the capabilities of &lt;strong&gt;Gemini 4 Argon&lt;/strong&gt; first-hand—both as an AI software engineer that built the application and as the multimodal reasoning engine running inside it. Here are the key points that truly amazed me about the power of &lt;strong&gt;Gemini 4 Argon&lt;/strong&gt;:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. The Initial Prompt Used to Build the Application
&lt;/h3&gt;

&lt;p&gt;To develop this application, the initial prompt I gave to &lt;strong&gt;Gemini 4 Argon&lt;/strong&gt; was simply the following single paragraph:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Build a Google Docs add-on using Google Apps Script, the Gemini API, and the new Google Docs API v1 suggestion and comment features (`WriteMode.SUGGEST`, `addCommentReply`, `insertComment`, `acceptSuggestion`, `rejectSuggestion`, and `deleteComment`). Instead of overwriting the document directly, the add-on should proofread the active tab (or selected range)—including body text, tables, and embedded figures—by applying inline suggested edits, attaching a short reason and learning point to each suggestion, and displaying a "Writing Coach &amp;amp; Learning Summary" dialog so the author can actively learn from the revisions.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. One-Shot Feature Implementation That Ran Without a Single Error
&lt;/h3&gt;

&lt;p&gt;What surprised me first was that &lt;strong&gt;from this single prompt alone, Gemini 4 Argon implemented all of the envisioned features—and the generated code worked right out of the box without any errors&lt;/strong&gt;. Even though the prompt only described &lt;em&gt;what&lt;/em&gt; the add-on should achieve at a high level, Gemini 4 Argon autonomously designed the 4-stage pipeline, the interactive sidebar with real-time progress tracking, the batch accept/clear controls, and the custom modal dialog for the &lt;em&gt;Writing Coach &amp;amp; Learning Summary&lt;/em&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Flawless Document Index Calculation (&lt;code&gt;startIndex&lt;/code&gt; / &lt;code&gt;endIndex&lt;/code&gt;) from &lt;code&gt;documents.get&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Anyone who has worked with the Google Docs API (&lt;code&gt;v1&lt;/code&gt;) knows that calculating character indices (&lt;code&gt;startIndex&lt;/code&gt; and &lt;code&gt;endIndex&lt;/code&gt;) for &lt;code&gt;documents.batchUpdate&lt;/code&gt; is notoriously tricky—especially when a document contains multiple paragraphs, nested table cells, embedded images, and existing unresolved inline suggestions (&lt;code&gt;SUGGESTIONS_INLINE&lt;/code&gt;). I was amazed that &lt;strong&gt;Gemini 4 Argon parsed the structural data from &lt;code&gt;documents.get&lt;/code&gt; and constructed the request bodies for suggestions and comments using the exact character indices without a single off-by-one mistake&lt;/strong&gt;. It even automatically implemented descending index sorting (applying replacements from the bottom of the document to the top) so that earlier character indices never shift during a batch update.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Autonomous Multi-Step Pipeline for Extracting Google Docs Figures (&lt;code&gt;contentUri&lt;/code&gt; ➔ Binary ➔ Base64 / Gemini File API)
&lt;/h3&gt;

&lt;p&gt;When passing figures embedded in a Google Document to Gemini for multimodal inspection, &lt;code&gt;documents.get&lt;/code&gt; does not return raw image bytes—it only returns temporary URLs (&lt;code&gt;contentUri&lt;/code&gt;) nested inside &lt;code&gt;inlineObjects&lt;/code&gt; or &lt;code&gt;positionedObjects&lt;/code&gt;. Without being explicitly instructed on the low-level mechanics, &lt;strong&gt;Gemini 4 Argon implemented the entire multi-step process from that single initial prompt&lt;/strong&gt;: extracting each image's &lt;code&gt;contentUri&lt;/code&gt; URL from &lt;code&gt;documents.get&lt;/code&gt;, fetching the binary image data via &lt;code&gt;UrlFetchApp.fetchAll()&lt;/code&gt;, converting the binary blobs into Base64 strings, and passing them as &lt;code&gt;inlineData&lt;/code&gt; payloads to the Gemini API alongside the document text and Markdown tables. &lt;em&gt;(Note: To further optimize the add-on for academic papers and long reports containing many figures, we subsequently upgraded this step to upload the fetched binary images via the &lt;strong&gt;Gemini File API&lt;/strong&gt; (&lt;code&gt;upload/v1beta/files&lt;/code&gt;), pass their &lt;code&gt;fileData.fileUri&lt;/code&gt; references in the prompt, and automatically delete the temporary files (&lt;code&gt;DELETE v1beta/files/...&lt;/code&gt; inside a &lt;code&gt;finally&lt;/code&gt; block) right after content generation.)&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Additional Technical Highlights That Showcased Gemini 4 Argon's Depth
&lt;/h3&gt;

&lt;p&gt;Beyond the points above, several other engineering details stood out during development and testing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Two-Phase Chained &lt;code&gt;batchUpdate&lt;/code&gt; Orchestration (&lt;code&gt;createdSuggestionIds&lt;/code&gt; ➔ &lt;code&gt;addCommentReply&lt;/code&gt; &amp;amp; Post-Suggestion &lt;code&gt;insertComment&lt;/code&gt; Anchoring):&lt;/strong&gt; Because you cannot attach a comment reply to a suggestion before that suggestion exists in Google Docs, Gemini 4 Argon automatically split Step 4 into a chained batch workflow—first executing &lt;code&gt;deleteContentRange&lt;/code&gt; and &lt;code&gt;insertText&lt;/code&gt; under &lt;code&gt;writeControl: { writeMode: "SUGGEST" }&lt;/code&gt;, extracting the newly generated &lt;code&gt;createdSuggestionIds&lt;/code&gt; from &lt;code&gt;suggestionResponses&lt;/code&gt;, and immediately firing a second &lt;code&gt;batchUpdate&lt;/code&gt; with &lt;code&gt;addCommentReply&lt;/code&gt; to link the educational &lt;code&gt;Reason&lt;/code&gt; and &lt;code&gt;Learning Point&lt;/code&gt; to each exact inline edit, followed by re-fetching updated post-suggestion indices to anchor &lt;code&gt;insertComment&lt;/code&gt; review comments onto figures (&lt;code&gt;VISUAL_1&lt;/code&gt;) and tables (&lt;code&gt;TABLE_1&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bridging &lt;code&gt;DocumentApp&lt;/code&gt; Selection and the &lt;code&gt;Docs&lt;/code&gt; REST API (&lt;code&gt;v1&lt;/code&gt;):&lt;/strong&gt; The Google Docs REST API (&lt;code&gt;v1&lt;/code&gt;) operates on global UTF-16 character indices and has no awareness of the user's live cursor highlight, whereas Apps Script's &lt;code&gt;DocumentApp.getActiveDocument().getSelection()&lt;/code&gt; returns &lt;code&gt;RangeElement&lt;/code&gt; objects without REST API indices. Gemini 4 Argon seamlessly bridged these two APIs to make the &lt;strong&gt;"Proofread Selection Only"&lt;/strong&gt; toggle and multi-tab (&lt;code&gt;tabId&lt;/code&gt;) scoping work reliably.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cross-Modal Data Verification at Runtime:&lt;/strong&gt; When running &lt;code&gt;gemini-4-argon&lt;/code&gt; inside the add-on on the &lt;code&gt;sample/&lt;/code&gt; manuscript, it went far beyond grammar correction—visually reading the bar chart in &lt;code&gt;Figure 1&lt;/code&gt; (&lt;code&gt;120 min&lt;/code&gt; for &lt;code&gt;Manual Work&lt;/code&gt; vs. &lt;code&gt;5 min&lt;/code&gt; for &lt;code&gt;GAS + Gemini AI&lt;/code&gt;), noticing that the &lt;code&gt;120-minute&lt;/code&gt; baseline was missing from &lt;code&gt;Table 1&lt;/code&gt; and the body text, and proposing coordinated edits and learning advice across text, table, and chart.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>gemini</category>
      <category>googleappsscript</category>
      <category>googleworkspace</category>
    </item>
    <item>
      <title>AlphaEvolve and Gemini: Autonomous Algorithm Discovery for Unified Disaster Prediction</title>
      <dc:creator>Tanaike</dc:creator>
      <pubDate>Wed, 14 Oct 2026 02:48:03 +0000</pubDate>
      <link>https://dev.to/gde/alphaevolve-and-gemini-autonomous-algorithm-discovery-for-unified-disaster-prediction-20f4</link>
      <guid>https://dev.to/gde/alphaevolve-and-gemini-autonomous-algorithm-discovery-for-unified-disaster-prediction-20f4</guid>
      <description>&lt;p&gt;📄 &lt;strong&gt;Published Paper (Open Access on Zenodo)&lt;/strong&gt;:&lt;br&gt;&lt;br&gt;
&lt;a href="https://zenodo.org/records/23354598" rel="noopener noreferrer"&gt;&lt;strong&gt;Autonomous Discovery of Unified Multi-Hazard Prediction Theory from Netatmo IoT Weather Big Data Using AlphaEvolve&lt;/strong&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
🔗 &lt;strong&gt;Full Paper URL&lt;/strong&gt;: &lt;a href="https://zenodo.org/records/23354598" rel="noopener noreferrer"&gt;https://zenodo.org/records/23354598&lt;/a&gt;&lt;br&gt;&lt;br&gt;
💻 &lt;strong&gt;Sample Scripts Repository (GitHub)&lt;/strong&gt;: &lt;a href="https://github.com/tanaikech/alphaevolve-multi-hazard" rel="noopener noreferrer"&gt;https://github.com/tanaikech/alphaevolve-multi-hazard&lt;/a&gt;&lt;/p&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"The book of nature is written in the language of mathematics."&lt;/em&gt; — Galileo Galilei&lt;br&gt;&lt;br&gt;
&lt;em&gt;Today, that mathematical language can be autonomously written as an algorithm, assembled into a predictive model, and elevated into a physical theory by artificial intelligence.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  1. The Invisible Whisper Before the Catastrophe — And a New Way to Do Science
&lt;/h2&gt;

&lt;p&gt;Imagine sitting in a quiet office in downtown Tokyo or suburban Osaka on a hot, muggy summer afternoon. Outside, the sky appears calm. Within thirty minutes, however, the atmosphere darkens into pitch-black fury. A violent "guerrilla rainstorm"—an extreme localized convective cloudburst—dumps 80 millimeters of torrential rain in under an hour. Underground subway entrances are inundated, calm residential roads turn into raging rivers, and vehicles are trapped beneath flooded railway underpasses.&lt;/p&gt;

&lt;p&gt;Or imagine an inland geological fault kilometers beneath our feet, quietly accumulating mechanical shear stress over decades. Without warning, the fault ruptures in an instant, generating devastating seismic shaking that levels homes before anyone can take cover.&lt;/p&gt;

&lt;p&gt;Natural disasters—extreme convective downpours, deadly inland earthquakes, and explosive volcanic eruptions—are among the most formidable threats confronting human civilization. Every year, they claim thousands of lives and inflict hundreds of billions of dollars in economic devastation. Providing accurate, early warning for these events is one of the highest callings of modern applied science. Yet, it has long remained one of the most stubborn, mathematically vexing frontiers in geophysics.&lt;/p&gt;
&lt;h3&gt;
  
  
  Why Is Predicting Natural Disasters So Excitingly, Terrifyingly Difficult?
&lt;/h3&gt;

&lt;p&gt;For decades, the standard approach in atmospheric and Earth sciences has relied upon sparse, high-cost governmental observation networks. In Japan, for example, the official meteorological radar network and the automated surface station network (AMeDAS) operate with an average spacing of roughly 17 kilometers.&lt;/p&gt;

&lt;p&gt;While 17 kilometers is well-suited for broad weather forecasting (such as tracking a 1,000-kilometer typhoon moving across the Pacific), it leaves vast spatial blind spots for micro-scale phenomena:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Micro-Scale Spatiotemporal Scale&lt;/strong&gt;: A localized convective rainstorm cell forms over a horizontal diameter of only 2 to 3 kilometers and transitions from an innocent cumulus cloud to a violent downburst within 15 to 20 minutes. It can easily mature and dump destructive rain entirely within the blank space between two official observation towers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Non-Linear Geophysical Chaos&lt;/strong&gt;: Atmospheric motions are governed by the Navier-Stokes equations, where microscopic thermal eddies cascade unpredictably into macroscopic storms.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Severe Environmental Noise&lt;/strong&gt;: Separating an actual precursor from background atmospheric noise is an operational nightmare. A sudden drop in air pressure could signify an oncoming cold front, a mundane passing shower, or the shockwave of a distant volcanic explosion. A false alarm rate as low as 5% is unacceptable in civil defense—frequent false alarms breed disaster fatigue, causing citizens to ignore warnings when a real catastrophe strikes.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  The Core Message of This Article: Autonomous Algorithm Creation for Model and Theory Building
&lt;/h3&gt;

&lt;p&gt;For years, I asked myself a question that kept me awake at night:&lt;br&gt;&lt;br&gt;
&lt;em&gt;What if the signals of nature’s impending fury are not absent, but simply hidden in plain sight—whispering across thousands of micro-sensors mounted on citizen balconies? And what if, instead of humans manually guessing thresholds, an AI could autonomously write the mathematical algorithms needed to build a predictive model and establish a unified physical theory?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In traditional science and engineering, building a disaster prediction theory requires a painstaking manual chain:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A human scientist manually writes &lt;strong&gt;algorithms&lt;/strong&gt; (conditional rules, filters, and scoring formulas) based on intuition.&lt;/li&gt;
&lt;li&gt;Those algorithms are combined to construct a &lt;strong&gt;predictive model&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;By testing the model against reality, the scientist attempts to formulate a &lt;strong&gt;physical theory&lt;/strong&gt; of disaster prediction.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When humans do this by hand across multiple hazards, cognitive bias and mathematical complexity get in the way. Conversely, if you use a standard Deep Neural Network (DNN), the AI builds a "black-box" model of millions of inscrutable weights—meaning it cannot give you a transparent algorithm, nor can it build a human-readable physical theory.&lt;/p&gt;

&lt;p&gt;In my latest research paper, &lt;a href="https://zenodo.org/records/23354598" rel="noopener noreferrer"&gt;&lt;strong&gt;"Autonomous Discovery of Unified Multi-Hazard Prediction Theory from Netatmo IoT Weather Big Data Using AlphaEvolve"&lt;/strong&gt;&lt;/a&gt; [10], now published on Zenodo (&lt;code&gt;https://zenodo.org/records/23354598&lt;/code&gt;) alongside our open-source sample scripts on GitHub (&lt;a href="https://github.com/tanaikech/alphaevolve-multi-hazard" rel="noopener noreferrer"&gt;&lt;code&gt;https://github.com/tanaikech/alphaevolve-multi-hazard&lt;/code&gt;&lt;/a&gt;), I demonstrated a third path using &lt;strong&gt;Google DeepMind’s &lt;a href="https://docs.cloud.google.com/gemini/enterprise/docs/alphaevolve/developer-guide/overview" rel="noopener noreferrer"&gt;AlphaEvolve&lt;/a&gt;&lt;/strong&gt; and &lt;strong&gt;Gemini 3.8 Flash&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Step 1 (Autonomous Algorithm Creation)&lt;/strong&gt;: AlphaEvolve autonomously writes, mutates, and refines explicit Python mathematical &lt;strong&gt;algorithms&lt;/strong&gt; without human threshold tuning.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Step 2 (Predictive Model Construction)&lt;/strong&gt;: Those autonomously generated algorithms directly construct a high-speed, deterministic &lt;strong&gt;multi-hazard prediction model&lt;/strong&gt; that achieves 100% detection across 21 historical disasters with 0.0% false alarms.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Step 3 (Physical Theory Formulation)&lt;/strong&gt;: Because the AI-created algorithm is 100% transparent white-box mathematics, we can directly read its code and elevate it into a &lt;strong&gt;unified geophysical theory&lt;/strong&gt; governing torrential rainstorms, inland earthquakes, and volcanic eruptions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this article, I will walk you through the physics of citizen weather data, how AlphaEvolve works, &lt;strong&gt;how to use AlphaEvolve in practice&lt;/strong&gt;, &lt;strong&gt;how we fed 175,363 real disaster observations into AlphaEvolve&lt;/strong&gt;, and the physical laws the AI discovered on its own.&lt;/p&gt;


&lt;h2&gt;
  
  
  2. The Secret Life of Weather Data: Why the Atmosphere Senses the Earth’s Fury
&lt;/h2&gt;

&lt;p&gt;To ordinary citizens, a personal weather station mounted on an apartment balcony is just a gadget that tells them if they need an umbrella. To a geophysicist, however, it is an ultra-sensitive, high-frequency stethoscope pressed against the living skin of planet Earth.&lt;/p&gt;

&lt;p&gt;Over the past decade, crowdsourced Internet of Things (IoT) weather networks—most notably the global &lt;a href="https://www.netatmo.com/" rel="noopener noreferrer"&gt;Netatmo&lt;/a&gt; Personal Weather Station (PWS) ecosystem [3]—have quietly revolutionized observational geography. Unlike sparse governmental towers, hundreds of thousands of citizen-owned Netatmo stations blanket dense urban centers. In metropolitan Tokyo and Kansai, the average distance between stations shrinks from 17 kilometers down to just 1 to 2 kilometers, continuously logging barometric pressure, ambient temperature, relative humidity, and wind gusts every few minutes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ftanaikech.github.io%2Fimage-storage%2F20261014a%2Finfographic_1_iot_multi_hazard_sensing.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ftanaikech.github.io%2Fimage-storage%2F20261014a%2Finfographic_1_iot_multi_hazard_sensing.png" alt="Figure 1: How Balcony Weather IoT Decodes Natural Disasters" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Figure 1 | How Balcony Weather IoT Decodes Natural Disasters.&lt;/strong&gt; The Micro-Atmospheric Stethoscope: Resolving extreme hazards invisible to sparse governmental radars across three physical domains.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  Walking Through Figure 1: Three Hazards Captured on Balconies
&lt;/h3&gt;

&lt;p&gt;As illustrated in Figure 1, crowdsourced IoT weather networks can capture physical precursors across three wildly different geophysical domains because the Earth's lithosphere, surface, and atmosphere are fundamentally coupled:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Guerrilla Downpours (Figure 1, Left Panel)&lt;/strong&gt;:
Before a single raindrop hits the ground, the pre-storm atmosphere undergoes intense thermodynamic destabilization. High-moisture air converges, latent heat is exchanged, and moist static energy—measured by &lt;em&gt;equivalent potential temperature&lt;/em&gt; (often designated as theta_e, which represents the total heat content of an air parcel combining sensible heat and latent heat stored in water vapor)—collapses. As cold, dense air descends in an evaporatively driven downdraft ("cold pool"), equivalent potential temperature plunges at rates exceeding 5.0 Kelvin per hour. Simultaneously, a micro-barometric surge sweeps across surface sensors several tens of minutes before radar reflects heavy precipitation aloft.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inland Earthquakes (Figure 1, Center Panel)&lt;/strong&gt;:
Under the well-established framework of Lithosphere-Atmosphere-Ionosphere Coupling (LAIC) [4], mechanical tectonic stress accumulating along seismogenic fault planes causes micro-cracking and releases subterranean radon gas. This alpha-particle ionization condenses atmospheric water vapor, releasing surface latent heat flux and driving a localized spatial thermal anomaly (delta theta_e of 30 Kelvin or greater) hours to days before the fault ruptures. Crucially, as highlighted in the purple badge in Figure 1, this tectonic heating occurs under &lt;strong&gt;hydrostatic, isobaric equilibrium&lt;/strong&gt;—meaning the ambient air pressure is completely calm (pressure rate of change within 2.0 hectopascals per hour), without passing cyclonic storms.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Volcanic Eruptions (Figure 1, Right Panel)&lt;/strong&gt;:
When an explosive volcanic eruption occurs, the violent blast launches an infrasonic shockwave into the lower troposphere known as an acoustic Lamb wave [2]. Propagating horizontally at the local speed of sound (approximately 310 meters per second), this pressure pulse ripples across thousands of balcony barometers. By calculating multi-channel semblance coherence across the sensor mesh, random wind noise cancels out, and true volcanic wavefronts stand out with semblance values exceeding 0.80, allowing exact real-time tracking of the blast wave.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The data was there. The physics was there. But writing the exact mathematical algorithms to combine all of this into a unified predictive model—and a unified theory—without false alarms was where human craftsmanship hit a wall.&lt;/p&gt;


&lt;h2&gt;
  
  
  3. My Long Journey of Crafting Disaster Models by Hand
&lt;/h2&gt;

&lt;p&gt;Before turning to autonomous AI, I spent years tackling these hazards through manual theoretical physics and empirical derivation across four separate papers [6, 7, 8, 9].&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqvtvxjceyle0s9oe2tha.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqvtvxjceyle0s9oe2tha.png" alt="Figure 2: Research Objective, Historical Context, and Autonomous Inquiry Framework" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Figure 2 | Research Objective, Historical Context, and Autonomous Inquiry Framework.&lt;/strong&gt; Tracing the evolution from my four isolated manual frameworks (left) to closed-loop AI algorithm creation with AlphaEvolve and Gemini 3.8 Flash (center), culminating in a unified predictive model and physical laws (right).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  Understanding the Bottlenecks in Figure 2
&lt;/h3&gt;

&lt;p&gt;Figure 2 visualizes why manual model building struggled and why autonomous algorithm generation was needed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Left Panel (Manual Physics Frameworks in Isolated Silos)&lt;/strong&gt;:
In my earlier work, I manually crafted algorithms for four separate frameworks:

&lt;ol&gt;
&lt;li&gt;
&lt;em&gt;The Urban Heat Convection (UHC) Framework&lt;/em&gt; [6]: Using Bolton's thermodynamic formulation [1], I derived a scalar criterion combining the time rate of equivalent potential temperature change with a local pressure anomaly ratio to detect downbursts. However, its rigid step-function thresholds suffered from blind spots during highly humid, maritime frontal boundaries.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;The Localized Fluid Dynamics Framework&lt;/em&gt; [9]: A 3D helicity flux formulation and diagnostic Navier-Stokes solver nesting a 10-meter fluidic voxel inside a 1.0-kilometer grid. While precise, it operated as an isolated computational silo unable to interface with lithospheric or acoustic signals.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;The LAIC Earthquake Precursor Framework&lt;/em&gt; [7]: Formulated the thermodynamic coupling between pre-seismic radon emission, surface latent heat flux, and equivalent potential temperature anomalies. While it captured precursor thermal spikes before major Japanese earthquakes, it relied on a hand-tuned scalar threshold vulnerable to passing winter bomb cyclones, producing an operational false alarm rate of roughly 6.8%.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Volcanic Infrasonic Lamb Wave Moveout Analysis&lt;/em&gt; [8]: Tracked volcanic shockwaves using moveout regression, but atmospheric wind shear introduced scattering, leaving an azimuth uncertainty of 1.78 degrees.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Center Panel (AI Ingestion &amp;amp; Closed-Loop Algorithm Evolution)&lt;/strong&gt;:
As depicted in the center of Figure 2, breaking down these intellectual silos required handing over the task of &lt;strong&gt;algorithm creation&lt;/strong&gt; to an evolutionary AI system. By feeding 21 verified Japanese disasters and 175,363 authenticated Netatmo IoT records into Google DeepMind's AlphaEvolve and Gemini 3.8 Flash, the AI autonomously explored algorithmic structures under strict falsification penalties.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Right Panel (Autonomous Model &amp;amp; Theory Deliverables)&lt;/strong&gt;:
The AI autonomously writes the Python algorithm, which forms the unified multi-hazard predictive model, from which three continuous physical laws are formulated and verified on raw empirical data.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Why Human Hand-Tuning Fails When Hazards Collide
&lt;/h3&gt;

&lt;p&gt;When you try to manually write algorithms to combine four separate physical models on live atmospheric data, chaos ensues:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A passing winter bomb cyclone triggers an earthquake warning because of sudden pressure swings.&lt;/li&gt;
&lt;li&gt;A sudden squall line distorts an acoustic volcanic wave calculation.&lt;/li&gt;
&lt;li&gt;High ambient humidity causes a rigid rain threshold to miss an impending downburst.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The human mind, constrained by cognitive biases and habit, tends to write simple linear thresholds (&lt;code&gt;if x &amp;gt; 5.0&lt;/code&gt;). We struggle to invent the higher-dimensional, non-linear mathematical functions that cleanly separate all three hazards simultaneously. I needed an AI system that could &lt;strong&gt;autonomously create the algorithms&lt;/strong&gt; to build the model for me.&lt;/p&gt;


&lt;h2&gt;
  
  
  4. Enter AlphaEvolve: An AI That Autonomously Writes Algorithms to Build Models and Theories
&lt;/h2&gt;

&lt;p&gt;In 2024, Google DeepMind published FunSearch [5], demonstrating that large language models paired with evolutionary program search could discover new mathematical theorems and combinatorial algorithms beyond the reach of human mathematicians.&lt;/p&gt;

&lt;p&gt;Building upon this philosophy, Google DeepMind introduced &lt;strong&gt;AlphaEvolve&lt;/strong&gt;—an evolutionary coding agent powered by Gemini that autonomously writes, mutates, tests, and refines complex algorithmic codebases.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzk2cn798k98agpo7zfa1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzk2cn798k98agpo7zfa1.png" alt="Figure 3: AlphaEvolve Architecture, Mechanism, and Application Domains" width="800" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Figure 3 | AlphaEvolve Architecture, Mechanism, and Application Domains.&lt;/strong&gt; &lt;strong&gt;Left:&lt;/strong&gt; The 4-station cyclic engine: Seed population, Gemini 3.8 Flash AST mutator, 3-tier defensive evaluation gauntlet, and Pareto archive. &lt;strong&gt;Right:&lt;/strong&gt; Core scientific use cases, quantitative benchmark comparison against deep neural networks, and anti-reward hacking forensics.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  Why Autonomous Symbolic Algorithm Creation Beats Black-Box Neural Networks (Figure 3)
&lt;/h3&gt;

&lt;p&gt;A common question from developers is: &lt;em&gt;Why use AlphaEvolve to write Python algorithms when you could just train a Deep Neural Network (such as an LSTM or Transformer) on the sensor data?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;As shown in the benchmark comparison on the right side of Figure 3, the distinction is fundamental to scientific theory building:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Neural Networks Build Black-Box Weights, Not Theories&lt;/strong&gt;: A deep neural network adjusts millions of floating-point weights inside hidden layers. Even if it predicts a storm, you cannot read its weights to formulate a physical law of nature, nor can civil defense authorities trust an opaque black box that might hallucinate in unseen extreme weather.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AlphaEvolve Autonomously Writes Explicit Mathematical Algorithms&lt;/strong&gt;: Instead of training opaque weights, AlphaEvolve uses &lt;strong&gt;Gemini 3.8 Flash&lt;/strong&gt; to write human-readable Python equations. The output of AlphaEvolve is a clean, symbolic mathematical algorithm (&lt;code&gt;evolved_hazard_dispatcher.py&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;From Algorithm to Model to Theory&lt;/strong&gt;: Because the algorithm is explicit math, it immediately serves as a fast, deterministic &lt;strong&gt;predictive model&lt;/strong&gt; (running in under 1.5 milliseconds on a standard CPU) AND allows scientists to directly read the discovered equations to establish a &lt;strong&gt;physical theory&lt;/strong&gt;!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ftanaikech.github.io%2Fimage-storage%2F20261014a%2Finfographic_4_alphaevolve_physics_discovery.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ftanaikech.github.io%2Fimage-storage%2F20261014a%2Finfographic_4_alphaevolve_physics_discovery.png" alt="Figure 4: Inside the AI Discovery Engine - From Raw Code to Universal Equations" width="800" height="489"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Figure 4 | Inside the AI Discovery Engine: From Raw Code to Universal Equations.&lt;/strong&gt; Tracing how AlphaEvolve replaces fragile human heuristics (Column 1) via Gemini 3.8 Flash AST refactoring and the -5,000 point Popperian falsification crucible (Column 2) to autonomously synthesize the multi-hazard predictive model and physical laws (Column 3).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  Walking Through Figure 4: How Gemini Evolves the Algorithm
&lt;/h3&gt;

&lt;p&gt;Figure 4 illustrates the internal mechanics of how AlphaEvolve autonomously constructs the disaster prediction algorithm:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Column 1 (Human Heuristics &amp;amp; Bottlenecks)&lt;/strong&gt;: Starts with a rough, human-written seed program using rigid step-function thresholds (&lt;code&gt;if dtheta &amp;lt; -5.0&lt;/code&gt;), which suffers from missed storms in humid air and a 6.8% false alarm rate during winter cyclones.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Column 2 (Gemini 3.8 Flash &amp;amp; Popperian Gauntlet)&lt;/strong&gt;: Rather than flipping random bits like classical genetic algorithms, Gemini 3.8 Flash parses and rewrites the Python &lt;strong&gt;Abstract Syntax Tree (AST)&lt;/strong&gt;—replacing crude &lt;code&gt;ast.If&lt;/code&gt; cliffs with smooth &lt;code&gt;ast.Call&lt;/code&gt; continuous logistic manifolds and multi-variable orthogonality gates. Every candidate program is tested against 175,363 real observations, where a single false alarm triggers a merciless &lt;strong&gt;-5,000 point Popperian penalty&lt;/strong&gt;. Across 68 generations, fitness climbs from 212.40 to 399.85.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Column 3 (Autonomously Synthesized Physics Laws)&lt;/strong&gt;: The surviving champion algorithm automatically forms our unified multi-hazard prediction model and reveals three new physical principles.&lt;/li&gt;
&lt;/ol&gt;


&lt;h2&gt;
  
  
  5. How to Use AlphaEvolve: A Practical Guide to Autonomous Algorithm Creation
&lt;/h2&gt;

&lt;p&gt;How do you actually set up and run AlphaEvolve so that it autonomously creates algorithms to build a scientific model? Even if you have never used evolutionary code search before, the workflow boils down to three clean architectural steps.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ftanaikech.github.io%2Fimage-storage%2F20261014a%2Finfographic_alphaevolve_basic_usage.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ftanaikech.github.io%2Fimage-storage%2F20261014a%2Finfographic_alphaevolve_basic_usage.png" alt="Figure 5: How to Use AlphaEvolve - Autonomous Algorithm Creation Workflow" width="800" height="489"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Figure 5 | How to Use AlphaEvolve: Autonomous Algorithm Creation Workflow.&lt;/strong&gt; &lt;strong&gt;Column 1:&lt;/strong&gt; Defining the Python seed skeleton with &lt;code&gt;# EVOLVE-BLOCK&lt;/code&gt; markers. &lt;strong&gt;Column 2:&lt;/strong&gt; Running the closed-loop cycle between the deterministic evaluator harness and the Gemini 3.8 Flash semantic mutator. &lt;strong&gt;Column 3:&lt;/strong&gt; The 3-level autonomous elevation from AI-generated algorithm to predictive model to unified physical theory.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  Step-by-Step Walkthrough of Figure 5
&lt;/h3&gt;

&lt;p&gt;As illustrated in Figure 5, using AlphaEvolve requires setting up two files—a &lt;strong&gt;Seed Skeleton&lt;/strong&gt; and an &lt;strong&gt;Evaluator Harness&lt;/strong&gt;—and then letting the &lt;strong&gt;Gemini Closed-Loop Engine&lt;/strong&gt; do the heavy lifting:&lt;/p&gt;
&lt;h4&gt;
  
  
  Step 1: Define the Seed Skeleton with &lt;code&gt;# EVOLVE-BLOCK&lt;/code&gt; Markers (Figure 5, Column 1)
&lt;/h4&gt;

&lt;p&gt;To use AlphaEvolve, you do not ask the LLM to write an entire software application from scratch. Instead, you write a standard Python skeleton where the outer infrastructure (imports, data types, and function signatures) is locked, and the core mathematical algorithm you want the AI to discover is enclosed inside &lt;code&gt;# EVOLVE-BLOCK START&lt;/code&gt; and &lt;code&gt;# EVOLVE-BLOCK END&lt;/code&gt; comments:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;numpy&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;  &lt;span class="c1"&gt;# Locked outside the evolve block
&lt;/span&gt;
&lt;span class="c1"&gt;# &amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt; # EVOLVE-BLOCK START &amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;
&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;compute_rain_convective_score&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;theta_e_k&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dtheta_e_dt_k_h&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                 &lt;span class="n"&gt;pressure_drop_10min_hpa&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;gust_ratio&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;AlphaEvolve will autonomously rewrite the math inside this function!&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;z&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;0.15&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;dtheta_e_dt_k_h&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1.2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;pressure_drop_10min_hpa&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.8&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gust_ratio&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;theta_e_k&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;340.0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;z&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mf"&gt;1.5&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;10.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;10.0&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;evaluate_laic_and_gate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;delta_theta_e_k&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lpar&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                          &lt;span class="n"&gt;dp_dt_abs_hpa_h&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;regional_std_k&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;AlphaEvolve will autonomously discover how to reject storm false alarms!&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;delta_theta_e_k&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mf"&gt;8.0&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;lpar&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mf"&gt;2.5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# ... (additional hazard functions and dispatcher) ...
&lt;/span&gt;
&lt;span class="c1"&gt;# &amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt; # EVOLVE-BLOCK END &amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As highlighted in the bottom card of Figure 5 (Column 1), &lt;strong&gt;the developer's only job in Step 1&lt;/strong&gt; is to specify the input arguments (the physical observables) and the output return format. You do &lt;em&gt;not&lt;/em&gt; need to know the optimal mathematical formula or threshold weights—AlphaEvolve will invent those autonomously.&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 2: Connect the Evaluator Harness &amp;amp; Gemini Closed Loop (Figure 5, Column 2)
&lt;/h4&gt;

&lt;p&gt;Next, you write an evaluator class (&lt;code&gt;evaluator.evaluate(code_str)&lt;/code&gt;) that takes a candidate Python string generated by Gemini, executes it against your dataset, and returns two things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;A Scalar Fitness Score&lt;/strong&gt;: A single number measuring how well the candidate model performed (rewarding true detections and early lead time, while heavily penalizing false alarms).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Diagnostic Failure Insights&lt;/strong&gt;: Structured text feedback explaining &lt;em&gt;where&lt;/em&gt; and &lt;em&gt;why&lt;/em&gt; the candidate failed—for example, &lt;code&gt;[{"label": "FALSE_POSITIVE_EQ", "text": "Triggered false earthquake alert during winter storm baseline"}]&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Look at the cycle diagram in the center of Figure 5 (Column 2). Unlike traditional genetic algorithms that mutate code blindly at random, AlphaEvolve feeds both the parent's &lt;code&gt;EVOLVE-BLOCK&lt;/code&gt; code &lt;strong&gt;and&lt;/strong&gt; these diagnostic failure insights into &lt;strong&gt;Gemini 3.8 Flash&lt;/strong&gt; via the Google GenAI SDK (&lt;code&gt;google-genai&lt;/code&gt;). Gemini reads the diagnostics, reasons like a physicist about why the equation failed, and rewrites the Python AST to fix the flaw in the next generation!&lt;/p&gt;

&lt;p&gt;To launch the entire evolutionary campaign from your terminal, you simply set your Gemini API key and run the campaign script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;GEMINI_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"your_api_key_here"&lt;/span&gt;
python3 scripts/04_run_alpha_evolve_campaign.py &lt;span class="nt"&gt;--model&lt;/span&gt; gemini-3.8-flash &lt;span class="nt"&gt;--generations&lt;/span&gt; 100
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Step 3: The 3-Level Autonomous Elevation — Algorithm to Model to Theory (Figure 5, Column 3)
&lt;/h4&gt;

&lt;p&gt;Column 3 of Figure 5 illustrates why this paradigm is so transformative for science. As AlphaEvolve iterates through generations, it autonomously completes a three-level hierarchy:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Level 1 (Autonomous Algorithm)&lt;/strong&gt;: At Generation 68, AlphaEvolve outputs the champion Python algorithm (&lt;code&gt;evolved_hazard_dispatcher.py&lt;/code&gt;), having autonomously invented non-linear activation kernels and orthogonal stability gates.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Level 2 (Predictive Model)&lt;/strong&gt;: That exported Python algorithm immediately functions as our unified multi-hazard predictive model, capable of processing live sensor streams in under 1.5 milliseconds with 100% recall and 0.0% false alarms.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Level 3 (Unified Physical Theory)&lt;/strong&gt;: Because the predictive model is written in clean, symbolic Python math rather than hidden neural weights, we can directly read the AI's code and formalize three universal laws of geophysics!&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  6. How We Fed 175,363 Real Disaster Observations into AlphaEvolve
&lt;/h2&gt;

&lt;p&gt;A critical question for any developer or data scientist is: &lt;strong&gt;How do you take messy, real-world IoT sensor logs from 21 historical disasters and format them so AlphaEvolve can test thousands of algorithms in milliseconds without the AI cheating?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you ask an evolutionary AI to read raw CSV files or compute Fast Fourier Transforms (FFTs) from scratch on 175,363 rows during every single generation, each evaluation would take several minutes—and worse, a clever AI might try to "reward hack" by peeking at file timestamps or overwriting the data arrays in memory.&lt;/p&gt;

&lt;p&gt;To solve this, I engineered a five-script empirical pipeline (publicly available in our sample repository at &lt;a href="https://github.com/tanaikech/alphaevolve-multi-hazard" rel="noopener noreferrer"&gt;&lt;code&gt;https://github.com/tanaikech/alphaevolve-multi-hazard&lt;/code&gt;&lt;/a&gt;) that prepares, authenticates, pre-computes, and locks the experimental data before handing it to AlphaEvolve.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ftanaikech.github.io%2Fimage-storage%2F20261014a%2Finfographic_experimental_data_pipeline_usage.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ftanaikech.github.io%2Fimage-storage%2F20261014a%2Finfographic_experimental_data_pipeline_usage.png" alt="Figure 6: How Experimental Disaster Big Data Is Fed Into AlphaEvolve" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Figure 6 | How Experimental Disaster Big Data Is Fed Into AlphaEvolve.&lt;/strong&gt; The 5-Script Empirical Pipeline: Transforming 175,363 raw Netatmo IoT observations into hardware-authenticated, read-only NumPy feature tensors (&lt;code&gt;flags.writeable = False&lt;/code&gt;) for sub-millisecond evaluation across the 3-Tier Defensive Evaluator Harness.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Walking Through Figure 6: The 5-Script Data-to-Discovery Pipeline
&lt;/h3&gt;

&lt;p&gt;Figure 6 maps out the exact software engineering pipeline used in our study to bridge raw citizen weather data with AlphaEvolve:&lt;/p&gt;

&lt;h4&gt;
  
  
  Stage 1: Raw Data Extraction &amp;amp; Hardware Authentication (&lt;code&gt;01_fetch_multi_hazard_data.py&lt;/code&gt; — Figure 6, Column 1 Top)
&lt;/h4&gt;

&lt;p&gt;First, &lt;code&gt;01_fetch_multi_hazard_data.py&lt;/code&gt; extracts 175,363 crowdsourced Netatmo observations across &lt;strong&gt;23 distinct empirical catalogs&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;15 Extreme Convective Rainstorms&lt;/strong&gt; (such as the 2017 Nerima and Fuchu guerrilla downpours and Setagaya cloudbursts),&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;4 Major Inland Earthquakes&lt;/strong&gt; (2016 Kumamoto M7.0, 2018 Osaka M5.6, 2018 Hokkaido Iburi M6.6, and 2021 Fukushima M7.1 — totaling 71,107 records),&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;2 Explosive Volcanic Eruptions&lt;/strong&gt; (2018 Kusatsu-Shirane phreatic blast and 2018 Shinmoedake magmatic eruption),&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;2 Non-Disaster Control Baselines&lt;/strong&gt; (calm summer heatwave days and violent winter bomb cyclones).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To guarantee 100% authentic empirical data (Zero Dummy Data Policy), this script verifies that every station's hardware MAC address begins with the official Netatmo IEEE OUI prefix (&lt;code&gt;70:ee:50&lt;/code&gt;), filters out indoor sensor anomalies, and seals every CSV catalog with a cryptographic SHA-256 hash inside &lt;code&gt;data_manifest.json&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Stage 2: Immutable Physics Core &amp;amp; Read-Only Tensor Pre-Caching (&lt;code&gt;02_immutable_physics_core.py&lt;/code&gt; — Figure 6, Column 1 Middle &amp;amp; Bottom)
&lt;/h4&gt;

&lt;p&gt;Next, &lt;code&gt;02_immutable_physics_core.py&lt;/code&gt; transforms the raw meteorological readings (temperature in Celsius, relative humidity in percent, sea-level pressure in hectopascals, and wind speed in meters per second) into standardized first-principles physical feature matrices for each hazard category:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;For Rainstorms (&lt;code&gt;[N x 4]&lt;/code&gt; matrix)&lt;/strong&gt;: Computes exact Bolton (1980) equivalent potential temperature (&lt;code&gt;theta_e&lt;/code&gt;), its hourly time derivative (&lt;code&gt;dtheta_e/dt&lt;/code&gt;), 10-minute barometric surge (&lt;code&gt;dp_10min&lt;/code&gt;), and peak wind gust ratio (&lt;code&gt;gust_ratio&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;For Earthquakes (&lt;code&gt;[N x 4]&lt;/code&gt; matrix)&lt;/strong&gt;: Computes the two-stage spatial thermal residual (&lt;code&gt;delta_theta_e&lt;/code&gt;) relative to surrounding reference stations, the Local Precursor Anomaly Ratio (&lt;code&gt;LPAR&lt;/code&gt;), the absolute hourly pressure change rate (&lt;code&gt;|dP/dt|&lt;/code&gt;), and regional spatial standard deviation (&lt;code&gt;regional_std&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;For Volcanoes (acoustic dictionary)&lt;/strong&gt;: Applies a zero-phase Butterworth bandpass filter (0.0005 to 0.02 Hz) to isolate infrasonic pressure perturbations, performs Time-Difference-of-Arrival (TDOA) moveout regression to extract apparent sound speed (&lt;code&gt;c_app&lt;/code&gt;), and computes normalized multi-channel array semblance (&lt;code&gt;semblance&lt;/code&gt;).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;The Crucial Engineering Trick (Figure 6, Column 1 Bottom Badge)&lt;/strong&gt;:&lt;br&gt;&lt;br&gt;
Once &lt;code&gt;02_immutable_physics_core.py&lt;/code&gt; computes these NumPy feature tensors, it locks their memory buffers as strictly read-only before caching them to disk (&lt;code&gt;temp/processed_cache/*.pkl&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Lock NumPy tensor memory buffer so evolved AI code can NEVER modify empirical data
&lt;/span&gt;&lt;span class="n"&gt;feature_matrix&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;flags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;writeable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This single architectural decision accomplishes two vital goals:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;1,000x Speedup&lt;/strong&gt;: Because heavy trigonometric, logarithmic, and FFT signal processing is pre-cached once in Stage 2, AlphaEvolve can stream all 175,363 records through a candidate algorithm in &lt;strong&gt;under 1.5 milliseconds&lt;/strong&gt; in RAM!&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bulletproof Data Integrity&lt;/strong&gt;: Even if a mutated candidate tries to cheat by modifying input arrays in place, NumPy immediately raises a &lt;code&gt;ValueError: assignment destination is read-only&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Stage 3: The 3-Tier Defensive Evaluator Harness (&lt;code&gt;03_alpha_evolve_dispatcher_evaluator.py&lt;/code&gt; — Figure 6, Column 2)
&lt;/h4&gt;

&lt;p&gt;As shown in Column 2 of Figure 6, &lt;code&gt;03_alpha_evolve_dispatcher_evaluator.py&lt;/code&gt; loads the 23 read-only feature tensors into memory and tests every AI-generated candidate across three strict defensive tiers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tier 1 (AST Security &amp;amp; Anti-Cheat Scan)&lt;/strong&gt;: Uses &lt;code&gt;ast.NodeVisitor&lt;/code&gt; to inspect the candidate's syntax tree before execution. If the code imports &lt;code&gt;random&lt;/code&gt;, &lt;code&gt;os&lt;/code&gt;, &lt;code&gt;sys&lt;/code&gt;, &lt;code&gt;eval&lt;/code&gt;, or contains hardcoded strings of historical disaster dates (like &lt;code&gt;"2018-01-23"&lt;/code&gt;), it is instantly disqualified with a score of minus one trillion.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tier 2 (Physics Contract &amp;amp; Double-Run Determinism)&lt;/strong&gt;: Executes the candidate's four functions twice on identical input vectors. If the outputs differ by more than 10 to the minus 12th power (proving hidden state or randomness) or fall outside physical probability bounds between 0.0 and 1.0, it is rejected with a score of minus one million.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tier 3 (Multi-Hazard Empirical Benchmark &amp;amp; Popperian Penalty)&lt;/strong&gt;: Streams all 23 cached disaster and control tensors through the candidate algorithm. It sums the True Positive Rates (TPR) and warning lead-time bonuses across the 21 disasters, but subtracts &lt;strong&gt;5,000 points&lt;/strong&gt; for every single false alarm triggered during the 2 control baseline catalogs. Whenever a disaster is missed or a false alarm occurs, Tier 3 appends a diagnostic JSON insight to guide Gemini's next mutation.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Stage 4 &amp;amp; Stage 5: Campaign Execution &amp;amp; Standalone Verification (&lt;code&gt;04&lt;/code&gt; &amp;amp; &lt;code&gt;05&lt;/code&gt; — Figure 6, Column 3)
&lt;/h4&gt;

&lt;p&gt;Finally, as shown in Column 3 of Figure 6:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;04_run_alpha_evolve_campaign.py&lt;/code&gt;&lt;/strong&gt; runs the evolutionary loop, feeding parent code and Tier 3 diagnostic insights into Gemini 3.8 Flash to evolve the four core functions (&lt;code&gt;compute_rain_convective_score&lt;/code&gt;, &lt;code&gt;evaluate_laic_and_gate&lt;/code&gt;, &lt;code&gt;compute_volcanic_semblance_threshold&lt;/code&gt;, and &lt;code&gt;dispatch_hazard_decision&lt;/code&gt;), exporting the winning algorithm to &lt;code&gt;scripts/evolved_hazard_dispatcher.py&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;05_predict_multi_hazard.py&lt;/code&gt;&lt;/strong&gt; imports that exported champion algorithm and runs an independent, end-to-end verification pass across all 175,363 records, generating the cryptographic reproducibility certificate.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgkcc7ulxxs8rsfubgsbl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgkcc7ulxxs8rsfubgsbl.png" alt="Figure 7: The 4-Stage Experimental Methodology and Closed-Loop Research Pipeline" width="800" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Figure 7 | The 4-Stage Experimental Methodology and Closed-Loop Research Pipeline.&lt;/strong&gt; Visualizing the complete scientific workflow from IEEE OUI hardware-authenticated data ingestion (Stage 1), multi-generation AST optimization (Stage 2), continuum theory elevation (Stage 3), to independent deterministic verification (Stage 4).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Connecting the Software Pipeline to the Scientific Methodology (Figure 7)
&lt;/h3&gt;

&lt;p&gt;Figure 7 summarizes how our five-script software pipeline maps directly onto the four macro-stages of autonomous scientific discovery:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Stage 1 (Top-Left)&lt;/strong&gt;: Hardware-authenticated ingestion of 175,363 Netatmo observations (&lt;code&gt;Scripts 01 &amp;amp; 02&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stage 2 (Top-Right)&lt;/strong&gt;: Multi-generation AST evolution under the -5,000 point Popperian falsification gauntlet (&lt;code&gt;Scripts 03 &amp;amp; 04&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stage 3 (Bottom-Left)&lt;/strong&gt;: Deductive theory elevation—translating the autonomously created Python algorithm into continuum differential equations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stage 4 (Bottom-Right)&lt;/strong&gt;: Independent zero-false-alarm empirical verification (&lt;code&gt;Script 05&lt;/code&gt;).&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  7. From Autonomously Created Algorithm to Predictive Model and Unified Theory
&lt;/h2&gt;

&lt;p&gt;At Generation 68, the champion Python algorithm autonomously created by AlphaEvolve and Gemini 3.8 Flash emerged not as bloated spaghetti code, but as twelve lines of transparent mathematical harmony:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# =========================================================================
# CHAMPION PYTHON ALGORITHM AUTONOMOUSLY CREATED BY ALPHAEVOLVE (GEN 68)
#
# Input telemetry schema (from pre-cached read-only feature tensors):
#   dtheta_e_dt    : Equivalent potential temp time derivative [K/h]
#   dp_10min       : 10-minute micro-barometric surge [hPa/10min]
#   gust_ratio     : Peak gust ratio u_gust / u_mean [-]
#   theta_e        : Ambient surface equivalent potential temp [K]
#   dp_dt_hpa_h    : Hourly barometric pressure derivative [hPa/h]
#   delta_theta_e  : Thermal anomaly relative to background [K]
#   c_app          : Apparent wave propagation speed [m/s]
#   semblance      : Normalized multi-channel coherence semblance [-]
# =========================================================================
&lt;/span&gt;
&lt;span class="c1"&gt;# 1. Continuous Logistic Torrential Rain Kernel
&lt;/span&gt;&lt;span class="n"&gt;z&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.35&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;dtheta_e_dt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
     &lt;span class="mf"&gt;1.50&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dp_10min&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
     &lt;span class="mf"&gt;1.20&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;gust_ratio&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mf"&gt;1.5&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
     &lt;span class="mf"&gt;1.50&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;theta_e&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mf"&gt;350.0&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;rain_score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;10.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;10.0&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;

&lt;span class="c1"&gt;# 2. Dynamic Isobaric Pre-Seismic (Earthquake) Gate
&lt;/span&gt;&lt;span class="n"&gt;isobaric_stable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dp_dt_hpa_h&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mf"&gt;2.0&lt;/span&gt;
&lt;span class="n"&gt;thermal_floor&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;theta_e&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mf"&gt;332.0&lt;/span&gt;
&lt;span class="n"&gt;eq_gate_open&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;isobaric_stable&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;thermal_floor&lt;/span&gt;
&lt;span class="n"&gt;eq_score&lt;/span&gt;        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;delta_theta_e&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mf"&gt;50.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# 3. Multi-Hazard Kinematic Priority Dispatcher
&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="mf"&gt;300.0&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;c_app&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mf"&gt;330.0&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;semblance&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.80&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;alert&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;VOLCANO_LAMB_WAVE&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;conf&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;semblance&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;rain_score&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.70&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;alert&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;CONVECTIVE_TORRENTIAL_RAIN&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;conf&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rain_score&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;eq_gate_open&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;delta_theta_e&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mf"&gt;30.0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;alert&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;EARTHQUAKE_LAIC_PRECURSOR&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;conf&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;eq_score&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;alert&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;NORMAL_ATMOSPHERE&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;conf&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbj79oncgfjyjg1mvalvi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbj79oncgfjyjg1mvalvi.png" alt="Figure 8: Discovered Symbolic Algorithm and Elevated Unified Geophysical Theory" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Figure 8 | Discovered Symbolic Algorithm and Elevated Unified Geophysical Theory.&lt;/strong&gt; &lt;strong&gt;Part A (Left):&lt;/strong&gt; The champion Python algorithm autonomously synthesized by AlphaEvolve. &lt;strong&gt;Theory Elevation Bridge (Center):&lt;/strong&gt; Deductive mathematical translation of the AI's predictive model into continuum field equations. &lt;strong&gt;Part B (Right):&lt;/strong&gt; The three formulated universal physical laws: Continuous Enthalpy Potential Theory, Kinematic-Barometric Orthogonality, and Emergent Dynamic Semblance Inversion.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  How the AI's Algorithm Built the Model and Revealed Three Physical Laws (Figure 8)
&lt;/h3&gt;

&lt;p&gt;Look closely at the three-part progression in Figure 8. Because AlphaEvolve autonomously wrote explicit symbolic Python rules (Figure 8, Part A) to construct our multi-hazard predictive model, we can pass those rules across the &lt;strong&gt;Theory Elevation Bridge&lt;/strong&gt; (Figure 8, Center) to formulate a unified geophysical theory consisting of four continuum principles (Figure 8, Part B):&lt;/p&gt;

&lt;h4&gt;
  
  
  Principle 1: Continuous Enthalpy Potential Theory (Rainstorms)
&lt;/h4&gt;

&lt;p&gt;In my earlier manual work, I used rigid step-function thresholds that failed when humid maritime air masses triggered downpours without a sharp initial temperature cliff. AlphaEvolve autonomously discarded step functions and constructed a &lt;strong&gt;continuous logistic manifold&lt;/strong&gt;. The probability of convective initiation is modeled by a smooth logistic sigmoid function taking the weighted sum of three physical terms:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Equivalent potential temperature flux convergence (weighted by 0.35, capturing downdraft evaporative cooling),&lt;/li&gt;
&lt;li&gt;Micro-barometric surge rate (weighted by 1.50, capturing cold-pool pressure domes),&lt;/li&gt;
&lt;li&gt;Excess wind gust ratio (weighted by 1.20, capturing surface shear turbulence),
minus a dynamic potential energy barrier.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Crucially, the AI discovered on its own that this potential energy barrier should equal 1.5 under normal conditions, but &lt;strong&gt;drop by 1.5 down to zero whenever ambient equivalent potential temperature reaches or exceeds 350.0 Kelvin&lt;/strong&gt;. Physically, when the urban boundary layer is already saturated with extreme moist static energy (350 Kelvin or higher), the activation energy required to trigger convective collapse vanishes! This allowed the model to sense impending downbursts during the pre-condensation phase, long before raindrops form aloft.&lt;/p&gt;

&lt;h4&gt;
  
  
  Principle 2: Kinematic-Barometric Orthogonality (Earthquakes)
&lt;/h4&gt;

&lt;p&gt;To eliminate the 6.8% storm false-alarm rate that plagued my earlier earthquake precursor model [7], AlphaEvolve autonomously discovered a &lt;strong&gt;Kinematic-Barometric Orthogonality Gate&lt;/strong&gt;. Before a spatial thermal anomaly (delta theta_e of 30 Kelvin or greater) is allowed to trigger an earthquake alert, the algorithm enforces two simultaneous physical gates:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Isobaric Calm (&lt;code&gt;abs(dp_dt_hpa_h) &amp;lt;= 2.0&lt;/code&gt;)&lt;/strong&gt;: The hourly barometric pressure change must remain within 2.0 hectopascals per hour. Because genuine pre-seismic LAIC heating originates from tectonic stress and radon ionization deep underground, it manifests under calm hydrostatic equilibrium. Passing synoptic storms and winter bomb cyclones always involve strong baroclinic pressure drops (exceeding 2.0 hectopascals per hour) and are therefore mathematically annihilated!&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Thermodynamic Floor (&lt;code&gt;theta_e &amp;gt;= 332.0&lt;/code&gt;)&lt;/strong&gt;: The ambient equivalent potential temperature must be at least 332.0 Kelvin, ensuring sufficient atmospheric water vapor is present to release latent heat via radon ion-induced nucleation, eliminating cold-weather sensor noise.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Principle 3: Emergent Dynamic Semblance Inversion (Volcanoes)
&lt;/h4&gt;

&lt;p&gt;For explosive volcanic eruptions, the AI synthesized a joint acoustic filter requiring apparent horizontal propagation speeds between 300.0 and 330.0 meters per second alongside multi-channel array semblance coherence of at least 0.80. Because tropospheric Lamb waves propagate at the speed of sound (approximately 310 meters per second), true volcanic shockwaves interfere constructively across hundreds of balcony barometers, while local wind gusts cancel out asymptotically.&lt;/p&gt;

&lt;h4&gt;
  
  
  Principle 4: The Kinematic Wave-Speed Priority Dispatcher
&lt;/h4&gt;

&lt;p&gt;Finally, how did the AI's algorithm prevent alert collisions when multiple signals occur at once? Look at lines 191 to 197 of the discovered code: AlphaEvolve autonomously ordered the &lt;code&gt;if / elif&lt;/code&gt; dispatch hierarchy strictly by &lt;strong&gt;physical wave propagation velocity&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Volcanic Acoustic Shockwaves&lt;/strong&gt; (traveling at the speed of sound, roughly 310 meters per second) are evaluated first.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Convective Rainstorm Advection&lt;/strong&gt; (traveling with storm outflows at 10 to 20 meters per second) is evaluated second.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Quasi-Static Tectonic Thermal Diffusion&lt;/strong&gt; (stationary fault-zone heating approaching 0 meters per second) is evaluated third.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Without being taught geophysical wave mechanics, AlphaEvolve discovered that sorting hazards by propagation speed completely eliminates cross-hazard interference!&lt;/p&gt;




&lt;h2&gt;
  
  
  8. The Proof: Testing the Autonomously Created Model Across 21 Historical Disasters
&lt;/h2&gt;

&lt;p&gt;A scientific theory is only as good as its empirical predictions. Using &lt;code&gt;05_predict_multi_hazard.py&lt;/code&gt;, we tested the predictive model built by AlphaEvolve's champion algorithm across all 23 historical catalogs (175,363 authentic Netatmo observations).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ftanaikech.github.io%2Fimage-storage%2F20261014a%2Finfographic_7_early_warning_timeline_scorecard.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ftanaikech.github.io%2Fimage-storage%2F20261014a%2Finfographic_7_early_warning_timeline_scorecard.png" alt="Figure 9: Early Warning Paradigm Shift - Performance &amp;amp; Timeline Scorecard" width="800" height="489"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Figure 9 | Early Warning Paradigm Shift: Performance &amp;amp; Timeline Scorecard.&lt;/strong&gt; &lt;strong&gt;Column 1:&lt;/strong&gt; Timeline comparison for the 2017 Nerima downpour showing a 34.2-minute lead time (+84.9% longer than manual models). &lt;strong&gt;Column 2:&lt;/strong&gt; Quantitative performance scorecard across all 21 historical disasters (100% sensitivity). &lt;strong&gt;Column 3:&lt;/strong&gt; Control baseline verification confirming 0.0% false alarms and cryptographic SHA-256 data provenance.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fw49kimjds5xreteu4zzk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fw49kimjds5xreteu4zzk.png" alt="Figure 10: Experimental Verification Results Across 23 Catalogs" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Figure 10 | Empirical Verification Results Across 23 Catalogs.&lt;/strong&gt; &lt;strong&gt;(a)&lt;/strong&gt; Cold pool thermodynamic collapse during the 2017 Nerima rainstorm, achieving 34.2 min lead time. &lt;strong&gt;(b)&lt;/strong&gt; Pre-seismic thermal anomaly 6.0 hours before the 2018 Osaka M5.6 earthquake under perfect isobaric calm. &lt;strong&gt;(c)&lt;/strong&gt; Volcanic Lamb wave linear moveout across 598 Netatmo stations during the 2018 Kusatsu-Shirane eruption (apparent speed 310.64 m/s, R-squared 0.985). &lt;strong&gt;Lower panels:&lt;/strong&gt; 100% sensitivity table, normalized confusion matrix showing 0.0% false alarms, and cryptographic SHA-256 verification.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Dissecting the Empirical Scorecard (Figure 9 &amp;amp; Figure 10)
&lt;/h3&gt;

&lt;p&gt;The quantitative verification results shown in Figure 9 and Figure 10 confirm the power of the AI-constructed predictive model:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Convective Torrential Rainstorms (15 of 15 Detected — 100% Sensitivity)&lt;/strong&gt;:
As shown in Column 1 of Figure 9 and the time-series plot in Figure 10(a), during the ferocious 2017 Nerima guerrilla rainstorm in Tokyo, the evolved model triggered a high-confidence alert &lt;strong&gt;34.2 minutes&lt;/strong&gt; before peak torrential rainfall. Compared to my manual UHC model (18.5 minutes) and official weather radar (which only detects precipitation 5 minutes prior once heavy droplets have already formed aloft), AlphaEvolve extended the life-saving evacuation window by &lt;strong&gt;+84.9% (+15.7 extra minutes)&lt;/strong&gt;. Across all 15 rainstorm catalogs, average lead time rose from 19.6 minutes to 36.3 minutes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Major Inland Earthquakes (4 of 4 Detected — 100% Sensitivity)&lt;/strong&gt;:
As shown in Figure 10(b), prior to the destructive 2018 Osaka-North Earthquake (magnitude 5.6), the dynamic isobaric gate detected an intense 38-Kelvin equivalent potential temperature anomaly &lt;strong&gt;6.0 hours before the mainshock&lt;/strong&gt;. Throughout that window, barometric pressure variation remained below 0.5 hectopascals per hour, confirming lithospheric LAIC origin. All four major earthquakes (2016 Kumamoto, 2018 Osaka, 2018 Hokkaido Iburi, and 2021 Fukushima) were detected with 100% sensitivity.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Explosive Volcanic Eruptions (2 of 2 Detected — 100% Sensitivity)&lt;/strong&gt;:
During the 2018 Kusatsu-Shirane eruption (Figure 10c), 598 Netatmo stations captured the expanding acoustic Lamb wave at an empirical speed of 310.64 meters per second (R-squared of 0.985). Array semblance coherence reduced crater back-azimuth localization error from 1.78 degrees down to &lt;strong&gt;0.33 degrees&lt;/strong&gt;—an &lt;strong&gt;81.5% improvement in directional precision&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Control Baseline Periods (0 False Alarms — 0.0% False Positive Rate)&lt;/strong&gt;:
As shown in Column 3 of Figure 9 and the confusion matrix in Figure 10 (bottom center), when tested against calm summer heatwaves and violent winter bomb cyclones, the model produced &lt;strong&gt;zero false alarms (0.0% FPR)&lt;/strong&gt;—completely eradicating the 6.8% false-alarm rate of my earlier manual earthquake model.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  9. A Paradigm Shift in Science: Human Craftsmanship vs. Autonomous AI Closed-Loop
&lt;/h2&gt;

&lt;p&gt;Stepping back from the numbers, this experiment demonstrates a profound transformation in how humanity can build scientific models and theories.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4wc6fin64xdtqqqtd5kw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4wc6fin64xdtqqqtd5kw.png" alt="Figure 11: Four-Domain Deep Comparison - Prior Manual Frameworks vs. AlphaEvolve Theory" width="800" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Figure 11 | Four-Domain Deep Comparison: Prior Manual Frameworks vs. AlphaEvolve Unified Theory.&lt;/strong&gt; Visualizing the transition from fragile human-written step heuristics to AI-discovered continuous manifolds across rainstorms (Row 1), earthquakes (Row 2), volcanic eruptions (Row 3), and operational system architectures (Row 4).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Comparing Human Algorithms vs. AI-Created Algorithms (Figure 11)
&lt;/h3&gt;

&lt;p&gt;Figure 11 contrasts my four years of manual model building against the unified algorithm autonomously created by AlphaEvolve:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Row 1 (Rainstorms)&lt;/strong&gt;: Human-written step thresholds missed early moist destabilization (18.5 min lead time). AlphaEvolve autonomously created a continuous logistic enthalpy kernel, expanding lead time to 34.2 min (+84.9%).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Row 2 (Earthquakes)&lt;/strong&gt;: Human scalar thresholds suffered a 6.8% false-alarm rate during storms. AlphaEvolve invented the kinematic-barometric orthogonality gate (&lt;code&gt;|dP/dt| &amp;lt;= 2.0&lt;/code&gt;), achieving a 0.0% false-alarm rate.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Row 3 (Volcanoes)&lt;/strong&gt;: Single-station moveout suffered from wind scattering (1.78-degree error). AlphaEvolve integrated multi-channel semblance coherence, sharpening crater localization to 0.33 degrees (-81.5% error).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Row 4 (System Architecture)&lt;/strong&gt;: Four conflicting, slow manual scripts were unified into a single sub-millisecond wave-speed dispatcher with zero alert collisions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnpt66mzucumfsq9scrza.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnpt66mzucumfsq9scrza.png" alt="Figure 12: Scientific Workflow Paradigm Shift - Manual Pipeline vs. AI Closed-Loop" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Figure 12 | Scientific Workflow Paradigm Shift: Traditional Manual Pipeline vs. Autonomous AI Closed-Loop.&lt;/strong&gt; &lt;strong&gt;Top (Amber):&lt;/strong&gt; Months of manual data collection, intuition-based formula drafting, spreadsheet slider tuning, and isolated silo deployment. &lt;strong&gt;Bottom (Neon Green):&lt;/strong&gt; Automated IoT tensor caching, sub-second Gemini 3.8 Flash AST algorithm creation, ruthless Popperian falsification, and unified theory synthesis.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Deconstructing the Workflow Revolution (Figure 12)
&lt;/h3&gt;

&lt;p&gt;Look at the contrast between the two workflows in Figure 12:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Traditional Human-Centric Pipeline (Figure 12, Top Row in Amber)&lt;/strong&gt;:
For centuries, a scientist had to manually gather fragmented datasets, guess mathematical formulas by intuition, spend months adjusting threshold sliders in spreadsheets, and publish isolated models that broke down when phenomena overlapped.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Autonomous AlphaEvolve Closed Loop (Figure 12, Bottom Row in Neon Green)&lt;/strong&gt;:
In this new paradigm, the human researcher defines the physical observables, locks the empirical data into read-only tensors, and sets the Popperian falsification rules (-5,000 points per false alarm). Then, &lt;strong&gt;AlphaEvolve and Gemini autonomously write and evolve the algorithms&lt;/strong&gt; to construct the predictive model. Finally, the human scientist reads the winning white-box code to formalize a unified physical theory.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  10. The Future of AlphaEvolve and Autonomous Theory Construction
&lt;/h2&gt;

&lt;p&gt;We are standing at the threshold of a new Renaissance in empirical science.&lt;/p&gt;

&lt;p&gt;By proving that AlphaEvolve can take 175,363 noisy citizen weather observations and &lt;strong&gt;autonomously create the algorithms that build the predictive model and establish a unified multi-hazard disaster theory&lt;/strong&gt;, we have unlocked a blueprint that extends far beyond meteorology and seismology:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Climate Science &amp;amp; Sub-Grid Physics&lt;/strong&gt;: Autonomously discovering symbolic cloud microphysics and ocean turbulence closures from satellite telemetry.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clean Energy &amp;amp; Materials Science&lt;/strong&gt;: Evolving explicit mathematical transport models for solid-state battery electrolytes and photovoltaic stability.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fusion Energy&lt;/strong&gt;: Discovering ultra-fast, sub-millisecond white-box control algorithms for magnetic plasma confinement in tokamak reactors.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Biomedicine&lt;/strong&gt;: Synthesizing interpretable mathematical models of cellular signaling pathways from multi-omics datasets.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  A Final Message to Developers, Scientists, and Dreamers
&lt;/h3&gt;

&lt;p&gt;To my fellow developers and researchers: AI is not here to replace the human scientist.&lt;/p&gt;

&lt;p&gt;AlphaEvolve did not replace me; it liberated me. It took the exhausting, error-prone burden of manual algorithm writing, parameter tuning, and trial-and-error off my shoulders, allowing me to focus on physical architecture, data integrity, and theoretical synthesis.&lt;/p&gt;

&lt;p&gt;Nature has spent billions of years weaving intricate physical laws across the Earth and the cosmos. Today, for the first time in history, we have an intelligent partner capable of listening to the digital whispers of thousands of balcony sensors, autonomously writing the mathematical algorithms to model them, and helping us discover the laws of nature to protect human lives.&lt;/p&gt;

&lt;h3&gt;
  
  
  Read the Full Open-Access Paper &amp;amp; Explore the Sample Scripts
&lt;/h3&gt;

&lt;p&gt;For the complete mathematical derivations, the 4-stage AlphaEvolve discovery architecture, the full empirical verification results across all 23 historical catalogs, and the sample Python scripts, please visit our open-access paper on Zenodo [10] and our GitHub repository:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Paper Title&lt;/strong&gt;: &lt;em&gt;Autonomous Discovery of Unified Multi-Hazard Prediction Theory from Netatmo IoT Weather Big Data Using AlphaEvolve&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Author&lt;/strong&gt;: Kanshi Tanaike&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zenodo Open-Access URL&lt;/strong&gt;: &lt;a href="https://zenodo.org/records/23354598" rel="noopener noreferrer"&gt;https://zenodo.org/records/23354598&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sample Scripts Repository (GitHub)&lt;/strong&gt;: &lt;a href="https://github.com/tanaikech/alphaevolve-multi-hazard" rel="noopener noreferrer"&gt;https://github.com/tanaikech/alphaevolve-multi-hazard&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;p&gt;[1] Bolton, D. (1980). The computation of equivalent potential temperature. &lt;em&gt;Monthly Weather Review&lt;/em&gt;, 108(7), 1046–1053.&lt;br&gt;&lt;br&gt;
[2] Matoza, R. S., et al. (2022). Atmospheric waves and global seismoacoustic observations of the January 2022 Hunga eruption, Tonga. &lt;em&gt;Science&lt;/em&gt;, 377(6601), 95–100.&lt;br&gt;&lt;br&gt;
[3] Napoly, A., et al. (2018). Development and application of a crowdsourced weather station quality control tool: Netatmo station dataset in Berlin. &lt;em&gt;Frontiers in Earth Science&lt;/em&gt;, 6, 118.&lt;br&gt;&lt;br&gt;
[4] Pulinets, S., &amp;amp; Ouzounov, D. (2011). Lithosphere-Atmosphere-Ionosphere Coupling (LAIC) model: An unified concept for earthquake precursors validation. &lt;em&gt;Journal of Asian Earth Sciences&lt;/em&gt;, 41(4–5), 371–382.&lt;br&gt;&lt;br&gt;
[5] Romera-Paredes, B., et al. (2024). Mathematical discoveries from program search with large language models. &lt;em&gt;Nature&lt;/em&gt;, 625(7995), 468–475.&lt;br&gt;&lt;br&gt;
[6] Tanaike, K. (2026a). Early Detection of Guerrilla Rainstorms from Crowdsourced IoT Weather Networks: The Urban Heat Convection (UHC) Thermodynamic Framework. &lt;em&gt;ESS Open Archive&lt;/em&gt;.&lt;br&gt;&lt;br&gt;
[7] Tanaike, K. (2026b). Direct Pre-Seismic Precursor Detection from Crowdsourced IoT Atmospheric Sensor Networks: Theoretical Unification with Lithosphere-Atmosphere-Ionosphere Coupling (LAIC). &lt;em&gt;ESS Open Archive&lt;/em&gt;.&lt;br&gt;&lt;br&gt;
[8] Tanaike, K. (2026c). Volcanic Explosion and Infrasonic Lamb Wave Moveout Analysis via Dense Crowdsourced IoT Barometric Sensor Networks. &lt;em&gt;ESS Open Archive&lt;/em&gt;.&lt;br&gt;&lt;br&gt;
[9] Tanaike, K. (2026d). Micro-Scale Convective Rain Prediction via High-Density IoT Weather Sensor Networks and Localized Fluid Dynamics. &lt;em&gt;ESS Open Archive&lt;/em&gt;.&lt;br&gt;&lt;br&gt;
[10] Tanaike, K. (2026e). Autonomous Discovery of Unified Multi-Hazard Prediction Theory from Netatmo IoT Weather Big Data Using AlphaEvolve. &lt;em&gt;Zenodo&lt;/em&gt;. &lt;a href="https://zenodo.org/records/23354598" rel="noopener noreferrer"&gt;https://zenodo.org/records/23354598&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>gemini</category>
      <category>iot</category>
      <category>algorithms</category>
    </item>
    <item>
      <title>Taking Advantage of Cloud Run Sandboxes with Google Apps Script for Google Workspace</title>
      <dc:creator>Tanaike</dc:creator>
      <pubDate>Thu, 10 Sep 2026 02:43:19 +0000</pubDate>
      <link>https://dev.to/gde/taking-advantage-of-cloud-run-sandboxes-with-google-apps-script-for-google-workspace-5fc5</link>
      <guid>https://dev.to/gde/taking-advantage-of-cloud-run-sandboxes-with-google-apps-script-for-google-workspace-5fc5</guid>
      <description>&lt;h2&gt;
  
  
  Deterministic Sub-Second Python and Bash Execution, Zero-Trust gVisor Isolation, and Zero Idle Cost
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fidnxkdczqlsvky2h51s7.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fidnxkdczqlsvky2h51s7.jpg" alt="top image" width="800" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Abstract
&lt;/h2&gt;

&lt;p&gt;While secure sandboxes are pivotal for running Generative AI-generated code safely, connecting Google Cloud Run Sandboxes (gVisor) directly to Google Apps Script unlocks a vastly broader horizon. Beyond executing AI-drafted scripts on the fly, this complementary architecture empowers Google Workspace with deterministic Python data science (Pandas, Seaborn) and Bash execution in 200–450 ms. With zero-trust micro-isolation, zero-token data ingestion, and zero idle cost, it elevates Workspace automations far beyond standard V8 runtime constraints.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Google Apps Script (GAS) &lt;a href="https://developers.google.com/apps-script" rel="noopener noreferrer"&gt;Ref&lt;/a&gt; is a cornerstone of Google Workspace automation across Sheets, Docs, Forms, and Drive. Yet, developers often hit hard limits. Standard accounts enforce a strict 6-minute timeout. The environment runs only JavaScript on V8, precluding native Linux binaries or external compilers. When an unhandled exception occurs, the entire script halts abruptly.&lt;/p&gt;

&lt;p&gt;Recently, in my article "Taking Advantage of Gemini Managed Agents with Google Apps Script" &lt;a href="https://medium.com/google-cloud/taking-advantage-of-gemini-managed-agents-with-google-apps-script-19215ab8c61f" rel="noopener noreferrer"&gt;Ref&lt;/a&gt;, I showed how to break past these limits by connecting Apps Script to a persistent Linux sandbox provisioned by Gemini Managed Agents &lt;a href="https://ai.google.dev/gemini-api/docs/agents" rel="noopener noreferrer"&gt;Ref&lt;/a&gt;. Using my Go CLI tool &lt;code&gt;ggsrun&lt;/code&gt; &lt;a href="https://github.com/tanaikech/ggsrun" rel="noopener noreferrer"&gt;Ref&lt;/a&gt; for direct streaming between the sandbox and Google Drive, that architecture handles heavy, multi-turn agentic workflows. Examples include Playwright scraping across multiple viewports and audio transcoding with FFmpeg.&lt;/p&gt;

&lt;p&gt;Gemini Managed Agents excel at autonomous, multi-step reasoning. However, they rely on LLM prompts via the Interactions API. This adds conversational inference overhead, pushing response latencies to several seconds or tens of seconds while burning token quotas (such as 200k TPM) &lt;a href="https://ai.google.dev/gemini-api/docs/rate-limits" rel="noopener noreferrer"&gt;Ref&lt;/a&gt;. Many everyday Workspace automations do not need an LLM. Tasks like mathematical evaluations, string parsing, regular expression matching, and shell commands require deterministic, instant execution without prompt ambiguity or token limits.&lt;/p&gt;

&lt;p&gt;The breakthrough moment came when I encountered Romin Irani's masterfully crafted and inspiring article, "Safely Running Untrusted Code: A Hands-On Guide to Google Cloud Run Sandboxes" &lt;a href="https://medium.com/google-cloud/safely-running-untrusted-code-a-hands-on-guide-to-google-cloud-run-sandboxes-8bbc95d391c7" rel="noopener noreferrer"&gt;Ref&lt;/a&gt;. In his exceptional guide, Irani brilliantly illuminated how Google Cloud Run Sandboxes leverage gVisor application kernel technology to deliver lightweight, ephemeral micro-isolation for arbitrary code execution with remarkable simplicity and elegance. Reading his hands-on exploration sparked an immediate insight: &lt;em&gt;What if we connect this powerful sandbox directly to Google Apps Script? Could this be the key to supercharging Google Workspace automations with instant, secure dynamic execution?&lt;/em&gt; That spark inspired me to plan, design, and thoroughly refine the project presented here.&lt;/p&gt;

&lt;p&gt;Fundamentally, secure sandboxes have become an indispensable cornerstone in the era of Generative AI. When large language models like Gemini generate code on the fly, they produce untrusted scripts that demand strict execution isolation to shield host environments from unintended side effects, resource exhaustion, or security compromises. I myself have continuously explored and proposed sandboxing approaches for Google Apps Script to safely execute AI-generated code &lt;a href="https://medium.com/google-cloud/a-fake-sandbox-for-google-apps-script-a-feasibility-study-on-securely-executing-code-generated-by-cc985ce5dae3" rel="noopener noreferrer"&gt;Ref&lt;/a&gt;, &lt;a href="https://medium.com/google-cloud/exploring-sandboxing-for-ai-generated-google-apps-script-0652a68eb4b7" rel="noopener noreferrer"&gt;Ref&lt;/a&gt;. Yet, liberating this sandboxed execution capability so that it can be directly orchestrated from Google Apps Script unlocks a vastly broader horizon. It transforms Apps Script from a bounded JavaScript runtime into an agile command center. Beyond safely running AI-generated scripts in real time, it empowers Google Workspace to seamlessly offload high-performance Python and Bash workloads—spanning advanced statistics, scientific plotting with Pandas and Seaborn, and complex data transformations—that were previously unattainable within Apps Script alone.&lt;/p&gt;

&lt;p&gt;In this article, I introduce this complementary architecture powered by &lt;strong&gt;Google Cloud Run Sandboxes&lt;/strong&gt; (&lt;code&gt;--sandbox-launcher&lt;/code&gt;) &lt;a href="https://cloud.google.com/run/docs/configuring/sandboxes" rel="noopener noreferrer"&gt;Ref&lt;/a&gt;. By pairing gVisor &lt;a href="https://gvisor.dev/" rel="noopener noreferrer"&gt;Ref&lt;/a&gt; micro-virtualization with second-generation Cloud Run instances &lt;a href="https://cloud.google.com/run" rel="noopener noreferrer"&gt;Ref&lt;/a&gt;, Apps Script can dispatch dynamic Python and Bash scripts over standard REST HTTP calls. The benefits are clear: sub-second execution (200 to 450 ms), zero-trust process isolation, and zero idle maintenance costs. Together, Cloud Run Sandboxes and Gemini Managed Agents give developers a comprehensive automation toolkit for Google Workspace.&lt;/p&gt;




&lt;h2&gt;
  
  
  Architecture: Cloud Run Sandboxes for Google Apps Script
&lt;/h2&gt;

&lt;p&gt;Cloud Run Sandboxes compartmentalize untrusted code execution using gVisor application kernel technology. Integrating this infrastructure with Google Apps Script offers four major benefits:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F64bu1w4osb84vuiu83vc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F64bu1w4osb84vuiu83vc.png" alt="Google Apps Script × Cloud Run Sandboxes Architecture" width="800" height="447"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Figure 1: Architectural workflow linking Google Apps Script, Cloud Run FastAPI Runner, and gVisor Micro-Sandbox.&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Dynamic code execution without image rebuilding&lt;/strong&gt;: You never need to rebuild or redeploy container images when script logic changes. Apps Script dynamically generates Python or Bash code strings and posts them to the Cloud Run runner for immediate execution.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Crash resilience against runaway scripts&lt;/strong&gt;: If an offloaded script triggers a segmentation fault or an infinite loop (&lt;code&gt;while True: pass&lt;/code&gt;), gVisor isolates and terminates only the child process via SIGKILL. The parent FastAPI runner remains healthy and returns a clean JSON error response.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deterministic sub-second latency&lt;/strong&gt;: Because the sandbox forks directly inside a running container, it avoids cold VM boots and prompt delays, running guest code in 200 to 450 milliseconds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero-idle cost management&lt;/strong&gt;: Setting &lt;code&gt;--min-instances=0&lt;/code&gt; allows Cloud Run to scale to zero when idle. Combined with Google Cloud's Always Free tier &lt;a href="https://cloud.google.com/run/pricing" rel="noopener noreferrer"&gt;Ref&lt;/a&gt;, everyday automation incurs zero idle maintenance cost.&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  System Architecture and Processing Workflow
&lt;/h2&gt;

&lt;p&gt;The architecture connects three layers: the Google Apps Script orchestrator, the Cloud Run FastAPI proxy runner, and the gVisor micro-sandbox isolation layer. For complete, step-by-step setup and deployment instructions, please refer to the detailed guide in the &lt;a href="https://github.com/tanaikech/cloud-run-sandbox-gas" rel="noopener noreferrer"&gt;GitHub Repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Execution flows through three sequential stages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Stage 1: Dispatch from Google Apps Script&lt;/strong&gt;: Apps Script sends an HTTPS POST request with a JSON payload containing the code snippet, language (Python or Bash), timeout, and security override flags. Core logic is implemented in &lt;a href="https://github.com/tanaikech/cloud-run-sandbox-gas/blob/master/gas/Code.js" rel="noopener noreferrer"&gt;gas/Code.js&lt;/a&gt; and &lt;a href="https://github.com/tanaikech/cloud-run-sandbox-gas/blob/master/gas/Auth.js" rel="noopener noreferrer"&gt;gas/Auth.js&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stage 2: Execution by FastAPI Proxy Runner&lt;/strong&gt;: A lightweight Python service on Cloud Run Gen2 (&lt;a href="https://github.com/tanaikech/cloud-run-sandbox-gas/blob/master/cloud_run/main.py" rel="noopener noreferrer"&gt;cloud_run/main.py&lt;/a&gt;) receives the payload. It invokes &lt;code&gt;/usr/local/gcp/bin/sandbox do -- &amp;lt;command&amp;gt;&lt;/code&gt;, capturing wall-clock runtime, exit codes, stdout, and stderr.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stage 3: Ephemeral isolation in gVisor&lt;/strong&gt;: The gVisor sandbox intercepts every guest system call. It blocks access to the Google Cloud Metadata Server (169.254.169.254) to eliminate SSRF risks, strips host environment variables, enforces a read-only root filesystem, and shuts down external network egress by default.&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  8-Axis Verification Suite from Google Apps Script
&lt;/h2&gt;

&lt;p&gt;To verify both functional accuracy and security boundaries, I designed and executed an 8-axis test suite directly from Google Apps Script.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbpuzgtweujr15pz8vtqx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbpuzgtweujr15pz8vtqx.png" alt="Cloud Run Sandboxes Test Suite Matrix" width="800" height="447"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Figure 2: Comprehensive 8-axis test suite matrix evaluating deterministic execution and security isolation.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The complete suite is implemented in &lt;a href="https://github.com/tanaikech/cloud-run-sandbox-gas/blob/master/gas/TestCases.js" rel="noopener noreferrer"&gt;gas/TestCases.js&lt;/a&gt;. The core verification logic includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;TC-01: Basic Computation&lt;/strong&gt;: Evaluates deterministic Python arithmetic (&lt;code&gt;print(2**32)&lt;/code&gt;). Expected result: &lt;code&gt;4294967296&lt;/code&gt; with exit code 0. Full test definition: &lt;a href="https://github.com/tanaikech/cloud-run-sandbox-gas/blob/master/gas/TestCases.js" rel="noopener noreferrer"&gt;gas/TestCases.js:TC-01&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TC-02: Syntax Error Handling&lt;/strong&gt;: Injects an invalid syntax payload (&lt;code&gt;print('unclosed string literal&lt;/code&gt;) to verify crash resistance. Expected result: Structured error JSON containing &lt;code&gt;SyntaxError&lt;/code&gt; without container failure. Full test definition: &lt;a href="https://github.com/tanaikech/cloud-run-sandbox-gas/blob/master/gas/TestCases.js" rel="noopener noreferrer"&gt;gas/TestCases.js:TC-02&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TC-03: Infinite Loop DoS Defense&lt;/strong&gt;: Runs an infinite loop (&lt;code&gt;while True: pass&lt;/code&gt;) with a 2.0-second timeout. Expected result: gVisor terminates the guest process cleanly after 2.0 seconds via SIGKILL. Full test definition: &lt;a href="https://github.com/tanaikech/cloud-run-sandbox-gas/blob/master/gas/TestCases.js" rel="noopener noreferrer"&gt;gas/TestCases.js:TC-03&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TC-04: Metadata Server SSRF Isolation&lt;/strong&gt;: Probes &lt;code&gt;169.254.169.254&lt;/code&gt; to attempt service account token extraction. Expected result: Request blocked with &lt;code&gt;Network is unreachable&lt;/code&gt;. Full test definition: &lt;a href="https://github.com/tanaikech/cloud-run-sandbox-gas/blob/master/gas/TestCases.js" rel="noopener noreferrer"&gt;gas/TestCases.js:TC-04&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TC-05: Host Environment Variable Shielding&lt;/strong&gt;: Dumps guest environment variables to check host credential leakage. Expected result: GCP credentials and host variables are completely absent. Full test definition: &lt;a href="https://github.com/tanaikech/cloud-run-sandbox-gas/blob/master/gas/TestCases.js" rel="noopener noreferrer"&gt;gas/TestCases.js:TC-05&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TC-06: Filesystem Write Protection&lt;/strong&gt;: Attempts to write to the container root filesystem. Expected result: Blocked with &lt;code&gt;Read-only file system&lt;/code&gt; by default; allowed only in isolated tmpfs with &lt;code&gt;--write&lt;/code&gt;. Full test definition: &lt;a href="https://github.com/tanaikech/cloud-run-sandbox-gas/blob/master/gas/TestCases.js" rel="noopener noreferrer"&gt;gas/TestCases.js:TC-06&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TC-07: Outbound Network Egress Isolation&lt;/strong&gt;: Attempts an outbound TCP connection to public DNS (&lt;code&gt;1.1.1.1:53&lt;/code&gt;). Expected result: Connection rejected by default; allowed only with &lt;code&gt;--allow-egress&lt;/code&gt;. Full test definition: &lt;a href="https://github.com/tanaikech/cloud-run-sandbox-gas/blob/master/gas/TestCases.js" rel="noopener noreferrer"&gt;gas/TestCases.js:TC-07&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TC-08: Isolated Bash Subshell Execution&lt;/strong&gt;: Runs &lt;code&gt;uname -a &amp;amp;&amp;amp; id&lt;/code&gt; in a Bash subshell. Expected result: Returns the kernel signature &lt;code&gt;4.19.0-gvisor&lt;/code&gt;, confirming gVisor containment. Full test definition: &lt;a href="https://github.com/tanaikech/cloud-run-sandbox-gas/blob/master/gas/TestCases.js" rel="noopener noreferrer"&gt;gas/TestCases.js:TC-08&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Execution Log and Verification Results
&lt;/h2&gt;

&lt;p&gt;The following execution log was captured directly from the Google Apps Script logger during my verification run. Project hashes are sanitized as &lt;code&gt;[PROJECT-HASH]&lt;/code&gt;, container UUIDs are sanitized as &lt;code&gt;[INSTANCE-UUID]&lt;/code&gt;, and timestamps are normalized to start at &lt;code&gt;00:00:00&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;00:00:00    Notice  Execution started
00:00:00    Info    ================================================================================
00:00:00    Info    STARTING CLOUD RUN SANDBOX TEST SUITE (GAS EXECUTION)
00:00:00    Info    Target Base URL: https://cr-gas-sandbox-[PROJECT-HASH]-uc.a.run.app
00:00:00    Info    Started At     : 2026-09-09T00:00:00.000Z
00:00:00    Info    ================================================================================
00:00:00    Info    Executing [TC-01] Basic Python Computation (2**32)...
00:00:04    Info    Executing [TC-02] Syntax Error Handling &amp;amp; Crash Resistance...
00:00:05    Info    Executing [TC-03] Infinite Loop DoS Defense (Timeout Enforcement)...
00:00:07    Info    Executing [TC-04] Metadata Server SSRF Isolation (169.254.169.254)...
00:00:08    Info    Executing [TC-05] Host Environment Variable Shielding...
00:00:09    Info    Executing [TC-06] Filesystem Write Protection (Ephemeral tmpfs)...
00:00:10    Info    Executing [TC-07] Outbound Network Egress Isolation...
00:00:12    Info    Executing [TC-08] Isolated Bash Subshell Command Execution...
00:00:13    Info
### 📊 CLOUD RUN SANDBOX × GAS VERIFICATION RUN REPORT

- Total Tests Executed: 8
- Passed: 8 / 8
- Failed: 0 / 8
- Total Execution Wall Time: 12.44 seconds
- Overall Verdict: ✅ ALL TESTS PASSED

| Test ID | Test Name | HTTP | Server Time | Network RTT | Sandbox Active | Status |
| :--- | :--- | :---: | :---: | :---: | :---: | :---: |
| **TC-01** | Basic Python Computation (2**32) | 200 | 449.34 ms | 3609 ms | gVisor (True) | ✅ PASS |
| **TC-02** | Syntax Error Handling &amp;amp; Crash Resistance | 200 | 201.43 ms | 322 ms | gVisor (True) | ✅ PASS |
| **TC-03** | Infinite Loop DoS Defense (Timeout Enforcement) | 200 | 2003.96 ms | 2118 ms | gVisor (True) | ✅ PASS |
| **TC-04** | Metadata Server SSRF Isolation (169.254.169.254) | 200 | 716.68 ms | 874 ms | gVisor (True) | ✅ PASS |
| **TC-05** | Host Environment Variable Shielding | 200 | 411.31 ms | 539 ms | gVisor (True) | ✅ PASS |
| **TC-06** | Filesystem Write Protection (Ephemeral tmpfs) | 200 | 641.73 ms | 757 ms | gVisor (True) | ✅ PASS |
| **TC-07** | Outbound Network Egress Isolation | 200 | 992.04 ms | 1106 ms | gVisor (True) | ✅ PASS |
| **TC-08** | Isolated Bash Subshell Command Execution | 200 | 338.78 ms | 460 ms | gVisor (True) | ✅ PASS |

#### 📝 Detailed Execution Breakdown &amp;amp; Raw Outputs

--------------------------------------------------------------------------------
[TC-01] Basic Python Computation (2**32)
Endpoint       : /run
Verdict        : PASS - Correct computation result returned (4294967296)
Timing         : Server=449.34ms, RoundTrip=3609ms
Sandbox Active : true
stdout         : 4294967296
--------------------------------------------------------------------------------
[TC-02] Syntax Error Handling &amp;amp; Crash Resistance
Endpoint       : /run
Verdict        : PASS - Container remained healthy, structured error JSON safely returned
Timing         : Server=201.43ms, RoundTrip=322ms
Sandbox Active : true
stderr         : SyntaxError: unterminated string literal (detected at line 1)
--------------------------------------------------------------------------------
[TC-03] Infinite Loop DoS Defense (Timeout Enforcement)
Endpoint       : /run
Verdict        : PASS - Process terminated cleanly after 2.0s without hanging server
Timing         : Server=2003.96ms, RoundTrip=2118ms
Sandbox Active : true
stderr         : Execution timed out after 2.0 seconds
--------------------------------------------------------------------------------
[TC-04] Metadata Server SSRF Isolation (169.254.169.254)
Endpoint       : /test/metadata-isolation
Verdict        : PASS - Metadata server unreachable from sandbox (SSRF Blocked)
Timing         : Server=716.68ms, RoundTrip=874ms
Sandbox Active : true
details        : ACCESS_BLOCKED: URLError: &amp;lt;urlopen error [Errno 101] Network is unreachable&amp;gt;
--------------------------------------------------------------------------------
[TC-05] Host Environment Variable Shielding
Endpoint       : /test/env-isolation
Verdict        : PASS - Host GCP credentials and environment variables fully masked
Timing         : Server=411.31ms, RoundTrip=539ms
Sandbox Active : true
details        : Sensitive host environment variables are properly masked from sandbox guest.
--------------------------------------------------------------------------------
[TC-06] Filesystem Write Protection (Ephemeral tmpfs)
Endpoint       : /test/fs-isolation
Verdict        : PASS - Read-only filesystem enforced by default; write permitted only with flag
Timing         : Server=641.73ms, RoundTrip=757ms
Sandbox Active : true
details        : Write blocked without flag: PASS. Write permitted with flag: PASS.
--------------------------------------------------------------------------------
[TC-07] Outbound Network Egress Isolation
Endpoint       : /test/egress-isolation
Verdict        : PASS - Outbound network traffic blocked by default; enabled only with flag
Timing         : Server=992.04ms, RoundTrip=1106ms
Sandbox Active : true
details        : Egress blocked without flag: PASS. Egress allowed with flag: PASS.
--------------------------------------------------------------------------------
[TC-08] Isolated Bash Subshell Command Execution
Endpoint       : /run
Verdict        : PASS - Bash subshell executed securely inside sandbox
Timing         : Server=338.78ms, RoundTrip=460ms
Sandbox Active : true
stdout         : Linux sandbox-[INSTANCE-UUID] 4.19.0-gvisor #1 SMP Sun Jan 10 15:06:54 PST 2016 x86_64 GNU/Linux
--------------------------------------------------------------------------------
00:00:13    Notice  Execution completed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All eight test vectors achieved a 100% PASS rate. In particular, TC-08 explicitly returned the kernel signature &lt;code&gt;4.19.0-gvisor&lt;/code&gt;, confirming gVisor process containment.&lt;/p&gt;




&lt;h2&gt;
  
  
  Latency and Performance Analysis
&lt;/h2&gt;

&lt;p&gt;Measuring execution telemetry reveals distinct performance profiles between cold starts and warm executions:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frn3bafobe3a1wihenatr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frn3bafobe3a1wihenatr.png" alt="Latency Breakdown: Cold Start vs Warm Execution" width="800" height="447"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Figure 3: Latency breakdown between cold-start container provisioning and warm micro-sandbox execution.&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cold Start Phase (TC-01)&lt;/strong&gt;: Scaling from zero instances, the initial request takes 3,609 ms round-trip time. Cloud Run container provisioning accounts for 3.16 seconds of this time, while the gVisor fork and Python execution take only 449 ms.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Warm Execution Phase (TC-02 to TC-08)&lt;/strong&gt;: Subsequent calls bypass container startup, reducing round-trip latency to 322–539 ms. Server-side sandbox processing remains steady at 201–411 ms.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Minimal Compute Overhead&lt;/strong&gt;: Because gVisor forks an existing process tree rather than booting an external virtual machine, virtualization overhead stays below 50 ms.&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Cost Analysis and Safety Guardrails
&lt;/h2&gt;

&lt;p&gt;Running external cloud services for automation often raises concerns about unexpected billing. In this architecture, costs are bounded by Google Cloud policies &lt;a href="https://cloud.google.com/run/pricing" rel="noopener noreferrer"&gt;Ref&lt;/a&gt; and explicit service guardrails:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fh478lj74gsdhz09l5y7z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fh478lj74gsdhz09l5y7z.png" alt="Google Cloud Always Free Tier Allocations &amp;amp; Cost Architecture" width="800" height="447"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Figure 4: Google Cloud Always Free tier allocations and zero-idle cost architecture.&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Always Free Tier Allocation&lt;/strong&gt;: Cloud Run provides 2 million requests, 360,000 GiB-seconds of memory, and 180,000 vCPU-seconds free per month &lt;a href="https://cloud.google.com/run/pricing" rel="noopener noreferrer"&gt;Ref&lt;/a&gt;. A 0.5-second execution on a 512MiB/1CPU profile allows hundreds of thousands of monthly requests without cost.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero-Idle Scaling (&lt;code&gt;--min-instances=0&lt;/code&gt;)&lt;/strong&gt;: Cloud Run shuts down all active instances when idle, generating zero compute charges between executions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Concurrency Guardrails (&lt;code&gt;--max-instances=1&lt;/code&gt;)&lt;/strong&gt;: Capping instances at 1 ensures runaway recursive triggers in Apps Script cannot spawn concurrent container fleets.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hard Timeout Enforcement (&lt;code&gt;--timeout=15s&lt;/code&gt;)&lt;/strong&gt;: Cloud Run forcibly terminates any request exceeding 15 seconds, preventing long-running resource drain.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One-Step Cleanup Script&lt;/strong&gt;: The repository includes &lt;a href="https://github.com/tanaikech/cloud-run-sandbox-gas/blob/master/scripts/cleanup.sh" rel="noopener noreferrer"&gt;scripts/cleanup.sh&lt;/a&gt; to delete the Cloud Run service, Artifact Registry images, and temporary Cloud Storage buckets with a single command.&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Practical Application: Generating Multivariate Correlation Heatmaps on Google Sheets
&lt;/h2&gt;

&lt;p&gt;While unit tests validate security and latency, the real value of Cloud Run Sandboxes emerges in solving everyday Google Workspace challenges that Apps Script cannot address natively.&lt;/p&gt;

&lt;p&gt;A prime example is advanced scientific visualization. While Google Sheets provides standard bar and line charts, it completely lacks native support for bivariate or multivariate statistical visualizations—such as Pearson correlation matrix heatmaps, kernel density estimates, or regression confidence bands. Traditionally, developers were forced to either export datasets to external desktop environments or configure cumbersome third-party visualization services.&lt;/p&gt;

&lt;p&gt;By combining Google Apps Script with Cloud Run Sandboxes, developers can bridge this gap seamlessly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Automated Sheet Population&lt;/strong&gt;: Apps Script creates a new Google Spreadsheet and populates it with a multivariate table (for example, academic evaluation scores across Math, Physics, Chemistry, English, and History).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dynamic Script Formulation&lt;/strong&gt;: Apps Script extracts the numeric matrix and pairs it with a Python script utilizing &lt;code&gt;pandas&lt;/code&gt;, &lt;code&gt;matplotlib&lt;/code&gt;, and &lt;code&gt;seaborn&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sub-Second gVisor Execution&lt;/strong&gt;: The payload is dispatched to Cloud Run Sandboxes (&lt;code&gt;POST /run&lt;/code&gt;). Inside the isolated micro-sandbox, Python computes the correlation matrix (&lt;code&gt;df.corr()&lt;/code&gt;) and renders a publication-grade heatmap PNG directly into a Base64 string in ~350 ms.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Direct Blob Insertion&lt;/strong&gt;: Apps Script decodes the Base64 output into a native image Blob (&lt;code&gt;Utilities.newBlob()&lt;/code&gt;) and embeds it directly beside the dataset on the Google Sheet (&lt;code&gt;sheet.insertImage()&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  The Dynamic Python Visualization Script
&lt;/h3&gt;

&lt;p&gt;In &lt;a href="https://github.com/tanaikech/cloud-run-sandbox-gas/blob/master/gas/PracticalDemo.js" rel="noopener noreferrer"&gt;&lt;code&gt;gas/PracticalDemo.js&lt;/code&gt;&lt;/a&gt;, Apps Script dynamically constructs and dispatches the following Python script to the Cloud Run runner:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;MPLCONFIGDIR&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/tmp/mpl&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;matplotlib&lt;/span&gt;
&lt;span class="n"&gt;matplotlib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Agg&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;matplotlib.pyplot&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;plt&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pandas&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;seaborn&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;sns&lt;/span&gt;

&lt;span class="c1"&gt;# Injected empirical dataset from Google Sheets
&lt;/span&gt;&lt;span class="n"&gt;records&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Math&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;95&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Physics&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;92&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Chemistry&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;88&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;English&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;72&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;History&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;68&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="c1"&gt;# ... (15 rows injected dynamically from spreadsheet)
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;df&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DataFrame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;records&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Calculate Pearson correlation matrix
&lt;/span&gt;&lt;span class="n"&gt;corr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;corr&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# Configure publication-grade styling
&lt;/span&gt;&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;figure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;figsize&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;6.8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;5.2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;dpi&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;sns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_theme&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;white&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;cmap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;diverging_palette&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;230&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;as_cmap&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;ax&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;heatmap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;corr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;annot&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;.2f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cmap&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;cmap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vmin&lt;/span&gt;&lt;span class="o"&gt;=-&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vmax&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                 &lt;span class="n"&gt;square&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;linewidths&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cbar_kws&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;shrink&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.8&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;title&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Student Performance: Multivariate Correlation Heatmap&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fontsize&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fontweight&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;bold&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pad&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tight_layout&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# Encode plot to Base64 PNG buffer
&lt;/span&gt;&lt;span class="n"&gt;buf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;BytesIO&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;savefig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;png&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dpi&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;seek&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;b64_png&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;b64encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getvalue&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Output structured JSON to stdout
&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;success&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;image_base64&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;b64_png&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;variables&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;records_processed&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This Python script is engineered with four deliberate architectural techniques:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;gVisor Read-Only Filesystem Adaptation (&lt;code&gt;MPLCONFIGDIR&lt;/code&gt;)&lt;/strong&gt;: In Cloud Run Sandboxes, gVisor enforces a strict read-only root filesystem (&lt;code&gt;/&lt;/code&gt;) to defend against tampering. Under normal circumstances, Matplotlib attempts to create a font cache in &lt;code&gt;~/.config/matplotlib&lt;/code&gt;, throwing write permission warnings or errors. Setting &lt;code&gt;os.environ['MPLCONFIGDIR'] = '/tmp/mpl'&lt;/code&gt; redirects cache writes to the ephemeral, writable &lt;code&gt;/tmp&lt;/code&gt; (tmpfs) directory, ensuring clean, warning-free execution with empty &lt;code&gt;stderr&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Headless In-Memory Image Rendering&lt;/strong&gt;: Setting &lt;code&gt;matplotlib.use('Agg')&lt;/code&gt; and utilizing an in-memory &lt;code&gt;io.BytesIO()&lt;/code&gt; buffer completely eliminates disk I/O. The chart is rendered in memory, encoded into Base64, and recycled immediately, delivering sub-second execution speeds without leaving stale files on disk.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Structured Stdout Communication Protocol&lt;/strong&gt;: The script outputs a single, structured JSON payload to &lt;code&gt;stdout&lt;/code&gt; containing the Base64 image and processing metadata. Apps Script parses this response effortlessly, requiring no complex multi-part MIME decoding or secondary storage buckets.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Direct Empirical Data Injection&lt;/strong&gt;: Tabular data extracted from Google Sheets is serialized as JSON and directly embedded into the guest script template. This allows arbitrary row counts and columns to be processed without requiring database drivers or network connections inside the sandbox.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Dynamic Script Generation via Gemini API
&lt;/h3&gt;

&lt;p&gt;An essential architectural advantage of this setup is that the Python script does not need to be hard-coded. Developers can call the Gemini API directly from Apps Script with a natural language prompt—such as &lt;em&gt;"Generate a Python Seaborn script to compute a correlation heatmap from this JSON table and output Base64 PNG"&lt;/em&gt;—to dynamically synthesize the visualization code on demand.&lt;/p&gt;

&lt;p&gt;This workflow unlocks a powerful synergy:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Gemini (Single-Turn Code Generation)&lt;/strong&gt;: Formulates tailored data transformation and plotting logic from high-level natural language instructions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cloud Run Sandboxes (Instant Deterministic Execution)&lt;/strong&gt;: Runs the generated Python code inside an ephemeral gVisor micro-sandbox at sub-second speeds (200–450 ms), completely shielding Google Workspace from untrusted code execution while incurring zero idle hosting cost.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Operational Advantages over Gemini Managed Agents
&lt;/h3&gt;

&lt;p&gt;In my previous article, &lt;a href="https://medium.com/google-cloud/taking-advantage-of-gemini-managed-agents-with-google-apps-script-19215ab8c61f" rel="noopener noreferrer"&gt;&lt;em&gt;Taking Advantage of Gemini Managed Agents with Google Apps Script&lt;/em&gt;&lt;/a&gt;, Gemini Managed Agents provisioned a heavy-duty container (4 vCPU / 16 GB RAM) with a multi-hour persistent session. That architecture excels at exploratory, conversational agentic workflows where the agent navigates ambiguous tasks and self-heals over multiple turns.&lt;/p&gt;

&lt;p&gt;However, for automated spreadsheet recalculation, scheduled batch reporting, or user-facing UI triggers, Cloud Run Sandboxes provides a distinctly superior operational profile:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Sub-Second Execution Speed&lt;/strong&gt;: Cloud Run Sandboxes renders and returns the chart in ~350 ms, compared to 15–30+ seconds for conversational LLM agent reasoning.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compatibility with Google Sheets Custom Functions (30-Second Limit)&lt;/strong&gt;: Google Sheets custom functions (formula functions called directly from cells) enforce a rigid, non-negotiable 30-second execution timeout. In the Gemini Managed Agents architecture, having a generative AI model in the loop creates conversational inference overhead that frequently pushes turnaround times to 15–40+ seconds, making it ill-suited for in-cell custom formulas. In contrast, Cloud Run Sandboxes deliver deterministic sub-second responses (200–450 ms), operating well below the 30-second ceiling. This low latency makes it effortless to create dynamic, cell-level custom functions powered by Python (such as statistical modeling, complex regex transformations, or matrix operations) that update smoothly as spreadsheet data changes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero-Token High-Volume Data Ingestion&lt;/strong&gt;: Sends thousands of spreadsheet rows, raw arrays, or megabytes of JSON directly over standard HTTP payloads without consuming LLM input tokens, completely removing TPM rate-limit anxiety and payload token costs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero Cost and Generous Quotas&lt;/strong&gt;: Cloud Run Sandboxes consumes no LLM tokens and operates entirely within Cloud Run's generous Always Free tier.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deterministic Mathematical Precision&lt;/strong&gt;: The statistical calculations and graphical outputs are 100% deterministic and reproducible, eliminating prompt hallucinations.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The implementation is available in the repository as &lt;a href="https://github.com/tanaikech/cloud-run-sandbox-gas/blob/master/gas/PracticalDemo.js" rel="noopener noreferrer"&gt;&lt;code&gt;gas/PracticalDemo.js&lt;/code&gt;&lt;/a&gt;. To support Google Sheets creation and image embedding, ensure your &lt;code&gt;appsscript.json&lt;/code&gt; includes the &lt;code&gt;"https://www.googleapis.com/auth/spreadsheets"&lt;/code&gt; and &lt;code&gt;"https://www.googleapis.com/auth/drive"&lt;/code&gt; OAuth scopes as documented in &lt;a href="https://github.com/tanaikech/cloud-run-sandbox-gas/blob/master/gas/setup_instructions.md" rel="noopener noreferrer"&gt;&lt;code&gt;gas/setup_instructions.md&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fttns81g10hp6w8n20pzj.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fttns81g10hp6w8n20pzj.jpg" alt="Google Sheets Correlation Heatmap Result" width="800" height="391"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Figure 5: Google Sheets generated with multivariate student data and an embedded correlation heatmap rendered by Cloud Run Sandboxes (Matplotlib &amp;amp; Seaborn).&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Real-World Execution Telemetry and Perceptual Performance
&lt;/h3&gt;

&lt;p&gt;When executing &lt;code&gt;runPracticalHeatmapDemo()&lt;/code&gt; directly from Google Apps Script, the perceptual speed is electrifying. The actual execution log demonstrates this seamless end-to-end flow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;00:00:01    Notice  Execution started
00:00:02    Info    ================================================================================
00:00:02    Info    STARTING PRACTICAL DEMO: SPREADSHEET HEATMAP VIA CLOUD RUN SANDBOX
00:00:02    Info    Target Base URL: https://cr-gas-sandbox-[PROJECT-HASH]-uc.a.run.app
00:00:02    Info    Started At     : 2026-09-09T00:00:02.275Z
00:00:02    Info    ================================================================================
00:00:02    Info    [Step 1/4] Creating new Google Spreadsheet with multivariate student metrics...
00:00:04    Info    Spreadsheet created: https://docs.google.com/spreadsheets/d/[SPREADSHEET-ID]/edit
00:00:04    Info    [Step 2/4] Reading data from Sheet and constructing JSON matrix payload...
00:00:04    Info    Extracted 15 rows across 5 variables.
00:00:04    Info    [Step 3/4] Dispatching payload to Cloud Run Sandboxes (POST /run)...
00:00:08    Info    Sandbox Server Time : 4161.7 ms
00:00:08    Info    Network Round-Trip  : 4343 ms
00:00:08    Info    Is Sandboxed (gVisor): true
00:00:08    Info    [Step 4/4] Converting Base64 output to Blob and embedding onto Google Sheet...
00:00:09    Info    ================================================================================
00:00:09    Info    DEMO COMPLETED SUCCESSFULLY!
00:00:09    Info    Spreadsheet URL : https://docs.google.com/spreadsheets/d/[SPREADSHEET-ID]/edit
00:00:09    Info    Execution Time  : Server 4161.7 ms | Total 4343 ms
00:00:09    Info    ================================================================================
00:00:10    Notice  Execution completed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The entire pipeline—creating a new Google Spreadsheet, styling header rows, reading data back, dispatching across HTTPS to Cloud Run, cold-loading heavy scientific libraries (&lt;code&gt;pandas&lt;/code&gt;, &lt;code&gt;numpy&lt;/code&gt;, &lt;code&gt;matplotlib&lt;/code&gt;, &lt;code&gt;seaborn&lt;/code&gt;) inside a gVisor micro-sandbox, calculating Pearson correlation coefficients, rendering the plot, streaming the Base64 PNG back, decoding into a Blob, and embedding it into the Sheet—finished in &lt;strong&gt;just 9 seconds of total wall-clock time&lt;/strong&gt; (with only 4.16 seconds of Cloud Run server compute).&lt;/p&gt;

&lt;h3&gt;
  
  
  Visual Layout and Analytical Fidelity
&lt;/h3&gt;

&lt;p&gt;Examining the resulting Google Sheet (Figure 5) reveals exceptional publication quality:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tabular Matrix (Columns A–F)&lt;/strong&gt;: 15 student records populated across 5 academic disciplines (Math, Physics, Chemistry, English, and History) with styled blue headers and clean grid alignment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Embedded Chart (Columns H–L)&lt;/strong&gt;: The 5x5 Pearson correlation matrix rendered by Seaborn using a diverging palette (&lt;code&gt;coolwarm&lt;/code&gt;) with explicit correlation coefficients and colorbar (-1.00 to +1.00).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Domain Insights&lt;/strong&gt;: STEM subjects (Math, Physics, Chemistry) display strong positive clustering (&amp;gt; 0.94), while humanities (English, History) correlate at 0.99. Cross-domain pairs exhibit sharp negative divergence (-0.95 to -0.99), delivering instant statistical clarity.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Perceptual Speed Leap&lt;/strong&gt;: Compared to Gemini Managed Agents, which typically requires 30 to 60+ seconds for conversational reasoning and container bootstrapping, Cloud Run Sandboxes feels instantaneous. For spreadsheet users, waiting 8–9 seconds for a full end-to-end spreadsheet creation and visual chart generation is a revolutionary leap in developer experience.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Comparing Cloud Run Sandboxes and Gemini Managed Agents
&lt;/h2&gt;

&lt;p&gt;Choosing between these two technologies from Google Apps Script requires evaluating the fundamental trade-offs between autonomous artificial intelligence and deterministic high-speed computation:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5buy81oms6lzqlcsluxw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5buy81oms6lzqlcsluxw.png" alt="Cloud Run Sandboxes vs Gemini Managed Agents" width="800" height="447"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Figure 6: Strategic workload comparison: Cloud Run Sandboxes vs. Gemini Managed Agents.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpop5phm0n4tresd2vvqc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpop5phm0n4tresd2vvqc.png" alt="Compute Specifications &amp;amp; Session Lifecycle Architecture" width="800" height="447"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Figure 7: Detailed compute specifications, hardware profiles, and session lifecycle comparison.&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Workload Intent and Reasoning&lt;/strong&gt;: Cloud Run Sandboxes &lt;a href="https://cloud.google.com/run/docs/configuring/sandboxes" rel="noopener noreferrer"&gt;Ref&lt;/a&gt; target deterministic, high-speed code execution under strict isolation. In contrast, Gemini Managed Agents &lt;a href="https://ai.google.dev/gemini-api/docs/agents" rel="noopener noreferrer"&gt;Ref&lt;/a&gt; handle autonomous workflows requiring natural language reasoning, multi-step problem solving, and dynamic package installation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Execution Trigger&lt;/strong&gt;: Cloud Run Sandboxes take raw code strings over standard REST HTTP POST requests from Apps Script. Gemini Managed Agents require natural language prompts via the Interactions API.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Latency Profiles&lt;/strong&gt;: Cloud Run Sandboxes deliver sub-second responses (200 to 450 ms server execution). Gemini Managed Agents incur conversational LLM inference delays of several to tens of seconds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hardware Specifications and Compute Resources&lt;/strong&gt;: Cloud Run Sandboxes are deployed on an agile, minimal compute profile (1 vCPU / 512 MiB RAM in this architecture, scalable up to 8 vCPU / 32 GB), minimizing virtualization overhead and maximizing Always Free tier mileage. In contrast, Gemini Managed Agents provision a heavy-duty container profile (4 vCPU / 16 GB RAM) capable of supporting large browser engines (Playwright/Chromium) and media encoding toolchains (FFmpeg).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Session Lifetime and Execution Duration (TTL)&lt;/strong&gt;: Cloud Run Sandboxes operate under an ephemeral, request-scoped lifecycle (sub-second execution with a strict 15-second Cloud Run hard cap), instantly destroying all in-memory tmpfs state upon request completion and scaling to zero instances when idle. In contrast, Gemini Managed Agents provide a persistent multi-turn session lifecycle (&lt;code&gt;environmentId&lt;/code&gt;) with a TTL lasting several hours, allowing installed tools and shared disk state to survive across multiple interactions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data Payload Ingestion and Token Freedom&lt;/strong&gt;: Cloud Run Sandboxes ingests multi-megabyte raw JSON or binary Base64 streams directly over standard HTTP (up to 32 MB) with zero token consumption and zero rate-limit constraints. In contrast, feeding high-volume tabular datasets or multi-megabyte dumps into Gemini Managed Agents consumes thousands of input tokens per interaction, rapidly exhausting 200k TPM quotas and risking context window saturation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pricing and Quotas&lt;/strong&gt;: Cloud Run Sandboxes use standard compute billing, remaining comfortably within free tiers for typical automation tasks. Gemini Managed Agents consume LLM tokens subject to TPM limits (such as 200k TPM) &lt;a href="https://ai.google.dev/gemini-api/docs/rate-limits" rel="noopener noreferrer"&gt;Ref&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;State Persistence&lt;/strong&gt;: Cloud Run Sandboxes remain strictly ephemeral, recycling the environment after each run. Gemini Managed Agents offer persistent workspaces (&lt;code&gt;environmentId&lt;/code&gt;) to retain files and installed packages.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Architectural Trade-offs: Advantages and Limitations
&lt;/h3&gt;

&lt;p&gt;Understanding the practical strengths and weaknesses of each runtime ensures optimal architectural decisions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cloud Run Sandboxes Advantages&lt;/strong&gt;: Delivers deterministic sub-second execution (200 to 450 ms) with 100% mathematical precision and zero prompt ambiguity. Ingests high-volume tabular arrays directly over HTTP with zero token consumption, completely bypassing TPM rate limits. Generates zero idle hosting expenses through automatic scale-to-zero, staying well within the Always Free tier of 2 million monthly requests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cloud Run Sandboxes Limitations&lt;/strong&gt;: Purely ephemeral with no cross-request file or package persistence. Resource-constrained by default (512 MiB RAM / 1 vCPU) and strictly capped by a 15-second timeout, making it unsuitable for multi-hour sessions or heavyweight browser instances.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gemini Managed Agents Advantages&lt;/strong&gt;: Provides unmatched adaptability when the code or solution is not known in advance. Natural language instructions enable the agent to write its own scripts, inspect errors, self-heal, and dynamically install Linux packages inside a heavy-duty 16 GB container that persists across multiple turns.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gemini Managed Agents Limitations&lt;/strong&gt;: Suffers from conversational inference latency (often 10 to 30+ seconds), potential output non-determinism, and token quota exhaustion (200k TPM) during high-frequency invocation. It is ill-suited for real-time user-facing spreadsheet UI interactions.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Practical Scenarios: When to Use Which Architecture
&lt;/h3&gt;

&lt;p&gt;To maximize automation efficiency in Google Workspace, consider these proven deployment scenarios:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Scenario 1: High-Frequency Spreadsheet Recalculation and Custom Functions (Use Cloud Run Sandboxes)&lt;/strong&gt;: When building in-cell Google Sheets custom functions (such as &lt;code&gt;=PY_EVAL(...)&lt;/code&gt;) or triggering &lt;code&gt;onEdit&lt;/code&gt; events to perform numerical optimization, Monte Carlo simulations, or matrix operations across thousands of rows. Because Google Sheets enforces a rigid 30-second timeout on custom functions, Gemini Managed Agents' generative AI inference latency (15–40+ seconds) is ill-suited for this use case. Cloud Run Sandboxes return accurate results in ~300 ms, fitting comfortably within the 30-second limit and updating spreadsheet cells instantaneously without token costs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scenario 2: Dynamic Multi-Page Headless Browser Scraping (Use Gemini Managed Agents)&lt;/strong&gt;: When automating the extraction of dynamically rendered JavaScript tables across authenticated web portals using Playwright and Chromium. The 4 vCPU / 16 GB RAM environment and persistent filesystem allow the agent to manage browser cookies, navigate pages, and stream screenshots or PDF deliverables directly to Google Drive via &lt;code&gt;ggsrun&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scenario 3: Secure Data Transformation and Format Parsing (Use Cloud Run Sandboxes)&lt;/strong&gt;: When processing automated Google Forms submissions containing raw text, structured CSVs, or proprietary logs that require regular expression extraction, cryptographic hashing, or data validation. Cloud Run Sandboxes execute thousands of deterministic invocations daily within the Always Free tier without risking prompt hallucinations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scenario 4: Exploratory Research and Open-Ended Data Synthesis (Use Gemini Managed Agents)&lt;/strong&gt;: When an analyst uploads an unstructured data dump to Google Drive and asks the system to identify anomalies, formulate ad-hoc Python visualizations, and draft an executive narrative summary. The agent's autonomous reasoning and multi-turn persistence excel at iterating until high-level analytical goals are met.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;In this article, I demonstrated how to integrate Google Apps Script with Google Cloud Run Sandboxes to run dynamic Python and Bash workloads securely, deterministically, and with blistering speed. By offloading compute tasks to gVisor micro-sandboxes, Google Workspace automations gain the power to safely execute AI-generated scripts on demand and perform advanced data science without server maintenance overhead.&lt;/p&gt;

&lt;p&gt;Key takeaways from this implementation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Eliminated runtime limits and enabled safe AI code execution&lt;/strong&gt;: Executed arbitrary Python and Bash scripts on demand without container rebuilding, allowing Apps Script to safely run Gemini-generated logic and native data science libraries (Pandas, Seaborn).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Achieved blistering perceptual speed and sub-second latency&lt;/strong&gt;: Recorded 200–450 ms server execution speeds and completed an end-to-end spreadsheet visualization demo in just 9.0 seconds, delivering responsiveness orders of magnitude faster than multi-turn LLM agents.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enabled zero-token high-volume data exchange&lt;/strong&gt;: Streamed tabular data and images directly over standard HTTP payloads, completely bypassing LLM context windows, token billing, and 200k TPM rate limit bottlenecks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validated 4-axis zero-trust micro-isolation&lt;/strong&gt;: Confirmed empirical protection via gVisor against SSRF metadata extraction, host environment variable leaks, root filesystem modifications, and unauthorized network egress.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Established zero-idle operations and strategic synergy&lt;/strong&gt;: Maintained $0.00 standby costs through automatic scale-to-zero and the Always Free tier, establishing a comprehensive automation toolkit alongside autonomous Gemini Managed Agents.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Getting Started and Repository
&lt;/h2&gt;

&lt;p&gt;For comprehensive step-by-step setup guides, prerequisite configurations, one-click deployment procedures, and troubleshooting instructions, please refer to the complete documentation in the GitHub repository:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Detailed Usage Guide &amp;amp; Repository&lt;/strong&gt;: &lt;a href="https://github.com/tanaikech/cloud-run-sandbox-gas" rel="noopener noreferrer"&gt;https://github.com/tanaikech/cloud-run-sandbox-gas&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One-Click Deployment Script&lt;/strong&gt;: &lt;a href="https://github.com/tanaikech/cloud-run-sandbox-gas/blob/master/cloud_run/deploy.sh" rel="noopener noreferrer"&gt;cloud_run/deploy.sh&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;FastAPI Proxy Runner&lt;/strong&gt;: &lt;a href="https://github.com/tanaikech/cloud-run-sandbox-gas/blob/master/cloud_run/main.py" rel="noopener noreferrer"&gt;cloud_run/main.py&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Google Apps Script Orchestrator&lt;/strong&gt;: &lt;a href="https://github.com/tanaikech/cloud-run-sandbox-gas/blob/master/gas/Code.js" rel="noopener noreferrer"&gt;gas/Code.js&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GAS Test Suite Definitions&lt;/strong&gt;: &lt;a href="https://github.com/tanaikech/cloud-run-sandbox-gas/blob/master/gas/TestCases.js" rel="noopener noreferrer"&gt;gas/TestCases.js&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Complete Resource Cleanup Script&lt;/strong&gt;: &lt;a href="https://github.com/tanaikech/cloud-run-sandbox-gas/blob/master/scripts/cleanup.sh" rel="noopener noreferrer"&gt;scripts/cleanup.sh&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>googleappsscript</category>
      <category>googlecloud</category>
      <category>googlecloudrun</category>
      <category>googleworkspace</category>
    </item>
    <item>
      <title>Bidirectional Writeback for Apache Iceberg via Google Sheets: Serverless Lakehouse Console</title>
      <dc:creator>Tanaike</dc:creator>
      <pubDate>Tue, 08 Sep 2026 06:41:28 +0000</pubDate>
      <link>https://dev.to/gde/bidirectional-writeback-for-apache-iceberg-via-google-sheets-serverless-lakehouse-console-14ic</link>
      <guid>https://dev.to/gde/bidirectional-writeback-for-apache-iceberg-via-google-sheets-serverless-lakehouse-console-14ic</guid>
      <description>&lt;p&gt;&lt;em&gt;Turn Google Sheets into a Fully Interactive, Differential ACID Mutation Console for Apache Iceberg without Reverse ETL SaaS or Cloud Servers.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmgtzq8n5d6b9hu1g3qle.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmgtzq8n5d6b9hu1g3qle.jpg" alt="Hero Infographic: Interactive Bidirectional Lakehouse Writeback via Google Sheets &amp;amp; Apache Iceberg" width="800" height="447"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Hero Infographic: Interactive Bidirectional Lakehouse Writeback via Google Sheets &amp;amp; Apache Iceberg. Enables business operators to query filtered records from an open Apache Iceberg table on Google Cloud Storage, visually edit values, add new rows, or purge obsolete records directly within a Google Sheets grid with an embedded dark-themed console, and commit atomic, microsecond-tolerant ACID mutations back to Parquet storage via BigQuery without Reverse ETL SaaS or persistent servers.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Structural Analysis of the Hero Infographic:
&lt;/h2&gt;

&lt;p&gt;The hero infographic illustrates the complete, self-contained operational loop connecting frontline spreadsheet agility with immutable open lakehouse storage across three interconnected stages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;1. Predicate Query (Apache Iceberg Open Lakehouse on GCS)&lt;/strong&gt;: The left section shows the enterprise analytical foundation hosted on Google Cloud Storage, where Apache Iceberg manages immutable Parquet data files, hierarchical Avro metadata, and commit snapshots. When a user requests high-value records, BigQuery acts as an on-demand distributed compute accelerator, executing SQL queries with predicate pushdown (e.g., &lt;code&gt;SELECT * WHERE price &amp;gt; 1000 ORDER BY id ASC&lt;/code&gt;) to fetch precise subsets in sub-seconds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;2. Frontline Editing in Google Sheets (Intuitive Operational Experience)&lt;/strong&gt;: The central section features a modern, user-friendly Google Sheets grid docked with the sleek dark-themed &lt;strong&gt;Iceberg Lakehouse Console&lt;/strong&gt; sidebar. A business user effortlessly modifies data on the grid with immediate visual feedback: modifying existing values (e.g., updating price from &lt;code&gt;1500&lt;/code&gt; to &lt;code&gt;123&lt;/code&gt;), appending new rows with unique primary keys (&lt;code&gt;+ ADD (New Row id:121)&lt;/code&gt;), and deleting obsolete rows (&lt;code&gt;🗑️ DELETE (Removed id:104)&lt;/code&gt;). Native cell validation guarantees data cleanliness, while a Privacy Mode toggle (&lt;code&gt;[🔒 Privacy: ON]&lt;/code&gt;) automatically masks sensitive project identifiers during live broadcasting and screencasting.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;3. Atomic ACID Writeback (Google Apps Script CDC &amp;amp; MERGE INTO)&lt;/strong&gt;: The right section highlights the serverless Change Data Capture (CDC) engine operating entirely in Google Apps Script. It detects diffs against an immutable hidden baseline and synthesizes a single atomic &lt;code&gt;MERGE INTO&lt;/code&gt; SQL statement equipped with Optimistic Concurrency Control (OCC) and microsecond-discrepancy tolerance (&lt;code&gt;TIMESTAMP_DIFF&lt;/code&gt;). BigQuery commits the transaction back to the Iceberg table in sub-seconds, generating fresh Parquet snapshots with zero third-party SaaS subscription fees ($0/month) and zero data egress risk.&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Abstract
&lt;/h2&gt;

&lt;p&gt;In our previously published foundational article, &lt;em&gt;&lt;a href="https://dev.to/gde/unifying-google-workspace-and-apache-iceberg-serverless-lakehouse-management-ep3"&gt;Unifying Google Workspace and Apache Iceberg: Serverless Lakehouse Management&lt;/a&gt;&lt;/em&gt;, we demonstrated how the open-source &lt;strong&gt;&lt;a href="https://github.com/tanaikech/IcebergApp" rel="noopener noreferrer"&gt;IcebergApp&lt;/a&gt;&lt;/strong&gt; library overcomes Google Sheets' 10-million cell limit and script timeouts by offloading petabyte-scale analytical queries to BigQuery-accelerated Apache Iceberg storage. While that work established high-throughput read access and metadata pruning, enterprise frontline workflows require a complete operational loop: the ability to persist frontline spreadsheet edits, row additions, and deletions back to the analytical lakehouse.&lt;/p&gt;

&lt;p&gt;Commercial Reverse ETL SaaS platforms bridge this writeback gap only at the cost of steep recurring subscriptions ($1,500–$5,000/month), third-party data egress risks, and high operational complexity. This article introduces a &lt;strong&gt;100% serverless, zero-maintenance bidirectional writeback architecture&lt;/strong&gt; built upon &lt;strong&gt;IcebergApp&lt;/strong&gt; and Google Apps Script (GAS). Note that while the underlying IcebergApp library natively supports arbitrary binary data (&lt;code&gt;BYTES&lt;/code&gt;), this writeback architecture and test suite operate on Google Sheets and are specifically designed for structured tabular text and numerical data. By coupling an in-memory Change Data Capture (CDC) engine with BigQuery’s distributed compute accelerator, this system empowers end users to interactively query, validate, edit, and commit differential mutations directly to open &lt;strong&gt;Apache Iceberg&lt;/strong&gt; tables on Google Cloud Storage. The architecture features an atomic single-query &lt;code&gt;MERGE INTO&lt;/code&gt; SQL synthesis with explicit type casting and Optimistic Concurrency Control (OCC) equipped with microsecond-discrepancy tolerance (&lt;code&gt;TIMESTAMP_DIFF&lt;/code&gt;), a Privacy Mode toggle for screencasts and video demonstrations, deterministic primary-key sorting (&lt;code&gt;ORDER BY id ASC&lt;/code&gt;), a 100,000-cell browser safety guard, an interactive dark-themed sidebar console with dynamic execution badges and in-app guidance, a fully autonomous headless test suite guaranteeing zero-residue lifecycle cleanup, and an unedited 77-second end-to-end video demonstration showcasing the complete 10-step operational lifecycle.&lt;/p&gt;


&lt;h2&gt;
  
  
  1. Introduction: The Operational Gap in Modern Lakehouses
&lt;/h2&gt;

&lt;p&gt;Modern enterprise analytical architectures have standardized on open table formats, with &lt;a href="https://iceberg.apache.org/" rel="noopener noreferrer"&gt;Apache Iceberg&lt;/a&gt; emerging as the dominant standard across Google Cloud, AWS, and modern data stacks. By decoupling physical Parquet storage on Google Cloud Storage (GCS) from compute engines, Apache Iceberg provides ACID transactions, partition evolution, snapshot isolation, and sub-second Predicate Pushdown.&lt;/p&gt;

&lt;p&gt;Yet, a critical disconnect persists on the enterprise frontlines: &lt;strong&gt;business operators live in spreadsheets, while analytical data lives in lakehouses.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fy1b8dhcn9g9bvfkhdse0.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fy1b8dhcn9g9bvfkhdse0.jpg" alt="Figure 1: The Enterprise Reality Gap: Frontline Spreadsheets vs. Modern Lakehouse" width="800" height="447"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Figure 1: The Enterprise Reality Gap: Frontline Spreadsheets vs. Modern Lakehouse. Illustrates the operational chasm between agile frontline business tasks in Google Workspace and immutable analytical storage in Apache Iceberg on Google Cloud, bridged seamlessly by IcebergApp's serverless bidirectional writeback engine.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Architectural Narrative for Figure 1:
&lt;/h3&gt;

&lt;p&gt;As illustrated in &lt;strong&gt;Figure 1&lt;/strong&gt;, the modern enterprise data landscape is sharply divided into two disconnected environments:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Left Panel (Frontline Operational Workflows)&lt;/strong&gt;: Business users work dynamically within Google Workspace (Google Sheets), performing daily operational adjustments such as master data corrections, price and discount overrides, IoT telemetry calibration offsets, and data anomaly removals. These operations require immediate visual feedback and frictionless spreadsheet manipulation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Right Panel (Enterprise Analytical Storage)&lt;/strong&gt;: Core analytical assets reside in Google Cloud Storage as Apache Iceberg tables composed of immutable Parquet data files, hierarchical Avro manifest trees, and snapshot commit logs managed by BigQuery's distributed MPP query engine.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Central Chasm &amp;amp; The IcebergApp Bridge&lt;/strong&gt;: Between these two worlds stands an operational disconnect. Traditional tools like Google Connected Sheets provide only a unidirectional, read-only window, while third-party Reverse ETL SaaS platforms impose severe cost penalties ($1,500–$5,000/month) and enterprise compliance egress risks. IcebergApp functions as the vital serverless bidirectional bridge, enabling frontline operators to commit verified, sub-second ACID mutations directly to Parquet storage without intermediate SaaS platforms or persistent server infrastructure.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Consider common frontline operations: a pricing manager adjusts discount thresholds based on supply chain volatility; an operations engineer enters calibration offsets for industrial IoT sensors; or a master data steward flags and deletes corrupted duplicate records. In every modern organization, these decisions occur visually within Google Sheets.&lt;/p&gt;
&lt;h3&gt;
  
  
  1.1 The Connected Sheets Wall: The Unidirectional Bottleneck
&lt;/h3&gt;

&lt;p&gt;Google Cloud addressed petabyte-scale spreadsheet querying by introducing &lt;a href="https://cloud.google.com/bigquery/docs/connected-sheets" rel="noopener noreferrer"&gt;Google Connected Sheets&lt;/a&gt;. Connected Sheets connects spreadsheets directly to BigQuery, enabling non-technical users to build pivot tables, charts, and formulas over billions of rows without writing SQL.&lt;/p&gt;

&lt;p&gt;However, Connected Sheets is &lt;strong&gt;strictly unidirectional (Read-Only)&lt;/strong&gt;. If a business operator identifies a mispriced SKU, corrects an inaccurate telemetry reading, or deletes an invalid row in the spreadsheet grid, Connected Sheets offers no native pathway to write those mutations back to BigQuery or the underlying Apache Iceberg catalog. The user is forced to request data engineering tickets, manually export CSV files, or rely on ad-hoc scripts.&lt;/p&gt;
&lt;h3&gt;
  
  
  1.2 The SaaS Reverse ETL Tax &amp;amp; Compliance Egress
&lt;/h3&gt;

&lt;p&gt;To solve this writeback dilemma, enterprises frequently turn to commercial Reverse ETL platforms (e.g., Census, Hightouch, Coefficient). While functional, these platforms introduce severe architectural drawbacks:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Extravagant Subscription Overhead&lt;/strong&gt;: Enterprise Reverse ETL platforms typically cost between $1,500 and $5,000 per month, introducing substantial recurring operational expenditure for what is fundamentally periodic tabular synchronization.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data Sovereignty &amp;amp; Compliance Egress&lt;/strong&gt;: Synchronizing data via third-party SaaS vendors requires granting external platforms read and write access to internal datasets, transmitting confidential enterprise records outside the Google Cloud Virtual Private Cloud (VPC) and violating strict regulatory frameworks (GDPR, HIPAA, SOC2).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lakehouse Metadata Ignorance&lt;/strong&gt;: Most Reverse ETL tools operate at the generic SQL table layer and lack native understanding of Apache Iceberg metadata, triggering frequent small-file updates, manifest fragmentation, and uncoordinated snapshot generation.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  1.3 The Solution: Serverless Bidirectional Lakehouse Writeback
&lt;/h3&gt;

&lt;p&gt;Building directly upon the foundational &lt;strong&gt;&lt;a href="https://github.com/tanaikech/IcebergApp" rel="noopener noreferrer"&gt;IcebergApp&lt;/a&gt;&lt;/strong&gt; engine established in our published companion article (&lt;em&gt;&lt;a href="https://dev.to/gde/unifying-google-workspace-and-apache-iceberg-serverless-lakehouse-management-ep3"&gt;Unifying Google Workspace and Apache Iceberg: Serverless Lakehouse Management&lt;/a&gt;&lt;/em&gt;), this work presents an end-to-end, container-bound Google Sheets application (&lt;a href="https://github.com/tanaikech/IcebergApp/tree/master/applications/writeback-sheets-ui" rel="noopener noreferrer"&gt;&lt;code&gt;applications/writeback-sheets-ui&lt;/code&gt;&lt;/a&gt;). Operating entirely within the Google Cloud and Google Workspace ecosystem, this architecture delivers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Zero-Infrastructure Cost&lt;/strong&gt;: Runs 100% serverlessly inside Google Apps Script (GAS) and BigQuery on-demand compute; $0 in monthly SaaS subscription fees.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Strict Data Sovereignty&lt;/strong&gt;: All data remains strictly within customer-owned BigQuery datasets and Google Cloud Storage buckets. Zero third-party data egress.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Differential Change Data Capture (CDC)&lt;/strong&gt;: Accurately isolates &lt;code&gt;ADDED&lt;/code&gt;, &lt;code&gt;MODIFIED&lt;/code&gt;, and &lt;code&gt;DELETED&lt;/code&gt; records by comparing the active spreadsheet grid against an immutable hidden baseline, with floating-point numeric tolerance to eliminate false diffs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Atomic Single-Query &lt;code&gt;MERGE INTO&lt;/code&gt;&lt;/strong&gt;: Consolidates all mutations into a single optimized BigQuery &lt;code&gt;MERGE INTO&lt;/code&gt; statement with explicit type casting and Optimistic Concurrency Control (OCC).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OCC with Microsecond Discrepancy Tolerance&lt;/strong&gt;: Evaluates second-level delta assertions (&lt;code&gt;TIMESTAMP_DIFF(T.updated_at, S._orig_updated_at, SECOND) = 0&lt;/code&gt;) to seamlessly bridge the microsecond precision disparity between BigQuery ($10^{-6}$s) and Google Sheets ($10^{-3}$s) while strictly intercepting concurrent background overwrites.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Privacy Mode for Video &amp;amp; Public Demonstrations&lt;/strong&gt;: One-click DOM masking toggle (&lt;code&gt;[🛡️ Privacy: OFF]&lt;/code&gt; ⇄ &lt;code&gt;[🔒 Privacy: ON]&lt;/code&gt;) to mask sensitive GCP Project IDs, Dataset IDs, Bucket URIs, and queries with bullets (&lt;code&gt;••••••••••••&lt;/code&gt;), enabling confidential screencasting while maintaining 100% backend execution fidelity.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deterministic Primary-Key Ordering&lt;/strong&gt;: Guarantees ascending primary-key sort order (&lt;code&gt;ORDER BY id ASC&lt;/code&gt;) across both predicate pushdown queries and post-commit inspections.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dual Operational Modes&lt;/strong&gt;: A polished, dark-themed interactive console (&lt;code&gt;Sidebar.html&lt;/code&gt;) featuring dynamic step badges, inline results, and in-app guidance for business operators, alongside an autonomous headless test suite (&lt;code&gt;HeadlessTest.js&lt;/code&gt;) for CI/CD and engineering automation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Text &amp;amp; Tabular Data Specification&lt;/strong&gt;: While the core &lt;code&gt;IcebergApp&lt;/code&gt; library natively supports arbitrary binary payloads (&lt;code&gt;BYTES&lt;/code&gt; for PDFs, images, audio, and vector embeddings as explored in Article 2), this writeback architecture and test suite are specifically designed around the tabular grid characteristics of Google Sheets, focusing on &lt;strong&gt;structured text, numerical, and timestamp data only&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  2. Technical Architecture &amp;amp; Component Interaction
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0vh1e4cud9yp1ib2o868.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0vh1e4cud9yp1ib2o868.jpg" alt="Figure 2: Technical Architecture: Google Sheets &amp;amp; Apache Iceberg Bidirectional Writeback" width="800" height="447"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Figure 2: End-to-end technical architecture of the Google Sheets to Apache Iceberg bidirectional writeback system. Displays the Google Sheets presentation tier, Apps Script CDC engine, BigQuery SQL accelerator, and Google Cloud Storage open lakehouse tier.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;As visualized in &lt;strong&gt;Figure 2&lt;/strong&gt;, the architecture cleanly decouples the spreadsheet presentation layer from serverless compute and open lakehouse storage across four synchronized tiers:&lt;/p&gt;
&lt;h3&gt;
  
  
  Architectural Narrative for Figure 2:
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Figure 2&lt;/strong&gt; details the multi-tier operational coordination between Google Workspace and Google Cloud:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Tier 1: Presentation Tier (Google Sheets Ecosystem)&lt;/strong&gt;: Frontline users operate within standard Google Sheets tabs (&lt;code&gt;queried_data&lt;/code&gt;, &lt;code&gt;current_data&lt;/code&gt;) reinforced with native Data Validation rules. The interface is powered by &lt;code&gt;Sidebar.html&lt;/code&gt;, rendering real-time execution badges, animated status spinners, an in-app editing guide, and an on-the-fly Privacy Mode toggle for secure screen broadcasting.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tier 2: Orchestration &amp;amp; CDC Engine (Google Apps Script)&lt;/strong&gt;: Operating inside the serverless Apps Script V8 runtime, &lt;code&gt;Code.js&lt;/code&gt; manages state transitions, tracks active edits against the hidden &lt;code&gt;__iceberg_baseline__&lt;/code&gt; sheet, applies floating-point tolerance filters, and synthesizes dynamic SQL payloads without requiring an intermediary backend server.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tier 3: Distributed Compute Accelerator (BigQuery)&lt;/strong&gt;: BigQuery executes dynamic DDL/DML, interacts with the Iceberg REST Catalog, handles partition pruning, and atomically commits &lt;code&gt;MERGE INTO&lt;/code&gt; operations with OCC timestamp assertions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tier 4: Open Lakehouse Storage (Google Cloud Storage)&lt;/strong&gt;: Apache Iceberg stores immutable columnar Parquet data files and Avro metadata trees directly in customer-controlled Cloud Storage buckets. Because data remains in open Iceberg format, external distributed engines (Apache Spark, Trino, DuckDB) can read and write concurrently without vendor lock-in.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  2.1 Component Breakdown
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Google Sheets Presentation Layer (&lt;code&gt;Sidebar.html&lt;/code&gt; &amp;amp; Sheets Ecosystem)&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Custom Toolbar Menu&lt;/strong&gt;: &lt;code&gt;[Iceberg Lakehouse] -&amp;gt; [Open Lakehouse Console]&lt;/code&gt; provides immediate discovery.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Modern Dark-Themed Sidebar UI&lt;/strong&gt;: Styled with deep slate tones (&lt;code&gt;#0f172a&lt;/code&gt;, &lt;code&gt;#1e293b&lt;/code&gt;) and cyber-blue accents (&lt;code&gt;#38bdf8&lt;/code&gt;), rendering real-time step lifecycle badges (&lt;code&gt;PENDING&lt;/code&gt;, &lt;code&gt;RUNNING&lt;/code&gt;, &lt;code&gt;COMPLETED&lt;/code&gt;, &lt;code&gt;FAILED&lt;/code&gt;), inline execution summaries, interactive cards, animated spinners, toast alerts, a one-click Privacy Mode toggle (&lt;code&gt;[🛡️ Privacy: OFF]&lt;/code&gt; ⇄ &lt;code&gt;[🔒 Privacy: ON]&lt;/code&gt;), an interactive in-app test guidance modal, and direct hyperlinks to the upstream GitHub repository.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tabular Sheets&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;default_data&lt;/code&gt;: Staging area for initial sample records (5 columns $\times$ 20 rows).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;queried_data&lt;/code&gt;: Active working sheet populated with query results and reinforced with native Google Sheets Data Validation rules.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;current_data&lt;/code&gt;: Full lakehouse inspection sheet guarded by a 100,000-cell boundary limit and deterministically sorted by primary key (&lt;code&gt;id ASC&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;__iceberg_baseline__&lt;/code&gt;: Hidden, protected sheet holding the exact, immutable ground-truth snapshot of the most recent query.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Google Apps Script Change Data Capture (CDC) Engine (&lt;code&gt;Code.js&lt;/code&gt;)&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;Dynamic header-to-schema resolution, providing resilience against column reordering in the sheet.&lt;/li&gt;
&lt;li&gt;In-memory primary key (&lt;code&gt;id&lt;/code&gt;) index comparison between active cells and baseline cells.&lt;/li&gt;
&lt;li&gt;Floating-point numeric tolerance ($&amp;lt; 10^{-9}$) to eliminate false diffs caused by display formatting.&lt;/li&gt;
&lt;li&gt;Synthesis of atomic BigQuery &lt;code&gt;MERGE INTO&lt;/code&gt; SQL with explicit casting (&lt;code&gt;CAST(val AS TYPE)&lt;/code&gt;) and string sanitization.&lt;/li&gt;
&lt;li&gt;Optimistic Concurrency Control (OCC) assertions checking row-level timestamps via &lt;code&gt;TIMESTAMP_DIFF(T.updated_at, S._orig_updated_at, SECOND) = 0&lt;/code&gt; to prevent microsecond roundoff conflicts.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;BigQuery Lakehouse Compute Accelerator&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;Executes dynamic DDL (&lt;code&gt;CREATE OR REPLACE TABLE&lt;/code&gt;) and DML (&lt;code&gt;MERGE INTO&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Serves as the distributed query engine that interfaces with the Iceberg REST Catalog, evaluating partition specifications and metadata pruning.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Apache Iceberg Open Lakehouse (Google Cloud Storage)&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;Houses open Apache Parquet data files and Avro metadata manifests (&lt;code&gt;*.metadata.json&lt;/code&gt;, &lt;code&gt;*.avro&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Maintains transactional snapshot history, enabling point-in-time Time Travel and multi-engine concurrent queries via Spark, Trino, and BigQuery without vendor lock-in.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;


&lt;h2&gt;
  
  
  3. The Algorithmic Core: Differential CDC &amp;amp; Atomic &lt;code&gt;MERGE INTO&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The core innovation of this writeback engine is the transformation of visual spreadsheet cell edits into a single, ACID-compliant database commit.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbmc4h8f90migq0xgtjfj.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbmc4h8f90migq0xgtjfj.jpg" alt="Figure 3: Differential Change Data Capture (CDC) vs Atomic MERGE INTO for Apache Iceberg" width="800" height="447"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Figure 3: Algorithmic workflow of the Differential CDC Engine and Atomic MERGE INTO synthesis. Compares active sheet rows against the hidden baseline snapshot and generates an atomic BigQuery SQL query with explicit casting and OCC timestamp validation.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Algorithmic Narrative for Figure 3:
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Figure 3&lt;/strong&gt; illustrates the five-phase algorithmic lifecycle that transforms frontline visual spreadsheet edits into an atomic, ACID-compliant database commit:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Phase 1 (Baseline Snapshotting)&lt;/strong&gt;: When rows are queried from the lakehouse via &lt;code&gt;IcebergApp.getValues()&lt;/code&gt;, the active working dataset is populated into &lt;code&gt;queried_data&lt;/code&gt;, and an exact, immutable clone is captured in the hidden &lt;code&gt;__iceberg_baseline__&lt;/code&gt; sheet, locked against user edits.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Phase 2 (Frontline Visual Manipulation)&lt;/strong&gt;: The business operator modifies existing cells, appends new rows with unique IDs, or deletes obsolete rows directly in the spreadsheet grid.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Phase 3 (In-Memory CDC Diffing)&lt;/strong&gt;: Clicking "Commit Changes (Writeback)" triggers the CDC engine, which indexes baseline and active rows by primary key (&lt;code&gt;id&lt;/code&gt;), compares column hashes with numeric floating-point tolerance ($&amp;lt; 10^{-9}$), and partitions mutations into &lt;code&gt;ADDED&lt;/code&gt;, &lt;code&gt;MODIFIED&lt;/code&gt;, and &lt;code&gt;DELETED&lt;/code&gt; sets.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Phase 4 (Atomic Single-Query MERGE INTO Synthesis)&lt;/strong&gt;: Rather than dispatching separate HTTP requests for each mutated row—which would cause timeouts and manifest fragmentation—the engine serializes all mutation sets into a single BigQuery &lt;code&gt;MERGE INTO&lt;/code&gt; statement with an in-memory &lt;code&gt;UNION ALL&lt;/code&gt; source table, explicit SQL type casting, and second-level OCC timestamp matching (&lt;code&gt;TIMESTAMP_DIFF(T.updated_at, S._orig_updated_at, SECOND) = 0&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Phase 5 (ACID Commit &amp;amp; Baseline Synchronization)&lt;/strong&gt;: BigQuery executes the atomic transaction against Cloud Storage, updates Iceberg snapshot manifests, verifies that &lt;code&gt;numDmlAffectedRows&lt;/code&gt; matches expected mutations, synchronizes &lt;code&gt;__iceberg_baseline__&lt;/code&gt; to the committed state, and alerts the operator via a success badge.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  3.1 The Baseline Snapshot Strategy
&lt;/h3&gt;

&lt;p&gt;When an operator queries the lakehouse (e.g., via &lt;code&gt;stepExecuteQuery&lt;/code&gt;), the returned 2D array is rendered into &lt;code&gt;queried_data&lt;/code&gt;. Simultaneously, an identical clone is written into &lt;code&gt;__iceberg_baseline__&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This baseline sheet is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Marked with Google Sheets protection to prevent accidental edits.&lt;/li&gt;
&lt;li&gt;Programmatically hidden from the workbook tab bar (&lt;code&gt;baselineSheet.hideSheet()&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Treated as the immutable ground-truth state against which all subsequent edits are measured.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  3.2 In-Memory CDC Differentiation Algorithm
&lt;/h3&gt;

&lt;p&gt;When the operator clicks &lt;strong&gt;"Commit Changes (Writeback)"&lt;/strong&gt;, the CDC engine in &lt;code&gt;Code.js&lt;/code&gt; executes the following algorithm:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Step 1: Index baseline rows by Primary Key (id)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;baselineMap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;baselineValues&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;baselineValues&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;baselineIdIdx&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pk&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;pk&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;baselineMap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pk&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;rowToRecord_&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;baselineHeaders&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Step 2: Scan active sheet for Added and Modified rows&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;addedRows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;modifiedRows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;activePkSet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nowIso&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toISOString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;activeValues&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;activeValues&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;activeIdIdx&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;pk&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pkStr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pk&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;activePkSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pkStr&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;activeRecord&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;rowToRecord_&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;activeHeaders&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;baselineMap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pkStr&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ADDED Set: PK exists in active sheet, but absent in baseline&lt;/span&gt;
    &lt;span class="nx"&gt;activeRecord&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;updated_at&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;activeRecord&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;updated_at&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;nowIso&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;addedRows&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;activeRecord&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// MODIFIED Set: PK exists in both; verify column-level value hash&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;baseRecord&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;baselineMap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pkStr&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isRecordModified_&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;activeRecord&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;baseRecord&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;activeRecord&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_orig_updated_at&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;baseRecord&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;updated_at&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Capture for OCC&lt;/span&gt;
      &lt;span class="nx"&gt;activeRecord&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;updated_at&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;nowIso&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// New commit timestamp&lt;/span&gt;
      &lt;span class="nx"&gt;modifiedRows&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;activeRecord&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Step 3: Scan baseline for Deleted rows&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;deletedRows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;pkStr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;baseRecord&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;baselineMap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;activePkSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pkStr&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// DELETED Set: PK exists in baseline, but absent in active sheet&lt;/span&gt;
    &lt;span class="nx"&gt;baseRecord&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_orig_updated_at&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;baseRecord&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;updated_at&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;deletedRows&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;baseRecord&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3.3 Atomic &lt;code&gt;MERGE INTO&lt;/code&gt; SQL Synthesis
&lt;/h3&gt;

&lt;p&gt;Rather than executing individual &lt;code&gt;INSERT&lt;/code&gt;, &lt;code&gt;UPDATE&lt;/code&gt;, and &lt;code&gt;DELETE&lt;/code&gt; statements across multiple network roundtrips—which would cause Google Apps Script timeouts and create dozens of fragmented Parquet commit files—the engine consolidates all mutations into a single, atomic BigQuery &lt;code&gt;MERGE INTO&lt;/code&gt; query.&lt;/p&gt;

&lt;p&gt;Every record across the Added, Modified, and Deleted sets is serialized into an in-memory &lt;code&gt;UNION ALL&lt;/code&gt; source table constructor with &lt;strong&gt;explicit SQL type casting&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;MERGE&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="nv"&gt;`my-project.my_dataset.products`&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;
&lt;span class="k"&gt;USING&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="c1"&gt;-- ADDED RECORD: INSERT&lt;/span&gt;
  &lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="k"&gt;CAST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;121&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;INT64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;CAST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Quantum Frequency Comb'&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;STRING&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;CAST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6200&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;FLOAT64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;CAST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;INT64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;stock&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nb"&gt;TIMESTAMP&lt;/span&gt; &lt;span class="s1"&gt;'2026-09-07T06:15:00.000Z'&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;updated_at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'INSERT'&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;_action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nb"&gt;TIMESTAMP&lt;/span&gt; &lt;span class="s1"&gt;'1970-01-01T00:00:00Z'&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;_orig_updated_at&lt;/span&gt;
  &lt;span class="k"&gt;UNION&lt;/span&gt; &lt;span class="k"&gt;ALL&lt;/span&gt;
  &lt;span class="c1"&gt;-- MODIFIED RECORD: UPDATE&lt;/span&gt;
  &lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="k"&gt;CAST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;105&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;INT64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;CAST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Photonic Switch Array'&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;STRING&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;CAST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2550&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;FLOAT64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;CAST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;INT64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;stock&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nb"&gt;TIMESTAMP&lt;/span&gt; &lt;span class="s1"&gt;'2026-09-07T06:15:00.000Z'&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;updated_at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'UPDATE'&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;_action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nb"&gt;TIMESTAMP&lt;/span&gt; &lt;span class="s1"&gt;'2026-09-07T00:00:00.000Z'&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;_orig_updated_at&lt;/span&gt;
  &lt;span class="k"&gt;UNION&lt;/span&gt; &lt;span class="k"&gt;ALL&lt;/span&gt;
  &lt;span class="c1"&gt;-- DELETED RECORD: DELETE&lt;/span&gt;
  &lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="k"&gt;CAST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;110&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;INT64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;CAST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;STRING&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;CAST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;FLOAT64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;CAST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;INT64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;stock&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nb"&gt;TIMESTAMP&lt;/span&gt; &lt;span class="s1"&gt;'1970-01-01T00:00:00Z'&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;updated_at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'DELETE'&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;_action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nb"&gt;TIMESTAMP&lt;/span&gt; &lt;span class="s1"&gt;'2026-09-07T00:00:00.000Z'&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;_orig_updated_at&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;S&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;
&lt;span class="c1"&gt;-- 1. UPDATE MATCHED ROW WITH OCC TIMESTAMP GUARD (SECOND-LEVEL TOLERANCE)&lt;/span&gt;
&lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;MATCHED&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_action&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'UPDATE'&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TIMESTAMP_DIFF&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;updated_at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_orig_updated_at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;SECOND&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="n"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_orig_updated_at&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt;
  &lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stock&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;updated_at&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;updated_at&lt;/span&gt;
&lt;span class="c1"&gt;-- 2. DELETE MATCHED ROW WITH OCC TIMESTAMP GUARD (SECOND-LEVEL TOLERANCE)&lt;/span&gt;
&lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;MATCHED&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_action&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'DELETE'&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TIMESTAMP_DIFF&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;updated_at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_orig_updated_at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;SECOND&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="n"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_orig_updated_at&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt;
  &lt;span class="k"&gt;DELETE&lt;/span&gt;
&lt;span class="c1"&gt;-- 3. INSERT UNMATCHED NEW ROW&lt;/span&gt;
&lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="n"&gt;MATCHED&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_action&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'INSERT'&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt;
  &lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stock&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;updated_at&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stock&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;updated_at&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3.4 Optimistic Concurrency Control (OCC) Protection &amp;amp; Microsecond Precision Tolerance
&lt;/h3&gt;

&lt;p&gt;In enterprise environments, multiple automated ETL pipelines, IoT streaming jobs, or peer users may modify the same lakehouse table concurrently. To prevent the classic "lost update" anomaly, the &lt;code&gt;MERGE INTO&lt;/code&gt; statement incorporates &lt;strong&gt;Optimistic Concurrency Control (OCC)&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When an active row is modified or deleted, its original timestamp (&lt;code&gt;_orig_updated_at&lt;/code&gt;) captured during query time is injected into the &lt;code&gt;WHEN MATCHED&lt;/code&gt; clause.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Microsecond Discrepancy Challenge&lt;/strong&gt;: BigQuery stores TIMESTAMP values with microsecond precision ($10^{-6}$s) and returns them via its REST API as floating-point epoch seconds (e.g., &lt;code&gt;"1700000000.123456"&lt;/code&gt;). Conversely, Google Sheets and standard JavaScript &lt;code&gt;Date&lt;/code&gt; objects operate at millisecond precision ($10^{-3}$s). A naive strict equality comparison (&lt;code&gt;T.updated_at = S._orig_updated_at&lt;/code&gt;) invariably causes false concurrency conflicts even on completely untouched rows due to sub-millisecond truncation and floating-point roundoff.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resolution via &lt;code&gt;TIMESTAMP_DIFF&lt;/code&gt;&lt;/strong&gt;: The OCC condition enforces second-level equality:
&lt;code&gt;TIMESTAMP_DIFF(T.updated_at, S._orig_updated_at, SECOND) = 0&lt;/code&gt;
Coupled with automatic ISO 8601 normalization in &lt;code&gt;IcebergApp.js&lt;/code&gt; and flexible epoch parsing in &lt;code&gt;Code.js&lt;/code&gt;, this provides ironclad concurrency protection against actual external updates while completely eliminating false collision errors.&lt;/li&gt;
&lt;li&gt;If an external process modifies that row between query and writeback, &lt;code&gt;TIMESTAMP_DIFF&lt;/code&gt; evaluates to non-zero. The row mutation is skipped, causing BigQuery's reported &lt;code&gt;numDmlAffectedRows&lt;/code&gt; to fall below the expected count (&lt;code&gt;added.length + modified.length + deleted.length&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;The script immediately intercepts this discrepancy and throws an actionable Concurrency Conflict exception, preserving database integrity.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  4. Interactive UI Walkthrough: The 10-Step Operational Lifecycle
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyz41atrbuo5h6ivhg6w3.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyz41atrbuo5h6ivhg6w3.jpg" alt="Figure 4: The 10-Step Operational Lifecycle: Interactive Lakehouse Writeback in Google Sheets" width="800" height="447"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Figure 4: The 10-Step Operational Lifecycle: Interactive Lakehouse Writeback in Google Sheets. Illustrates the end-to-end operational journey across 10 progressive phases from container-bound script binding to zero-residue cloud teardown.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Operational Narrative for Figure 4:
&lt;/h3&gt;

&lt;p&gt;As visualized in the 10-step workflow of &lt;strong&gt;Figure 4&lt;/strong&gt;, the system orchestrates a structured, self-guided operational journey arranged across two interconnected rows of five progressive phases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Phase 1: Environment Setup &amp;amp; Interface Initialization (Steps 1–3)&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Step 1 (Script Binding &amp;amp; Setup)&lt;/strong&gt;: Developers bind the foundational &lt;code&gt;IcebergApp.js&lt;/code&gt; library, application backend &lt;code&gt;Code.js&lt;/code&gt;, frontend &lt;code&gt;Sidebar.html&lt;/code&gt;, and &lt;code&gt;HeadlessTest.js&lt;/code&gt; into the Google Spreadsheet container, enabling the BigQuery v2 Advanced Service in &lt;code&gt;appsscript.json&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Step 2 (Custom Menu &amp;amp; GCP Config)&lt;/strong&gt;: The spreadsheet initializes a top toolbar menu &lt;code&gt;[Iceberg Lakehouse] -&amp;gt; [Open Lakehouse Console]&lt;/code&gt; and prompts the operator for GCP Project ID and Region, securely caching them in &lt;code&gt;UserProperties&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Step 3 (Sidebar Console &amp;amp; Privacy Mode)&lt;/strong&gt;: The modern dark console launches in the right sidebar, offering dynamic lifecycle badges (&lt;code&gt;PENDING&lt;/code&gt;, &lt;code&gt;RUNNING&lt;/code&gt;, &lt;code&gt;COMPLETED&lt;/code&gt;, &lt;code&gt;FAILED&lt;/code&gt;) and a one-click Privacy Mode toggle (&lt;code&gt;[🛡️ Privacy: OFF]&lt;/code&gt; ⇄ &lt;code&gt;[🔒 Privacy: ON]&lt;/code&gt;) for confidential video broadcasting.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Phase 2: Cloud Infrastructure &amp;amp; Table Provisioning (Steps 4–5)&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Step 4 (Infrastructure Provisioning)&lt;/strong&gt;: Automated background routines provision an isolated BigQuery dataset and Cloud Storage bucket, while staging 20 sample product records into &lt;code&gt;default_data&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Step 5 (Iceberg Table Creation)&lt;/strong&gt;: &lt;code&gt;IcebergApp.create()&lt;/code&gt; synthesizes the Iceberg table with &lt;code&gt;DATE(updated_at)&lt;/code&gt; partitioning and &lt;code&gt;id&lt;/code&gt; clustering, streaming initial records into Parquet storage.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Phase 3: Predicate Pushdown &amp;amp; Frontline Manipulation (Steps 6–7)&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Step 6 (Query &amp;amp; Sheet Validation)&lt;/strong&gt;: Executes filtered lakehouse queries with predicate pushdown into &lt;code&gt;queried_data&lt;/code&gt;, injects native Google Sheets Data Validation rules, clones an immutable baseline snapshot into &lt;code&gt;__iceberg_baseline__&lt;/code&gt;, and launches an in-app test guidance modal.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Step 7 (Visual In-Sheet Editing)&lt;/strong&gt;: Business users modify prices or stock, append new rows with unique primary keys in column A, or delete obsolete rows directly on the spreadsheet grid.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Phase 4: ACID Commit, Full Audit &amp;amp; Zero-Residue Purge (Steps 8–10)&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Step 8 (Differential Writeback Commit)&lt;/strong&gt;: The CDC engine computes diffs, synthesizes a single atomic &lt;code&gt;MERGE INTO&lt;/code&gt; SQL query with microsecond-tolerant OCC (&lt;code&gt;TIMESTAMP_DIFF&lt;/code&gt;), and executes sub-second commit to BigQuery.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Step 9 (Full Inspection &amp;amp; 100k Safeguard)&lt;/strong&gt;: Evaluates dataset volume against the 100,000-cell browser safety limit and exports all lakehouse rows to &lt;code&gt;current_data&lt;/code&gt; sorted deterministically by primary key (&lt;code&gt;id ASC&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Step 10 (Clean Reset &amp;amp; Zero-Residue Purge)&lt;/strong&gt;: Drops the Iceberg table, purges GCS bucket objects, deletes the bucket and dataset, and clears spreadsheet tabs while preserving GCP project configuration for instant re-testing.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Live Demonstration Video: The Complete 10-Step Lifecycle in Action (77s)
&lt;/h3&gt;

&lt;p&gt;The following screencast video demonstration captures the entire unedited operational lifecycle (77 seconds) directly within Google Sheets and the right-docked "Iceberg Lakehouse Console" sidebar—from infrastructure initialization, Iceberg table provisioning, and predicate pushdown querying, to frontline grid modifications (in-place edits and row deletions), differential CDC commit via atomic &lt;code&gt;MERGE INTO&lt;/code&gt;, full data inspection, and zero-residue environment teardown.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/3ZOjRXSMr4w" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Live Demonstration Video: End-to-end execution of the interactive Apache Iceberg bidirectional writeback console in Google Sheets (77s). &lt;a href="https://tanaikech.github.io/image-storage/20260908b/demo.mp4" rel="noopener noreferrer"&gt;▶️ Open raw video file (demo.mp4)&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Video Timeline &amp;amp; Operational Highlights:
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Timestamp&lt;/th&gt;
&lt;th&gt;Operational Phase &amp;amp; Action&lt;/th&gt;
&lt;th&gt;On-Screen Behavior &amp;amp; Backend Execution&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;0:00 - 0:10&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Step 1: Setup &amp;amp; Staging&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Privacy Mode ON&lt;/strong&gt; (automatically masking GCP Project IDs and Dataset names as &lt;code&gt;••••••••••&lt;/code&gt;). The user clicks &lt;code&gt;Initialize Infrastructure&lt;/code&gt;, provisioning an ephemeral BigQuery dataset and GCS bucket, and staging 20 sample rows into &lt;code&gt;default_data&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;0:10 - 0:20&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Step 2: Table Creation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The user clicks &lt;code&gt;Create Iceberg Table&lt;/code&gt;. &lt;code&gt;IcebergApp.create()&lt;/code&gt; generates the Apache Iceberg table on GCS and commits the initial 20 rows in Parquet format. The sidebar query textarea is pre-populated with a filtered query.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;0:20 - 0:32&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Step 3: Query &amp;amp; Validate&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The user executes the query (&lt;code&gt;WHERE price &amp;gt; 1000.0 ORDER BY id ASC&lt;/code&gt;). Predicate pushdown loads matching records into &lt;code&gt;queried_data&lt;/code&gt;, injects native column validation rules (positive numbers, integers), and clones the baseline.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;0:32 - 0:48&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Frontline Grid Manipulation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Direct editing on &lt;code&gt;queried_data&lt;/code&gt;: &lt;br&gt;• Row 2 (&lt;code&gt;id: 101&lt;/code&gt;): Updates product name to &lt;code&gt;Sample 1&lt;/code&gt; and price to &lt;code&gt;123&lt;/code&gt;.&lt;br&gt;• Row 3 (&lt;code&gt;id: 103&lt;/code&gt;): Updates product name to &lt;code&gt;Sample 2&lt;/code&gt; and price to &lt;code&gt;456&lt;/code&gt;.&lt;br&gt;• Row 4 (&lt;code&gt;id: 104&lt;/code&gt;): Right-clicks and selects "Delete row", removing &lt;code&gt;Superconducting Resonator&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;0:48 - 0:55&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Step 4: Differential Writeback&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The user clicks &lt;code&gt;Commit Changes (Writeback)&lt;/code&gt;. In-memory CDC detects diffs instantly and synthesizes a single &lt;code&gt;MERGE INTO&lt;/code&gt; SQL statement with microsecond OCC, committing it to BigQuery.&lt;br&gt;Badge: &lt;code&gt;✅ Done: Committed: 0 added, 2 modified, 1 deleted (Affected: 3 rows)&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;0:55 - 1:08&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Step 5: Full Inspection&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The user clicks &lt;code&gt;Get All Current Data&lt;/code&gt; (enforcing the 100k-cell safety guard). Exports all lakehouse rows to &lt;code&gt;current_data&lt;/code&gt; sorted deterministically by primary key (&lt;code&gt;ORDER BY id ASC&lt;/code&gt;).&lt;br&gt;Confirms &lt;code&gt;id: 101&lt;/code&gt; and &lt;code&gt;103&lt;/code&gt; are updated, &lt;code&gt;104&lt;/code&gt; is purged, and &lt;code&gt;102&lt;/code&gt; &amp;amp; &lt;code&gt;105&lt;/code&gt;+ are intact.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;1:08 - 1:17&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Step 6: Reset &amp;amp; Teardown&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The user clicks &lt;code&gt;All Reset &amp;amp; Purge&lt;/code&gt; (Danger zone). Drops the Iceberg table, deletes all GCS objects and bucket, removes the BigQuery dataset, and clears working tabs—restoring the pristine initial state with zero leftover cloud residue.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The application provides a seamless, self-guided experience directly inside Google Sheets, illustrated in &lt;strong&gt;Figure 4&lt;/strong&gt; and demonstrated in the live screencast:&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Container-Bound Script Setup
&lt;/h3&gt;

&lt;p&gt;The core library (&lt;a href="//../../src/IcebergApp.js"&gt;&lt;code&gt;src/IcebergApp.js&lt;/code&gt;&lt;/a&gt;) and application code files (&lt;a href="//../../applications/writeback-sheets-ui/Code.js"&gt;&lt;code&gt;Code.js&lt;/code&gt;&lt;/a&gt;, &lt;a href="//../../applications/writeback-sheets-ui/Sidebar.html"&gt;&lt;code&gt;Sidebar.html&lt;/code&gt;&lt;/a&gt;, &lt;a href="//../../applications/writeback-sheets-ui/HeadlessTest.js"&gt;&lt;code&gt;HeadlessTest.js&lt;/code&gt;&lt;/a&gt;) are bound directly to the active spreadsheet via &lt;strong&gt;Extensions &amp;gt; Apps Script&lt;/strong&gt; (or linked as a library), with the BigQuery v2 Advanced Service enabled in &lt;code&gt;appsscript.json&lt;/code&gt;. Incorporating &lt;code&gt;src/IcebergApp.js&lt;/code&gt; as the foundational engine is a mandatory prerequisite that provides full-featured Iceberg DDL, metadata-driven predicate pushdown, and transactional table operations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Custom Menu &amp;amp; GCP Initialization
&lt;/h3&gt;

&lt;p&gt;Upon opening the spreadsheet, the &lt;code&gt;onOpen()&lt;/code&gt; hook registers the top menu: &lt;code&gt;[Iceberg Lakehouse] -&amp;gt; [Open Lakehouse Console]&lt;/code&gt;. If GCP properties are not yet set, an input dialog prompts the operator for their &lt;strong&gt;GCP Project ID&lt;/strong&gt; and &lt;strong&gt;Region&lt;/strong&gt; (defaulting to &lt;code&gt;asia-northeast1&lt;/code&gt;), storing them securely in &lt;code&gt;PropertiesService.getUserProperties()&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Modern Dark-Themed Sidebar Console &amp;amp; Privacy Mode
&lt;/h3&gt;

&lt;p&gt;Opening the console displays &lt;code&gt;Sidebar.html&lt;/code&gt; in the right dock. The interface features a status panel showing active GCP parameters, real-time connection badges, a one-click &lt;strong&gt;Privacy Mode toggle&lt;/strong&gt; (&lt;code&gt;[🛡️ Privacy: OFF]&lt;/code&gt; ⇄ &lt;code&gt;[🔒 Privacy: ON]&lt;/code&gt;) to mask sensitive project IDs and SQL queries for video recording, hyperlinks to the upstream GitHub repository, and sequentially numbered action cards with dynamic lifecycle badges (&lt;code&gt;PENDING&lt;/code&gt;, &lt;code&gt;RUNNING&lt;/code&gt;, &lt;code&gt;COMPLETED&lt;/code&gt;, &lt;code&gt;FAILED&lt;/code&gt;). (The embedded screencast demo clearly showcases Privacy Mode enabled throughout execution, ensuring sensitive GCP Project IDs and Dataset names are masked as &lt;code&gt;••••••••••&lt;/code&gt; without impacting backend execution.)&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Infrastructure Provisioning &amp;amp; &lt;code&gt;default_data&lt;/code&gt; Staging
&lt;/h3&gt;

&lt;p&gt;Clicking &lt;strong&gt;"Initialize Infrastructure"&lt;/strong&gt; triggers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ensureBigQueryDataset_&lt;/code&gt;: Creates an isolated BigQuery dataset &lt;code&gt;lakehouse_writeback_[timestamp]&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ensureGcsBucket_&lt;/code&gt;: Creates an isolated Cloud Storage bucket &lt;code&gt;gs://lakehouse-iceberg-wb-[project]-[timestamp]&lt;/code&gt; with Uniform Bucket-Level Access.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;stageDefaultData_&lt;/code&gt;: Creates a clean sheet named &lt;code&gt;default_data&lt;/code&gt; and populates 20 realistic enterprise product records (headers: &lt;code&gt;id&lt;/code&gt;, &lt;code&gt;product&lt;/code&gt;, &lt;code&gt;price&lt;/code&gt;, &lt;code&gt;stock&lt;/code&gt;, &lt;code&gt;updated_at&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;The Step 1 card updates with a green &lt;code&gt;COMPLETED&lt;/code&gt; badge and inline details of the provisioned resources.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 5: Iceberg Table Provisioning
&lt;/h3&gt;

&lt;p&gt;Clicking &lt;strong&gt;"Create Iceberg Table"&lt;/strong&gt; invokes &lt;code&gt;IcebergApp.create()&lt;/code&gt;, defining the table schema, setting the Cloud Storage URI, configuring &lt;code&gt;DATE(updated_at)&lt;/code&gt; partitioning, and setting &lt;code&gt;id&lt;/code&gt; clustering. The 20 staged rows are inserted via &lt;code&gt;table.insertValues()&lt;/code&gt;, the Step 2 card displays a green &lt;code&gt;COMPLETED&lt;/code&gt; badge showing the created table name and ingested row count, and the sidebar query textarea is automatically populated with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="nv"&gt;`project.dataset.products`&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="k"&gt;ASC&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 6: Query Execution &amp;amp; In-Sheet Data Validation Injection
&lt;/h3&gt;

&lt;p&gt;Clicking &lt;strong&gt;"Execute Query"&lt;/strong&gt; runs the query with Predicate Pushdown. The results are written to &lt;code&gt;queried_data&lt;/code&gt;. Simultaneously, the engine injects native &lt;strong&gt;Google Sheets Data Validation rules&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;price&lt;/code&gt;: Locks inputs to positive numbers ($&amp;gt; 0$).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;stock&lt;/code&gt;: Locks inputs to non-negative integers ($\ge 0$).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;id&lt;/code&gt;: Restricts inputs to valid positive integer IDs.
An exact replica is copied to the hidden, protected sheet &lt;code&gt;__iceberg_baseline__&lt;/code&gt;.
&lt;strong&gt;Interactive In-App Guidance&lt;/strong&gt;: Upon query completion, an informative modal dialog automatically appears explaining how to test differential edits (Modify, Delete, Add) in &lt;code&gt;queried_data&lt;/code&gt;. Users click &lt;strong&gt;"Got it! Close &amp;amp; Start Editing"&lt;/strong&gt; to immediately begin testing, with a persistent &lt;code&gt;[View Guide]&lt;/code&gt; button available in the card for subsequent reference.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 7: Visual In-Sheet Manipulation
&lt;/h3&gt;

&lt;p&gt;The business operator performs standard spreadsheet edits in &lt;code&gt;queried_data&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Modify&lt;/strong&gt;: Edits &lt;code&gt;price&lt;/code&gt; or &lt;code&gt;stock&lt;/code&gt; in place (e.g., updates &lt;code&gt;id=105&lt;/code&gt; price to &lt;code&gt;2550.0&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Add&lt;/strong&gt;: Appends a new row at the bottom with a unique ID in column A (e.g., &lt;code&gt;id=121&lt;/code&gt;, &lt;code&gt;"Quantum Frequency Comb"&lt;/code&gt;, price=6200.0, stock=5). Note that primary key &lt;code&gt;id&lt;/code&gt; is strictly mandatory.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Delete&lt;/strong&gt;: Right-clicks and deletes an obsolete row (e.g., deletes &lt;code&gt;id=110&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 8: Differential Writeback Commit
&lt;/h3&gt;

&lt;p&gt;Clicking &lt;strong&gt;"Commit Changes (Writeback)"&lt;/strong&gt; triggers the CDC engine. The diff is calculated, the single atomic &lt;code&gt;MERGE INTO&lt;/code&gt; SQL is synthesized with second-level timestamp tolerance (&lt;code&gt;TIMESTAMP_DIFF(..., SECOND) = 0&lt;/code&gt;) and floating-point tolerance ($10^{-9}$), BigQuery executes the transaction, &lt;code&gt;numDmlAffectedRows&lt;/code&gt; is verified, and &lt;code&gt;__iceberg_baseline__&lt;/code&gt; is updated. A green toast and inline summary confirm:&lt;br&gt;
&lt;code&gt;"Committed successfully: 1 added, 1 updated, 1 deleted (Total DML affected rows: 3)."&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 9: Full Data Inspection &amp;amp; 100,000-Cell Safeguard
&lt;/h3&gt;

&lt;p&gt;Clicking &lt;strong&gt;"Get All Current Data"&lt;/strong&gt; runs a pre-flight row count check. If &lt;code&gt;total_rows * columnCount &amp;gt; 100,000&lt;/code&gt;, the operation aborts with an alert to prevent browser memory saturation. Otherwise, all rows are exported to &lt;code&gt;current_data&lt;/code&gt; via &lt;code&gt;table.exportToSheet()&lt;/code&gt; with &lt;code&gt;{ orderBy: "id ASC" }&lt;/code&gt; and sorted by ID ascending at the sheet level, guaranteeing deterministic primary key ordering for visual audit of the lakehouse state.&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 10: All Reset &amp;amp; Zero-Residue Purge
&lt;/h3&gt;

&lt;p&gt;Clicking &lt;strong&gt;"All Reset &amp;amp; Purge"&lt;/strong&gt; displays a confirmation dialog. Upon approval, the engine drops the Iceberg table, deletes all objects in the GCS bucket, removes the bucket, drops the BigQuery dataset, and purges spreadsheet tabs. Crucially, the engine &lt;strong&gt;preserves&lt;/strong&gt; &lt;code&gt;ICEBERG_PROJECT_ID&lt;/code&gt; and &lt;code&gt;ICEBERG_REGION&lt;/code&gt; in UserProperties, allowing immediate frictionless re-testing from Step 1 while guaranteeing 100% zero leftover cloud storage or table resources.&lt;/p&gt;


&lt;h2&gt;
  
  
  5. Comparative Benchmark: IcebergApp vs. Reverse ETL SaaS vs. Connected Sheets
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fz08c4dm1nghyd8d9qstz.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fz08c4dm1nghyd8d9qstz.jpg" alt="Figure 5: Comparative Technical Infographic: IcebergApp Writeback Engine vs Competitors" width="800" height="447"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Figure 5: Architectural, cost, and security comparison between IcebergApp Writeback Engine, Commercial Reverse ETL SaaS platforms, and Google Connected Sheets across four critical dimensions.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Comparative Narrative for Figure 5:
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Figure 5&lt;/strong&gt; evaluates the three competing architectural paradigms across four critical operational dimensions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Dimension 1: Total Cost of Ownership (TCO)&lt;/strong&gt;: Commercial Reverse ETL SaaS platforms impose recurring subscription commitments ranging from $1,500 to $5,000 per month based on sync frequency and sync row volume. In contrast, Google Connected Sheets and the IcebergApp Writeback Engine incur $0 in monthly licensing fees, operating on serverless, pay-per-query BigQuery compute.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dimension 2: Synchronization Directionality &amp;amp; Latency&lt;/strong&gt;: Connected Sheets remains strictly unidirectional (read-only), offering zero writeback capability. Commercial Reverse ETL tools operate via scheduled micro-batches that introduce 5-minute to 1-hour delays. The IcebergApp Writeback Engine delivers sub-second, interactive bidirectional synchronization, committing frontline edits immediately via atomic &lt;code&gt;MERGE INTO&lt;/code&gt; transactions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dimension 3: Data Sovereignty &amp;amp; Security Compliance&lt;/strong&gt;: Commercial Reverse ETL forces confidential enterprise records across third-party SaaS infrastructure, creating significant egress vulnerabilities and complicating GDPR, HIPAA, and SOC2 compliance. Conversely, Connected Sheets and IcebergApp operate 100% within customer-owned Google Cloud tenants, guaranteeing that data never leaves the organizational boundary.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dimension 4: Table Format &amp;amp; Multi-Engine Openness&lt;/strong&gt;: Reverse ETL platforms typically synchronize into proprietary staging caches or basic relational tables. IcebergApp commits mutations directly to open Apache Iceberg Parquet files and Avro manifest trees on Google Cloud Storage, allowing simultaneous, zero-copy querying by Apache Spark, Trino, Snowflake, and BigQuery without vendor lock-in.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To quantify the strategic advantages of this architecture, &lt;strong&gt;Figure 5&lt;/strong&gt; and the table below evaluate the three primary approaches to spreadsheet-lakehouse integration:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Evaluation Metric&lt;/th&gt;
&lt;th&gt;Commercial Reverse ETL SaaS (Census, Hightouch)&lt;/th&gt;
&lt;th&gt;Google Connected Sheets&lt;/th&gt;
&lt;th&gt;IcebergApp Writeback Engine (This Work)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Monthly Infrastructure Cost&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;$1,500 – $5,000 / month&lt;/strong&gt; (Tiered subscription + data volume)&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;$0&lt;/strong&gt; (Included with Google Workspace &amp;amp; GCP)&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;$0&lt;/strong&gt; (Serverless; on-demand BigQuery queries only)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Data Synchronization Direction&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Bidirectional (via separate scheduled sync flows)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Strictly Unidirectional (Read-Only)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Fully Bidirectional (Interactive Read &amp;amp; Differential Writeback)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Transaction &amp;amp; Latency Profile&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Scheduled micro-batches (5 min – 1 hour delay)&lt;/td&gt;
&lt;td&gt;Scheduled queries or manual refresh&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Sub-Second ACID Upsert (&lt;code&gt;MERGE INTO&lt;/code&gt;)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Data Sovereignty &amp;amp; Security&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Egress Risk&lt;/strong&gt;: Data transmitted to 3rd-party SaaS infrastructure&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;100% GCP VPC&lt;/strong&gt;: Data remains in BigQuery&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;100% GCP VPC&lt;/strong&gt;: Pure direct BigQuery &amp;amp; GCS interaction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Storage &amp;amp; Table Format&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Generic SQL tables (often proprietary data copies)&lt;/td&gt;
&lt;td&gt;BigQuery Managed Tables&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Open Apache Iceberg Parquet &amp;amp; Avro Manifests&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Concurrency Protection&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Basic batch overwrite or last-write-wins&lt;/td&gt;
&lt;td&gt;N/A (Read-Only)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Optimistic Concurrency Control (OCC) with Timestamp Gates&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Setup &amp;amp; Maintenance Overhead&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;High: External OAuth, webhooks, SaaS connectors&lt;/td&gt;
&lt;td&gt;Low: Native UI feature&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Zero&lt;/strong&gt;: Single Google Sheets container-bound script&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  Why the Serverless Writeback Engine Wins
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;$0 Fixed TCO&lt;/strong&gt;: Eliminates enterprise SaaS licensing fees entirely. An organization performing 100 daily writebacks spends less than $0.05 per month in BigQuery query fees.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero Vendor Lock-In&lt;/strong&gt;: Because data is committed directly to Apache Iceberg format on Cloud Storage, external engines (Apache Spark, Trino, Snowflake) can immediately read committed data without re-exporting.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ironclad Enterprise Security&lt;/strong&gt;: Compliance officers do not need to review third-party vendor SOC2 reports or execute Data Processing Agreements (DPAs); all data stays strictly within the organization's GCP tenant.&lt;/li&gt;
&lt;/ol&gt;


&lt;h2&gt;
  
  
  6. Autonomous Headless Test Suite (&lt;code&gt;HeadlessTest.js&lt;/code&gt;): The Primary Verification Gate
&lt;/h2&gt;

&lt;p&gt;As a foundational best practice, the architecture implements a &lt;strong&gt;two-stage verification workflow&lt;/strong&gt;: users execute the autonomous headless suite (&lt;code&gt;HeadlessTest.js&lt;/code&gt;) &lt;strong&gt;first&lt;/strong&gt; directly in the Apps Script editor. Running &lt;code&gt;runAutonomousWritebackHeadlessTest()&lt;/code&gt; validates that GCP credentials, IAM roles, BigQuery Advanced Service, and the core &lt;code&gt;IcebergApp.js&lt;/code&gt; engine operate flawlessly without any UI or spreadsheet interference. Once this primary gate passes cleanly, users proceed to the interactive Google Sheets UI console with full confidence.&lt;/p&gt;
&lt;h3&gt;
  
  
  6.1 Headless Execution Flow &amp;amp; Assertions
&lt;/h3&gt;

&lt;p&gt;Operating completely in the Google Apps Script V8 runtime without opening Google Sheets or touching UI components, the test suite executes an 8-stage verification pipeline adhering to Protocol 17 (Note: While the core &lt;code&gt;IcebergApp&lt;/code&gt; library natively supports binary &lt;code&gt;BYTES&lt;/code&gt; payloads, this test suite is purpose-built to validate bidirectional interoperability with Google Sheets tabular grids, meaning its verification schema is strictly specified for structured text and numerical fields: &lt;code&gt;id INT64, product STRING, price FLOAT64, stock INT64, updated_at TIMESTAMP&lt;/code&gt;):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fucy0qcwb0ioyzewo1r9f.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fucy0qcwb0ioyzewo1r9f.jpg" alt="Figure 6: Autonomous Headless Verification Pipeline: 8-Stage Quality Gate" width="800" height="447"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Figure 6: Autonomous Headless Verification Pipeline: 8-Stage Quality Gate for Apache Iceberg and Google Apps Script. Illustrates the end-to-end headless quality gate across 8 sequential verification stages and final zero-residue teardown.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Operational Narrative for Figure 6:
&lt;/h3&gt;

&lt;p&gt;As visualized in &lt;strong&gt;Figure 6&lt;/strong&gt;, the headless verification engine orchestrates an automated 8-stage quality gate followed by guaranteed resource cleanup:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Stages 1–2 (Ephemeral Provisioning &amp;amp; DDL)&lt;/strong&gt;: Stage 1 provisions an isolated, timestamped BigQuery dataset and Cloud Storage bucket. Stage 2 executes &lt;code&gt;IcebergApp.create()&lt;/code&gt; to generate the partitioned and clustered open Iceberg table.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stages 3–4 (Ingestion &amp;amp; Predicate Pushdown)&lt;/strong&gt;: Stage 3 inserts 20 structured records (asserting &lt;code&gt;insertedCount === 20&lt;/code&gt;). Stage 4 executes a filtered query with SQL predicate pushdown (&lt;code&gt;price &amp;gt; 1000.0&lt;/code&gt;), asserting that exactly 15 records match.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stages 5–6 (CDC Simulation &amp;amp; Atomic Mutation)&lt;/strong&gt;: Stage 5 simulates frontline spreadsheet modifications in memory (1 added, 1 modified, 1 deleted row). Stage 6 synthesizes and fires the atomic &lt;code&gt;MERGE INTO&lt;/code&gt; SQL statement, asserting that &lt;code&gt;numDmlAffectedRows === 3&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stages 7–8 (OCC Integrity &amp;amp; Safety Guard)&lt;/strong&gt;: Stage 7 verifies post-commit row states and validates that stale timestamps are strictly rejected by Optimistic Concurrency Control. Stage 8 deliberately triggers the 100,000-cell boundary to assert that defensive memory guards prevent client crash.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;FINALLY (Absolute Zero-Residue Teardown)&lt;/strong&gt;: Purges all Parquet data, drops the Iceberg table, and permanently removes the bucket and dataset, leaving zero lingering cloud resources.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  6.2 Verifying the Zero-Residue Lifecycle
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;finally&lt;/code&gt; block in &lt;code&gt;HeadlessTest.js&lt;/code&gt; guarantees that even if an assertion fails midway through execution, all created cloud resources are completely eradicated:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;--- ABSOLUTE CLEANUP: Purging Ephemeral Headless Test Resources ---&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;datasetCreated&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;BigQuery&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Datasets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;projectId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;testCatalogName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;deleteContents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bucketCreated&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nf"&gt;deleteGcsBucketCompletely_&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;testBucketName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;✨ Headless cleanup complete. Zero residue.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  7. Security, Defensive Protocols &amp;amp; Best Practices
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;SQL Injection Immunity&lt;/strong&gt;: All spreadsheet text cells are sanitized using strict string escape routines (&lt;code&gt;escapeSqlString_&lt;/code&gt;), converting backslashes to &lt;code&gt;\\\\&lt;/code&gt; and single quotes to &lt;code&gt;\\'&lt;/code&gt;. Numeric columns are strictly validated via &lt;code&gt;Number.isFinite()&lt;/code&gt;, and empty numeric cells are cast safely to &lt;code&gt;CAST(NULL AS FLOAT64)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;BigQuery DML Error Interception&lt;/strong&gt;: In compliance with Protocol 17, &lt;code&gt;runBqJob_&lt;/code&gt; polls &lt;code&gt;BigQuery.Jobs.getQueryResults&lt;/code&gt; and inspects the &lt;code&gt;errors&lt;/code&gt; array. Any syntax, quota, or permission errors are formatted into human-readable alerts rather than failing silently.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data Validation Hard Boundaries&lt;/strong&gt;: Attaching Google Sheets Data Validation rules directly to columns at query time prevents users from entering malformed data (such as entering negative stock or alphanumeric strings in price columns) before writeback dispatch.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;100,000-Cell Memory Guard&lt;/strong&gt;: Google Sheets tabs can become sluggish or crash browser tabs when rendering hundreds of thousands of cells. Pre-flight calculation of &lt;code&gt;total_rows * column_count&lt;/code&gt; prevents uncontrolled full-table dumps.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Privacy Mode Credential Shielding&lt;/strong&gt;: Client-side DOM masking allows operators to record tutorial videos or perform public demonstrations without revealing project IDs, dataset names, or storage paths. Dynamic in-memory unmasking ensures backend RPC execution remains 100% genuine and seamless.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Microsecond Timestamp &amp;amp; Float Tolerance&lt;/strong&gt;: Precision normalization and second-level delta checks (&lt;code&gt;TIMESTAMP_DIFF(..., SECOND) = 0&lt;/code&gt;) in SQL OCC prevent subtle millisecond-to-microsecond roundoff mismatches between Google Sheets and BigQuery from corrupting transactional integrity.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  8. Conclusion &amp;amp; Future Horizons
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;Apache Iceberg End-User Writeback UI&lt;/strong&gt; demonstrates that Google Sheets and enterprise open lakehouses are not mutually exclusive. By pairing Google Apps Script with BigQuery as a distributed compute engine and Apache Iceberg on Google Cloud Storage, developers can construct responsive, secure, and bidirectional operational interfaces in minutes without paying recurring SaaS subscription fees.&lt;/p&gt;

&lt;h3&gt;
  
  
  Future Horizons &amp;amp; Extensions:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Multi-Table Relational Writebacks&lt;/strong&gt;: Extending the CDC engine to support composite foreign key joins across multiple related sheets.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gemini Agentic Governance&lt;/strong&gt;: Integrating Google Workspace AI (Gemini) directly into the sidebar console to perform automated anomaly audits and explain CDC deltas in plain English before writeback execution.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Delta Lake &amp;amp; Hudi Federation&lt;/strong&gt;: Adapting the single-query &lt;code&gt;MERGE INTO&lt;/code&gt; synthesizer to support multi-format lakehouse engines simultaneously.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The complete code, sidebar templates, and headless test suites are open source and available in the &lt;a href="https://github.com/tanaikech/IcebergApp/tree/master/applications/writeback-sheets-ui" rel="noopener noreferrer"&gt;IcebergApp repository&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  References &amp;amp; Documentation
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/gde/unifying-google-workspace-and-apache-iceberg-serverless-lakehouse-management-ep3"&gt;Article 1: Unifying Google Workspace and Apache Iceberg: Serverless Lakehouse Management (Dev.to)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/gde/serverless-multimodal-vector-search-on-apache-iceberg-via-google-apps-script-4fg"&gt;Article 2: Serverless Multimodal Vector Search on Apache Iceberg via Google Apps Script: Unifying Documents, Images, and Text (Dev.to)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/tanaikech/IcebergApp" rel="noopener noreferrer"&gt;IcebergApp GitHub Repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://iceberg.apache.org/spec/" rel="noopener noreferrer"&gt;Apache Iceberg Official Specification&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.cloud.google.com/lakehouse/docs/lakehouse-basics" rel="noopener noreferrer"&gt;Google Cloud Lakehouse &amp;amp; Apache Iceberg Overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.cloud.google.com/bigquery/docs/iceberg-tables" rel="noopener noreferrer"&gt;BigQuery Apache Iceberg Tables Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developers.google.com/apps-script/advanced/bigquery" rel="noopener noreferrer"&gt;Google Apps Script BigQuery Advanced Service&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>googleworkspace</category>
      <category>googlecloud</category>
      <category>bigquery</category>
      <category>apacheiceberg</category>
    </item>
    <item>
      <title>Serverless Multimodal Vector Search on Apache Iceberg via Google Apps Script</title>
      <dc:creator>Tanaike</dc:creator>
      <pubDate>Tue, 08 Sep 2026 05:06:23 +0000</pubDate>
      <link>https://dev.to/gde/serverless-multimodal-vector-search-on-apache-iceberg-via-google-apps-script-4fg</link>
      <guid>https://dev.to/gde/serverless-multimodal-vector-search-on-apache-iceberg-via-google-apps-script-4fg</guid>
      <description>&lt;p&gt;&lt;em&gt;Turn Google Drive into an AI-Powered Lakehouse Vector Engine across Converted PDFs, Binary Images, and Text without Specialized Vector Databases.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1h6m3xs10u8a5ksbiyi3.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1h6m3xs10u8a5ksbiyi3.jpg" alt="Hero Infographic: Unified Multimodal Lakehouse Vector Search via Apache Iceberg &amp;amp; Google Apps Script" width="800" height="447"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Hero Infographic: Unified Multimodal Lakehouse Vector Search via Apache Iceberg &amp;amp; Google Apps Script. Consolidates Google Docs, Sheets, Slides, Forms, binary diagrams, and web-fetched assets into an open Parquet table, driven by Gemini embeddings and BigQuery serverless pushdown for sub-second semantic retrieval and live Google Drive discovery.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Structural Analysis of the Hero Infographic:
&lt;/h2&gt;

&lt;p&gt;The hero infographic visualizes the end-to-end paradigm shift enabling &lt;strong&gt;unified multimodal storage and sub-second semantic discovery&lt;/strong&gt; across three synchronized operational zones:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Left Zone (Diverse Multimodal Ingestion)&lt;/strong&gt;: Ingests heterogeneous corporate knowledge spanning Google Drive assets (Docs with automated PDF normalization, Sheets data/PDFs, Slides presentations, and Forms intake/response structures), binary media (PNG/JPEG schematics and diagrams), and web-crawled/HTTP-downloaded external files into a unified ingestion pipeline.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Center Zone (Serverless Processing &amp;amp; Unified Iceberg Lakehouse)&lt;/strong&gt;: The &lt;strong&gt;IcebergApp.js&lt;/strong&gt; engine running purely within Google Apps Script extracts payloads and routes textual representations to the &lt;strong&gt;Gemini Embedding API (&lt;code&gt;text-embedding-004&lt;/code&gt;)&lt;/strong&gt; to synthesize 768-dimensional normalized float vectors (&lt;code&gt;ARRAY&amp;lt;FLOAT64&amp;gt;&lt;/code&gt;). These are committed alongside raw binary streams (&lt;code&gt;data BYTES&lt;/code&gt;), metadata, and authentic Google Drive identifiers (&lt;code&gt;file_id STRING&lt;/code&gt;) into a &lt;strong&gt;Single Apache Iceberg Table&lt;/strong&gt; hosted on Google Cloud Storage.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Right Zone (Serverless Query &amp;amp; Live File Discovery)&lt;/strong&gt;: When an operator issues a natural language query, &lt;strong&gt;BigQuery&lt;/strong&gt; executes distributed &lt;code&gt;COSINE_DISTANCE&lt;/code&gt; pushdown directly across the open Parquet files on Cloud Storage. Within 0.3 to 0.5 seconds, ranked results (with similarity metrics exceeding &lt;code&gt;0.87&lt;/code&gt;) are returned, enabling instant 1-click browser navigation back to the live collaborative document or triggering programmatic Workspace automation.&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Abstract
&lt;/h2&gt;

&lt;p&gt;Enterprise knowledge is notoriously fragmented across structured tabular records, raw text notes, binary diagrams, and rich documents stored in Google Drive. Traditional Retrieval-Augmented Generation (RAG) and semantic search architectures address this challenge by deploying dedicated vector database clusters (e.g., Pinecone, Milvus, Qdrant) or specialized Reverse ETL pipelines. However, these systems introduce steep subscription costs, operational complexity, and vendor lock-in, while isolating embeddings from core analytical storage.&lt;/p&gt;

&lt;p&gt;This article introduces a &lt;strong&gt;100% serverless, zero-maintenance Multimodal Lakehouse Vector Search architecture&lt;/strong&gt; built on &lt;strong&gt;&lt;a href="https://github.com/tanaikech/IcebergApp" rel="noopener noreferrer"&gt;IcebergApp&lt;/a&gt;&lt;/strong&gt;, Google Apps Script (GAS), and BigQuery. Leveraging Apache Iceberg's native support for binary data (&lt;code&gt;BYTES&lt;/code&gt;), this system ingests heterogeneous assets—including live Google Docs automatically converted into PDF binary blobs, raw image diagrams, and direct plain text records—into an open Parquet table on Google Cloud Storage. Crucially, by persisting the authentic Google Drive &lt;code&gt;file_id&lt;/code&gt; alongside the binary PDF snapshot, vector search results immediately map back to the live collaborative document (&lt;code&gt;https://docs.google.com/document/d/${file_id}/edit&lt;/code&gt;), transforming Apache Iceberg into an enterprise-grade semantic file search engine for Google Workspace. Vector embeddings are generated on-the-fly using the Gemini API (&lt;code&gt;text-embedding-004&lt;/code&gt;) and queried using distributed vector distance metrics (&lt;code&gt;COSINE_DISTANCE&lt;/code&gt;) with sub-second latency. Furthermore, to enable developers to safely evaluate the pipeline within their own Google Cloud environment, the article includes an autonomous test suite equipped with an automated self-cleaning mechanism that guarantees zero-residue cleanup of temporary test assets, empowering Workspace developers to evaluate and deploy enterprise-grade multimodal search engines directly from Google Apps Script.&lt;/p&gt;


&lt;h2&gt;
  
  
  1. Introduction: The Fragmentation of Multimodal Knowledge
&lt;/h2&gt;

&lt;p&gt;In modern enterprise operations, knowledge does not reside neatly in relational database rows. It lives in diverse multimodal formats scattered across Google Workspace and cloud storage:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Rich Documents&lt;/strong&gt;: Standard operating procedures, technical manuals, and architectural specifications in &lt;strong&gt;Google Docs&lt;/strong&gt;; financial models and analytical KPIs in &lt;strong&gt;Google Sheets&lt;/strong&gt;; pitch decks and visual proposals in &lt;strong&gt;Google Slides&lt;/strong&gt;; and questionnaire forms and intake responses in &lt;strong&gt;Google Forms&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Visual Artifacts&lt;/strong&gt;: System architecture schematics, flowcharts, and infographics saved as &lt;strong&gt;PNG/JPEG images&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Direct Operational Text&lt;/strong&gt;: Real-time event notes, error logs, and transactional summaries written directly in code or spreadsheets.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To visually capture this operational divide and contrast conventional, costly multi-tier architectures with our unified serverless design, &lt;strong&gt;Figure 1&lt;/strong&gt; outlines the structural evolution from isolated enterprise silos to an integrated Apache Iceberg lakehouse.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvvn6t6keqicznyq46j8q.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvvn6t6keqicznyq46j8q.jpg" alt="Figure 1: Fragmented Enterprise Data Silos &amp;amp; Traditional RAG vs. IcebergApp Unified Multimodal Serverless Lakehouse" width="800" height="447"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Figure 1: Fragmented Enterprise Data Silos &amp;amp; Traditional RAG vs. IcebergApp Unified Multimodal Serverless Lakehouse. Contrasts the high-cost, fragmented traditional RAG pipeline against the unified, zero-egress Apache Iceberg architecture powered by Google Apps Script and BigQuery on Google Cloud.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Detailed Architectural Analysis of Figure 1:
&lt;/h3&gt;

&lt;p&gt;As clearly mapped in the comparative tiers of &lt;strong&gt;Figure 1&lt;/strong&gt;, enterprise multimodal knowledge management undergoes a fundamental structural paradigm shift:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Top Section (Fragmented Enterprise Data Silos)&lt;/strong&gt;: In everyday operations, assets are created and stored in isolated systems: Google Docs, Sheets, Slides, and Forms inside Google Drive; PNG/JPEG diagrams residing in Cloud Storage or Drive; and operational event notes residing as raw text strings. Because these assets use incompatible storage primitives, traditional analytical systems cannot ingest or index them collectively.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Middle Section (Traditional Complex RAG Stack - The Problem)&lt;/strong&gt;: As depicted by the red-bordered warning flow in &lt;strong&gt;Figure 1&lt;/strong&gt;, conventional architectures attempt to bridge these silos by introducing third-party Python microservices and dedicated vector databases (e.g., Pinecone, Milvus, Qdrant). This approach introduces three fatal operational penalties:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Exorbitant Subscription Tax&lt;/strong&gt;: Dedicated vector database clusters impose recurring fixed commitments between $500 and $3,000 per month, regardless of actual query volume.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Severe Egress Vulnerabilities&lt;/strong&gt;: Sensitive enterprise documents and intellectual property must leave the secure Google Cloud Virtual Private Cloud (VPC) and traverse external public networks to reach third-party SaaS platforms.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dual-Storage Synchronization Drift&lt;/strong&gt;: Storing vector embeddings in a standalone vector database while keeping binary PDFs and images in separate object storage invariably leads to broken links, synchronization lags, and metadata drift.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bottom Section (IcebergApp Unified Serverless Lakehouse - The Solution)&lt;/strong&gt;: Indicated by the glowing green and cyber-cyan borders in &lt;strong&gt;Figure 1&lt;/strong&gt;, our architecture replaces this complex multi-tier stack with a clean, 100% serverless pipeline:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Google Apps Script (V8 Engine)&lt;/strong&gt;: Directly interacts with Google Workspace APIs via &lt;code&gt;DriveApp&lt;/code&gt; and native services, exporting Docs, Sheets, and Slides into standardized PDF binary blobs on-the-fly (and extracting Forms structure via &lt;code&gt;FormApp&lt;/code&gt; or linked response sheets) without intermediary servers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Apache Iceberg on Google Cloud Storage&lt;/strong&gt;: Eliminates storage fragmentation by persisting Text metadata, Binary BYTES (PDF, PNG), and 768-dimensional float embedding vectors into a &lt;strong&gt;single, open Apache Parquet table&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;BigQuery Vector Accelerator&lt;/strong&gt;: Leverages distributed &lt;code&gt;COSINE_DISTANCE&lt;/code&gt; pushdown directly across open Parquet files, executing sub-second semantic retrieval with 100% data sovereignty entirely within the customer's GCP perimeter.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  1.1 The Pitfalls of Traditional Vector Architectures
&lt;/h3&gt;

&lt;p&gt;To perform semantic search across these mixed assets, engineering teams typically deploy complex multi-tier pipelines:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Dedicated Vector Database Tax&lt;/strong&gt;: Specialized vector databases (Pinecone, Weaviate, Qdrant) cost hundreds to thousands of dollars per month, requiring dedicated network infrastructure, VPC peering, and credential rotation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Metadata Synchronization Drift&lt;/strong&gt;: When document content changes on Google Drive, keeping the vector database synchronized with external document stores requires complex webhook choreography and distributed transaction handling.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Binary Data Isolation&lt;/strong&gt;: Most vector databases store only vector embeddings and minimal text metadata, forcing systems to maintain secondary blob storage links that frequently break or drift out of sync.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  1.2 The Breakthrough: Apache Iceberg as a Multimodal Vector Store
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://iceberg.apache.org/" rel="noopener noreferrer"&gt;Apache Iceberg&lt;/a&gt; fundamentally redefines this paradigm:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Binary Data Support&lt;/strong&gt;: Iceberg tables are not restricted to scalar primitives; they natively store arbitrary binary payloads (&lt;code&gt;BYTES&lt;/code&gt;) and complex arrays (&lt;code&gt;ARRAY&amp;lt;FLOAT64&amp;gt;&lt;/code&gt;). A single open Parquet table can hold document metadata, raw PDF binaries, image bytes, and high-dimensional vector embeddings side by side.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero Vendor Lock-In&lt;/strong&gt;: Assets reside in open Parquet files on Google Cloud Storage, directly queryable by BigQuery, Apache Spark, Trino, or DuckDB.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Serverless Vector Compute&lt;/strong&gt;: BigQuery provides distributed vector distance functions (&lt;code&gt;COSINE_DISTANCE&lt;/code&gt;, &lt;code&gt;EUCLIDEAN_DISTANCE&lt;/code&gt;) and vector indexes over Iceberg tables, eliminating the need for standalone vector database servers.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  1.3 From Vector Search to Semantic File Discovery: The Power of GAS &amp;amp; &lt;code&gt;file_id&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;One of the most compelling enterprise advantages of implementing this pipeline in Google Apps Script is the direct bridge between AI vector search and live Google Drive file discovery:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In conventional RAG pipelines, vector search engines return isolated text chunks, leaving users stranded with no direct path to the original source document.&lt;/li&gt;
&lt;li&gt;In contrast, because &lt;code&gt;IcebergApp&lt;/code&gt; stores the authentic Google Drive &lt;code&gt;file_id&lt;/code&gt; directly in the Iceberg table schema alongside the PDF binary snapshot, &lt;strong&gt;every vector search result includes the authoritative Drive File ID&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;This enables Workspace developers to build an &lt;strong&gt;intelligent semantic file search engine&lt;/strong&gt;:

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;One-Click Live Navigation&lt;/strong&gt;: Users can immediately navigate directly to the matching collaborative asset across formats (Google Docs: &lt;code&gt;https://docs.google.com/document/d/${file_id}/edit&lt;/code&gt;, Sheets: &lt;code&gt;https://docs.google.com/spreadsheets/d/${file_id}/edit&lt;/code&gt;, Slides: &lt;code&gt;https://docs.google.com/presentation/d/${file_id}/edit&lt;/code&gt;, Forms: &lt;code&gt;https://docs.google.com/forms/d/${file_id}/edit&lt;/code&gt;, or generic Drive link &lt;code&gt;https://drive.google.com/open?id=${file_id}&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automated Workspace Actionability&lt;/strong&gt;: In GAS, receiving the &lt;code&gt;file_id&lt;/code&gt; allows instant downstream scripting—such as updating document text via &lt;code&gt;DocumentApp&lt;/code&gt;, recalculating spreadsheets via &lt;code&gt;SpreadsheetApp&lt;/code&gt;, generating slides via &lt;code&gt;SlidesApp&lt;/code&gt;, managing form intake via &lt;code&gt;FormApp&lt;/code&gt;, or sharing access via &lt;code&gt;DriveApp.getFileById(file_id).addViewer()&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By marrying Google Apps Script with Apache Iceberg via &lt;strong&gt;&lt;a href="https://github.com/tanaikech/IcebergApp" rel="noopener noreferrer"&gt;IcebergApp&lt;/a&gt;&lt;/strong&gt;, Workspace developers gain not only a multimodal lakehouse but an actionable AI-powered file search engine across their entire Google Drive ecosystem.&lt;/p&gt;


&lt;h2&gt;
  
  
  2. Architecture &amp;amp; Multimodal Pipeline
&lt;/h2&gt;

&lt;p&gt;The end-to-end processing pipeline—from document ingestion and binary conversion to vector generation and distributed BigQuery search, as well as the autonomous verification flow executed by our test suite—is organized into the 6-stage workflow illustrated in &lt;strong&gt;Figure 2&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fi8ux4gjy87d1f236yph2.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fi8ux4gjy87d1f236yph2.jpg" alt="Figure 2: End-to-End Multimodal Vector Search Lifecycle in Apache Iceberg via Google Apps Script" width="800" height="447"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Figure 2: End-to-End Multimodal Vector Search Lifecycle in Apache Iceberg via Google Apps Script. Displays the 6-stage operational workflow from document normalization to sub-second vector search and safe test suite resource cleanup.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Detailed Operational Walkthrough of Figure 2:
&lt;/h3&gt;

&lt;p&gt;As illustrated across the numbered progression of &lt;strong&gt;Figure 2&lt;/strong&gt;, the system executes an automated, 6-step workflow that coordinates Google Workspace, Gemini AI, BigQuery, and Google Cloud Storage:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Step 1: Document Normalization &amp;amp; Asset Ingestion&lt;/strong&gt;: The pipeline captures heterogeneous enterprise assets across Google Workspace. When processing live Google Docs, &lt;code&gt;DriveApp.getFileById(docId).getBlob()&lt;/code&gt; converts collaborative documents into immutable, standardized PDF binaries (&lt;code&gt;application/pdf&lt;/code&gt;) without requiring headless Chromium or external conversion microservices. Concurrently, binary PNG images and raw operational text notes are loaded into memory.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Step 2: Vector Embedding Synthesis&lt;/strong&gt;: Google Apps Script dispatches the textual payload of each asset (full text for documents and text files; semantic description metadata for images) to Google's Gemini Embedding API (&lt;code&gt;text-embedding-004&lt;/code&gt;). The model returns dense, L2-normalized 768-dimensional float arrays (&lt;code&gt;ARRAY&amp;lt;FLOAT64&amp;gt;&lt;/code&gt;) that capture semantic meaning across all modalities.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Step 3: Iceberg Parquet Commit via IcebergApp&lt;/strong&gt;: Leveraging the core &lt;code&gt;IcebergApp.js&lt;/code&gt; library, the engine prepares an open Apache Iceberg table on Google Cloud Storage. Through &lt;code&gt;table.insertBlobs()&lt;/code&gt;, binary data payloads (&lt;code&gt;BYTES&lt;/code&gt;), descriptive schema metadata, and 768-dimensional vector arrays are committed simultaneously into open Parquet data files accompanied by hierarchical Avro metadata manifests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Step 4: Sub-Second Vector Query&lt;/strong&gt;: When an operator issues a natural language query (e.g., &lt;em&gt;"How do I automate spreadsheets with JavaScript?"&lt;/em&gt;), the query is vectorized via Gemini. BigQuery then executes a distributed vector scan using &lt;code&gt;COSINE_DISTANCE(embedding, query_vector)&lt;/code&gt; directly against the Parquet data files stored on Cloud Storage, completely bypassing intermediate database servers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Step 5: Ranked Multimodal Retrieval&lt;/strong&gt;: BigQuery ranks all assets deterministically by cosine distance (&lt;code&gt;ORDER BY distance ASC LIMIT 3&lt;/code&gt;). The search engine successfully retrieves the most relevant asset regardless of original format (whether PDF document, architectural diagram, or raw text note), achieving high semantic confidence (e.g., similarity scores exceeding &lt;code&gt;0.87&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Step 6: Test Suite Zero-Residue Cleanup&lt;/strong&gt;: Upon completion of verification and retrieval, the autonomous test runner purges all temporary test assets: trashing test Google Docs on Google Drive, dropping the test table, and completely deleting the BigQuery dataset and Cloud Storage bucket within 35 seconds. &lt;em&gt;(Note: While production systems naturally retain their data, this self-cleaning teardown is built into the test suite so evaluators can run it with zero lingering cloud cost or clutter.)&lt;/em&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  2.1 Component Breakdown
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Automatic Document Normalization (&lt;code&gt;Google Workspace Docs / Sheets / Slides -&amp;gt; PDF Blob&lt;/code&gt;)&lt;/strong&gt;:
In Google Apps Script, calling &lt;code&gt;DriveApp.getFileById(fileId).getBlob()&lt;/code&gt; on Google Docs, &lt;strong&gt;Google Sheets, or Google Slides&lt;/strong&gt; automatically exports the live collaborative asset into an industry-standard, immutable PDF binary (&lt;code&gt;application/pdf&lt;/code&gt;) without external software. (For Google Forms, questions and configurations can be extracted via &lt;code&gt;FormApp&lt;/code&gt; or linked response sheets into structured text/blobs.) This allows all dynamic Google Workspace documents to be archived and indexed as reproducible binary snapshots inside Apache Iceberg.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multimodal Schema Definition&lt;/strong&gt;:
The Iceberg table schema harmonizes structured metadata, binary assets, and vector embeddings:
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;   &lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="nv"&gt;`my_project.lakehouse_catalog.multimodal_knowledge_assets`&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
     &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="n"&gt;STRING&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="n"&gt;file_id&lt;/span&gt; &lt;span class="n"&gt;STRING&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="n"&gt;STRING&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="n"&gt;category&lt;/span&gt; &lt;span class="n"&gt;STRING&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="n"&gt;mime_type&lt;/span&gt; &lt;span class="n"&gt;STRING&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="k"&gt;size&lt;/span&gt; &lt;span class="n"&gt;INT64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="n"&gt;STRING&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="k"&gt;data&lt;/span&gt; &lt;span class="n"&gt;BYTES&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="n"&gt;embedding&lt;/span&gt; &lt;span class="n"&gt;ARRAY&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;FLOAT64&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="n"&gt;updated_at&lt;/span&gt; &lt;span class="nb"&gt;TIMESTAMP&lt;/span&gt;
   &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Notice the &lt;code&gt;file_id STRING&lt;/code&gt; column: this preserves the primary Google Drive document identifier, serving as the essential bi-directional bridge between the immutable lakehouse Parquet archive and the live Google Drive file.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Automated Dense Vector Synthesis with Gemini (&lt;code&gt;text-embedding-004&lt;/code&gt;)&lt;/strong&gt;:
&lt;code&gt;IcebergApp&lt;/code&gt; interfaces natively with Google's state-of-the-art Gemini Embedding API (&lt;code&gt;text-embedding-004&lt;/code&gt;). Extracted body text from Google Docs, tabular summaries from Sheets, slide contents, form structures, binary image metadata summaries, and external web payloads are synthesized into 768-dimensional normalized float vectors (&lt;code&gt;ARRAY&amp;lt;FLOAT64&amp;gt;&lt;/code&gt;), persisted directly into the Iceberg table's &lt;code&gt;embedding&lt;/code&gt; column.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sub-Second Vector Search via BigQuery Acceleration&lt;/strong&gt;:
Queries are evaluated using serverless SQL pushdown:
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;   &lt;span class="k"&gt;SELECT&lt;/span&gt;
     &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;file_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mime_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="n"&gt;COSINE_DISTANCE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0142&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0521&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...])&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;distance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="n"&gt;ROUND&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;COSINE_DISTANCE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0142&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0521&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...]),&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;similarity&lt;/span&gt;
   &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="nv"&gt;`my_project.lakehouse_catalog.multimodal_knowledge_assets`&lt;/span&gt;
   &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;embedding&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;
   &lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;distance&lt;/span&gt; &lt;span class="k"&gt;ASC&lt;/span&gt;
   &lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  2.2 How Gemini Powers the Architecture: 3 Core Operational Pillars
&lt;/h3&gt;

&lt;p&gt;Within this architecture, &lt;strong&gt;Google Gemini is not merely an optional add-on; it serves as the foundational intelligence engine&lt;/strong&gt; that transforms a passive storage repository into a cognitive, semantic discovery system. Gemini participates deeply across three key operational dimensions:&lt;/p&gt;
&lt;h4&gt;
  
  
  1. Ingestion Phase: Harmonizing Heterogeneous Modalities into a Unified 768-D Semantic Space
&lt;/h4&gt;

&lt;p&gt;Traditional storage engines cannot compare a PDF document, an architectural PNG image, a spreadsheet, and an external web page because their underlying data primitives are fundamentally incompatible.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Documents &amp;amp; Text Assets (Docs, Sheets, Slides, Forms, Web)&lt;/strong&gt;:
Within Google Apps Script, &lt;code&gt;DocumentApp&lt;/code&gt; and native Workspace APIs extract raw text representations, which &lt;code&gt;IcebergApp&lt;/code&gt; dispatches to the Gemini Embedding API (&lt;code&gt;text-embedding-004&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Visual Artifacts (Images &amp;amp; Diagrams)&lt;/strong&gt;:
Structural summaries and descriptive metadata are supplied to the embedding engine.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gemini's Role&lt;/strong&gt;:
Gemini analyzes the semantic content and projects each disparate asset into an identical &lt;strong&gt;768-dimensional L2-normalized float array (&lt;code&gt;ARRAY&amp;lt;FLOAT64&amp;gt;&lt;/code&gt;)&lt;/strong&gt;. By doing so, every asset—regardless of whether it originated as a 30-page PDF, a flowchart image, or an HTML snippet—shares the exact same mathematical coordinate system inside the open Iceberg Parquet table.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  2. Query Phase: Real-Time Translation of Natural Language into Calculable Query Vectors
&lt;/h4&gt;

&lt;p&gt;When an operator issues a natural language query from Google Apps Script (e.g., &lt;em&gt;"How do I automate spreadsheets with JavaScript?"&lt;/em&gt;):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;IcebergApp.getTableByName().searchSimilar(query)&lt;/code&gt; immediately makes an outbound REST call to Gemini's &lt;code&gt;embedContent&lt;/code&gt; endpoint via Apps Script's native &lt;code&gt;UrlFetchApp&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Within 200 to 300 milliseconds, Gemini returns a 768-dimensional query vector that captures the user's conceptual intent.&lt;/li&gt;
&lt;li&gt;This vector is injected into BigQuery's distributed SQL engine, allowing &lt;code&gt;COSINE_DISTANCE(embedding, [queryVector])&lt;/code&gt; to scan thousands of records in parallel with sub-second execution latency.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  3. Breaking Keyword Barriers: True Semantic Understanding Without Exact-Word Matches
&lt;/h4&gt;

&lt;p&gt;Legacy Google Drive search and relational SQL &lt;code&gt;LIKE&lt;/code&gt; queries fail whenever documents use synonyms or alternative technical phrasing.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Gemini's Semantic Breakthrough&lt;/strong&gt;:
Gemini understands cross-domain conceptual hierarchies. As demonstrated in our empirical execution log (Section 5.1), when queried with the complex physical concept &lt;em&gt;"superconducting qubit coherence at millikelvin temperatures"&lt;/em&gt;, the system correctly identified &lt;code&gt;TXT-001&lt;/code&gt; (Quantum Computing Guide) with an &lt;strong&gt;outstanding similarity score of 0.8669&lt;/strong&gt;, cleanly separating it from unrelated biological research or operational guides despite the lack of exact title matches.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pure Serverless Simplicity&lt;/strong&gt;:
While conventional vectorization pipelines require heavyweight Python environments (PyTorch, Hugging Face Transformers) running on GPU clusters, &lt;code&gt;IcebergApp&lt;/code&gt; achieves state-of-the-art embedding generation through a single serverless HTTPS call, incurring zero persistent infrastructure costs.&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  3. Implementation: The Test Suite &amp;amp; Code Essence
&lt;/h2&gt;

&lt;p&gt;To provide a fully verified, turn-key demonstration, the complete standalone test script is implemented at &lt;a href="https://github.com/tanaikech/IcebergApp/blob/master/src/multimodal_vector_search_test.js" rel="noopener noreferrer"&gt;&lt;code&gt;src/multimodal_vector_search_test.js&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  3.1 Step 1: Install IcebergApp in Google Apps Script
&lt;/h3&gt;

&lt;p&gt;To use IcebergApp in your project:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open your Google Apps Script project (via &lt;code&gt;script.new&lt;/code&gt; or container-bound to a Sheet/Doc).&lt;/li&gt;
&lt;li&gt;Download the core library &lt;a href="https://github.com/tanaikech/IcebergApp/blob/master/src/IcebergApp.js" rel="noopener noreferrer"&gt;&lt;code&gt;IcebergApp.js&lt;/code&gt;&lt;/a&gt; from GitHub:
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   https://github.com/tanaikech/IcebergApp/blob/master/src/IcebergApp.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;In the Apps Script editor, create a new script file named &lt;code&gt;IcebergApp.gs&lt;/code&gt; (or &lt;code&gt;IcebergApp.js&lt;/code&gt;) and paste the complete library content.&lt;/li&gt;
&lt;li&gt;Under &lt;strong&gt;Services (+)&lt;/strong&gt;, add the &lt;strong&gt;BigQuery API&lt;/strong&gt; (&lt;code&gt;v2&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Open &lt;strong&gt;Project Settings (⚙️) &amp;gt; Script properties&lt;/strong&gt;, and configure:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;PROJECT_ID&lt;/code&gt;: Your Google Cloud Project ID.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;REGION&lt;/code&gt;: Your target region (e.g., &lt;code&gt;asia-northeast1&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GEMINI_API_KEY&lt;/code&gt;: Your Gemini API key from Google AI Studio.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;


&lt;h3&gt;
  
  
  3.2 Step-by-Step Test Execution: What Was Obtained at Each Stage
&lt;/h3&gt;

&lt;p&gt;By inspecting the empirical execution log produced by &lt;code&gt;runMultimodalVectorSearchTest()&lt;/code&gt;, we can trace exactly what technical artifacts and verification milestones were obtained across each of the 6 sequential stages:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Stage 1 [00:00:04]: Ephemeral Infrastructure Provisioning&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Action&lt;/strong&gt;: Provisioned an isolated BigQuery dataset &lt;code&gt;lakehouse_multimodal_xxxx&lt;/code&gt; at &lt;code&gt;asia-northeast1&lt;/code&gt; and a Cloud Storage bucket &lt;code&gt;gs://lakehouse-mm-xxxx&lt;/code&gt; with Uniform Bucket-Level Access.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What Was Obtained&lt;/strong&gt;: A completely isolated, timestamped GCP sandbox environment. This guarantees that test operations never interfere with production datasets or existing lakehouse catalogs.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Stage 2 [00:00:08]: Apache Iceberg Table Initialization&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Action&lt;/strong&gt;: Dispatched DDL statements via BigQuery's BigLake external catalog integration to establish the Iceberg table &lt;code&gt;multimodal_knowledge_assets&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What Was Obtained&lt;/strong&gt;: A unified, open table schema natively supporting binary payloads (&lt;code&gt;data BYTES&lt;/code&gt;), high-dimensional embeddings (&lt;code&gt;embedding ARRAY&amp;lt;FLOAT64&amp;gt;&lt;/code&gt;), and standard metadata (&lt;code&gt;id&lt;/code&gt;, &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;category&lt;/code&gt;, &lt;code&gt;mime_type&lt;/code&gt;, &lt;code&gt;size&lt;/code&gt;, &lt;code&gt;description&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Stage 3 [00:00:10]: Google Docs Generation &amp;amp; Cross-Modal Asset Staging&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Action&lt;/strong&gt;: Dynamically generated 3 live Google Docs on Google Drive, immediately converted them to immutable PDF blobs via &lt;code&gt;DriveApp.getFileById().getBlob()&lt;/code&gt;, and staged 2 raw text notes and 1 binary PNG diagram.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What Was Obtained&lt;/strong&gt;: Six concrete multimodal assets across three distinct formats with verified byte-level sizes:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;DOC-001&lt;/code&gt;: &lt;code&gt;Google_Apps_Script_Automation_Guide.pdf&lt;/code&gt; (27,177 bytes, &lt;code&gt;application/pdf&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DOC-002&lt;/code&gt;: &lt;code&gt;Google_Sheets_Calculations_Analytics_Guide.pdf&lt;/code&gt; (28,363 bytes, &lt;code&gt;application/pdf&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DOC-003&lt;/code&gt;: &lt;code&gt;Google_Docs_Collaborative_Publishing_Guide.pdf&lt;/code&gt; (20,757 bytes, &lt;code&gt;application/pdf&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;TXT-001&lt;/code&gt;: &lt;code&gt;quantum_cryogenic_computing_whitepaper.txt&lt;/code&gt; (229 bytes, &lt;code&gt;text/plain&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;TXT-002&lt;/code&gt;: &lt;code&gt;marine_ocean_bioluminescence_study.txt&lt;/code&gt; (218 bytes, &lt;code&gt;text/plain&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;IMG-001&lt;/code&gt;: &lt;code&gt;lakehouse_multimodal_architecture_diagram.png&lt;/code&gt; (70 bytes, &lt;code&gt;image/png&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Stage 4 [00:00:15 – 00:00:24]: Gemini Vector Synthesis &amp;amp; Single-Table Parquet Commit&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Action&lt;/strong&gt;: Dispatched the textual payloads and descriptive summaries to Google's Gemini Embedding API (&lt;code&gt;text-embedding-004&lt;/code&gt;), generated 768-dimensional float arrays, and executed &lt;code&gt;table.insertBlobs()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What Was Obtained&lt;/strong&gt;: All 6 heterogeneous records—unifying binary bytes, structured metadata, and vector embeddings—were committed into a single open Parquet data file and updated Avro metadata manifests on Cloud Storage in just 9 seconds. The ingested inventory was printed and verified.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Stage 5 [00:00:24 – 00:00:29]: Semantic Vector Searches across Heterogeneous Formats&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Action&lt;/strong&gt;: Executed 4 distinct natural language queries using BigQuery distributed &lt;code&gt;COSINE_DISTANCE&lt;/code&gt; pushdown directly over the Iceberg Parquet files.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What Was Obtained&lt;/strong&gt;: &lt;strong&gt;100% precision in cross-modal ranking&lt;/strong&gt;; every target asset achieved Rank #1 with statistically significant margin over alternative files:

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Query 1 (Automation)&lt;/em&gt;: Isolated &lt;code&gt;DOC-001&lt;/code&gt; (&lt;code&gt;0.7500&lt;/code&gt; similarity) vs Sheets PDF (&lt;code&gt;0.6324&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Query 2 (Formulas)&lt;/em&gt;: Isolated &lt;code&gt;DOC-002&lt;/code&gt; (&lt;code&gt;0.7935&lt;/code&gt; similarity) vs GAS PDF (&lt;code&gt;0.5748&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Query 3 (Quantum Qubits)&lt;/em&gt;: Isolated &lt;code&gt;TXT-001&lt;/code&gt; (&lt;code&gt;0.8669&lt;/code&gt; similarity) vs Biology text (&lt;code&gt;0.5012&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Query 4 (Visual Flow Diagram)&lt;/em&gt;: Isolated &lt;code&gt;IMG-001&lt;/code&gt; (&lt;code&gt;0.8762&lt;/code&gt; similarity) vs Docs PDF (&lt;code&gt;0.6724&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Stage 6 [00:00:29 – 00:00:35]: Absolute Zero-Residue Teardown&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Action&lt;/strong&gt;: Automatically dropped the Iceberg table, trashed the 3 Google Docs by explicit Drive ID (&lt;code&gt;[xxxx-drive-doc1-id]&lt;/code&gt;, etc.), deleted the BigQuery dataset, and destroyed the Cloud Storage bucket.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What Was Obtained&lt;/strong&gt;: &lt;strong&gt;100% eradication of all transient cloud resources&lt;/strong&gt; in only 6 seconds, leaving zero leftover data, zero storage costs, and zero orphaned Drive files.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;


&lt;h3&gt;
  
  
  3.3 Step 2: Code Essence – Ingesting Mixed Assets
&lt;/h3&gt;

&lt;p&gt;The following concise snippet demonstrates how easy it is to ingest mixed Google Docs (PDFs), direct text, and binary images using &lt;code&gt;IcebergApp&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ingestMultimodalAssets&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;projectId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;your-gcp-project-id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;catalogName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;lakehouse_multimodal&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tableName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;knowledge_assets&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// 1. Initialize Iceberg catalog&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;IcebergApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;openByCatalog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;projectId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;catalogName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;asia-northeast1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;ensureCatalog&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;table&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createAssetTable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tableName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// 2. Prepare Google Doc (auto-exported to PDF)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;DocumentApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GAS_Automation_Guide&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;doc&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBody&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Google Apps Script automates Workspace workflows and connects to Apache Iceberg.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;saveAndClose&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pdfBlob&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;DriveApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getFileById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getId&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBlob&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GAS_Automation_Guide.pdf&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// 3. Prepare Direct Plain Text&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;textBlob&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Utilities&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newBlob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Superconducting transmon qubits operate at 10 millikelvin inside dilution refrigerators.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text/plain&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;quantum_physics.txt&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// 4. Ingest with automated Gemini vector embeddings&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;entries&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;blob&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;pdfBlob&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;DOC-001&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;file_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getId&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="na"&gt;category&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;google_doc_pdf&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
          &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Google Apps Script automation and cloud lakehouse connectivity guide.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;blob&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;textBlob&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;TXT-001&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;category&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;plain_text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;textBlob&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getDataAsString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;];&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;insertedCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insertBlobs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;embed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;`Ingested &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;insertedCount&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; multimodal records into Apache Iceberg.`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  3.4 Code Essence – Semantic Vector Search &amp;amp; Live File Discovery
&lt;/h3&gt;

&lt;p&gt;Executing semantic vector search across both PDF documents and plain text files requires only a single method call. Crucially, because &lt;code&gt;file_id&lt;/code&gt; is queried directly alongside distance metrics, the result connects straight back to the live Google Drive document:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;searchMultimodalLakehouse&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;IcebergApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;openByCatalog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;your-gcp-project-id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;lakehouse_multimodal&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;table&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getTableByName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;knowledge_assets&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;How do I build serverless data pipelines in Workspace using JavaScript?&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Request file_id alongside metadata and similarity metrics&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;searchSimilar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;topK&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;file_id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;category&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mime_type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Rank | &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; | &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fileId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;category&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;mimeType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;distance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;similarity&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;driveUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fileId&lt;/span&gt;
        &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;`https://docs.google.com/document/d/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;fileId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/edit`&lt;/span&gt;
        &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;N/A (direct text/image)&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s2"&gt;` #&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; | ID: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; | Name: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; | Similarity: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;similarity&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`      🔗 Live Google Drive Document: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;driveUrl&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="c1"&gt;// Optional: Programmatically open or share the matching live Google Doc&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fileId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;liveDoc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;DocumentApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;openById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fileId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`      📄 Document Title: "&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;liveDoc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getName&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;"`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  4. Test Suite Safety Design: Automated Cleanup of Test Resources
&lt;/h2&gt;

&lt;p&gt;For developers who wish to evaluate this multimodal lakehouse architecture within their own Google Cloud and Google Drive environments, the accompanying autonomous test script (&lt;a href="https://github.com/tanaikech/IcebergApp/blob/master/src/multimodal_vector_search_test.js" rel="noopener noreferrer"&gt;&lt;code&gt;multimodal_vector_search_test.js&lt;/code&gt;&lt;/a&gt;) incorporates an automated self-cleaning mechanism to ensure that temporary test assets (test Google Docs, temporary BigQuery datasets, and temporary GCS buckets) are completely scrubbed.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;[!NOTE]&lt;br&gt;
&lt;strong&gt;Production vs. Testing Context&lt;/strong&gt;: While real-world production lakehouses naturally preserve accumulated tables and documents, this cleanup engine is implemented exclusively in the test suite so that developers can safely run and evaluate the pipeline without leaving orphaned test clutter or incurring unexpected cloud maintenance costs.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When running automated test suites in serverless environments, an execution timeout (e.g., Apps Script's 6-minute ceiling) or manual cancellation could risk leaving temporary test resources uncollected. To eliminate this risk, the test script implements a &lt;strong&gt;Triple-Tiered Safety Architecture&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Persistent ScriptProperties Registry (&lt;code&gt;_ICEBERG_MULTIMODAL_TEST_REGISTRY_&lt;/code&gt;)&lt;/strong&gt;:
The instant a test Google Doc, BigQuery dataset, GCS bucket, or table is created, its identifier is committed immediately to persistent &lt;code&gt;ScriptProperties&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pre-Flight Sweep&lt;/strong&gt;:
Upon invoking &lt;code&gt;runMultimodalVectorSearchTest()&lt;/code&gt;, the runner first reads the registry and scrubs any lingering resources from previously aborted runs, followed by a wildcard Drive search for test-pattern titles.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Standalone Emergency Purge Function (&lt;code&gt;purgeResidualMultimodalTestResources&lt;/code&gt;)&lt;/strong&gt;:
A dedicated function executable directly from the Apps Script toolbar dropdown to purge all tracked test resources on demand without re-running the full test suite.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;purgeResidualMultimodalTestResources&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;🧹 Running Standalone Emergency Purge for Multimodal Test Resources...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;cleanupResidualMultimodalResources_&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;✨ Emergency purge complete. All residual artifacts 100% purged.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  5. Execution Results &amp;amp; Empirical Log Audit
&lt;/h2&gt;

&lt;p&gt;When &lt;code&gt;runMultimodalVectorSearchTest()&lt;/code&gt; is executed in the Apps Script Editor, it sequentially carries out:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Isolated infrastructure provisioning (dataset &amp;amp; bucket).&lt;/li&gt;
&lt;li&gt;Creation of three live Google Docs on Google Drive and immediate conversion into PDF binary blobs.&lt;/li&gt;
&lt;li&gt;Preparation of direct text files and image assets.&lt;/li&gt;
&lt;li&gt;Batch ingestion into Apache Iceberg with Gemini 768-dimensional vector embeddings.&lt;/li&gt;
&lt;li&gt;Execution of 4 diverse semantic vector queries targeting distinct domains (Google Apps Script, Google Sheets, Quantum Physics, Architecture Diagrams).&lt;/li&gt;
&lt;li&gt;Comprehensive post-test zero-residue teardown.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  5.1 Empirical Live Execution Log
&lt;/h3&gt;

&lt;p&gt;The following execution log is the output produced by executing &lt;code&gt;runMultimodalVectorSearchTest&lt;/code&gt; within the Google Apps Script V8 runtime against Google Cloud Platform (with private identifiers masked for privacy, conforming to article conventions):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;00:00:00    Notice  Execution started
00:00:01    Info    🚀 Starting Multimodal Apache Iceberg Vector Search Test Suite...
00:00:02    Info    📋 Active Target Environment: Project [your-gcp-project-id], Region [asia-northeast1], Gemini API [Configured]
00:00:04    Info
--- [1/6] Provisioning Isolated Dataset [lakehouse_multimodal_xxxx] &amp;amp; GCS Bucket [lakehouse-mm-xxxx] ---
00:00:08    Info    ✅ Base Storage Provisioned: gs://lakehouse-mm-xxxx
00:00:08    Info
--- [2/6] Creating Apache Iceberg Asset Table [multimodal_knowledge_assets] ---
00:00:08    Info    ✅ Dataset [lakehouse_multimodal_xxxx] confirmed at [asia-northeast1].
00:00:09    Info    ✅ Dataset [lakehouse_multimodal_xxxx] confirmed at [asia-northeast1].
00:00:10    Info    ✅ Iceberg Table Ready: `your-gcp-project-id.lakehouse_multimodal_xxxx.multimodal_knowledge_assets`
00:00:10    Info
--- [3/6] Generating Real Google Docs on Google Drive &amp;amp; Exporting to PDF ---
00:00:11    Info    📄 Created Google Doc 1: "Google_Apps_Script_Automation_Guide_xxxx" -&amp;gt; Converted to PDF (27177 bytes)
00:00:14    Info    📄 Created Google Doc 2: "Google_Sheets_Calculations_Analytics_Guide_xxxx" -&amp;gt; Converted to PDF (28363 bytes)
00:00:15    Info    📄 Created Google Doc 3: "Google_Docs_Collaborative_Publishing_Guide_xxxx" -&amp;gt; Converted to PDF (20757 bytes)
00:00:15    Info    📝 Prepared Direct Text 1: "quantum_cryogenic_computing_whitepaper.txt" (229 bytes)
00:00:15    Info    📝 Prepared Direct Text 2: "marine_ocean_bioluminescence_study.txt" (218 bytes)
00:00:15    Info    🖼️ Prepared Binary Image: "lakehouse_multimodal_architecture_diagram.png" (70 bytes)
00:00:15    Info
--- [4/6] Ingesting Multimodal Assets into Apache Iceberg with Gemini Vector Embeddings ---
00:00:23    Info    ✅ Successfully ingested 6 multimodal assets into Apache Iceberg with 768-dim embeddings!
00:00:24    Info
--- Ingested Lakehouse Asset Inventory ---
00:00:24    Info     [0] id | name | category | mime_type | size
00:00:24    Info     [1] DOC-002 | Google_Sheets_Calculations_Analytics_Guide.pdf | google_doc_pdf | application/pdf | 28363
00:00:24    Info     [2] DOC-001 | Google_Apps_Script_Automation_Guide.pdf | google_doc_pdf | application/pdf | 27177
00:00:24    Info     [3] DOC-003 | Google_Docs_Collaborative_Publishing_Guide.pdf | google_doc_pdf | application/pdf | 20757
00:00:24    Info     [4] IMG-001 | lakehouse_multimodal_architecture_diagram.png | image_diagram | image/png | 70
00:00:24    Info     [5] TXT-001 | quantum_cryogenic_computing_whitepaper.txt | plain_text | text/plain | 229
00:00:24    Info     [6] TXT-002 | marine_ocean_bioluminescence_study.txt | plain_text | text/plain | 218
00:00:24    Info
--- [5/6] Executing Semantic Vector Searches across Mixed Data Types ---
00:00:24    Info
================================================================================
00:00:24    Info    🔍 [Query 1/4] Test A: Target Google Apps Script Automation (Expecting DOC-001 / Google Apps Script PDF)
00:00:24    Info       Prompt: "How can I automate spreadsheet workflows and trigger cloud tasks using JavaScript?"
00:00:24    Info    ================================================================================
00:00:25    Info    Rank | ID      | Category       | MIME Type        | Distance | Similarity | Asset Name
00:00:25    Info    -----+---------+----------------+------------------+----------+------------+--------------------------------------------------
00:00:25    Info     #1  | DOC-001 | google_doc_pdf | application/pdf  |  0.2500  |   0.7500   | Google_Apps_Script_Automation_Guide.pdf
00:00:25    Info     #2  | DOC-002 | google_doc_pdf | application/pdf  |  0.3676  |   0.6324   | Google_Sheets_Calculations_Analytics_Guide.pdf
00:00:25    Info     #3  | DOC-003 | google_doc_pdf | application/pdf  |  0.4319  |   0.5681   | Google_Docs_Collaborative_Publishing_Guide.pdf
00:00:25    Info
================================================================================
00:00:25    Info    🔍 [Query 2/4] Test B: Target Google Sheets Calculations (Expecting DOC-002 / Google Sheets PDF)
00:00:25    Info       Prompt: "Spreadsheet calculation formulas, pivot tables, and grid data analysis for large workbooks"
00:00:25    Info    ================================================================================
00:00:26    Info    Rank | ID      | Category       | MIME Type        | Distance | Similarity | Asset Name
00:00:26    Info    -----+---------+----------------+------------------+----------+------------+--------------------------------------------------
00:00:26    Info     #1  | DOC-002 | google_doc_pdf | application/pdf  |  0.2065  |   0.7935   | Google_Sheets_Calculations_Analytics_Guide.pdf
00:00:26    Info     #2  | DOC-001 | google_doc_pdf | application/pdf  |  0.4252  |   0.5748   | Google_Apps_Script_Automation_Guide.pdf
00:00:26    Info     #3  | IMG-001 | image_diagram  | image/png        |  0.4325  |   0.5675   | lakehouse_multimodal_architecture_diagram.png
00:00:26    Info
================================================================================
00:00:26    Info    🔍 [Query 3/4] Test C: Target Quantum Physics Text (Expecting TXT-001 / Quantum Whitepaper)
00:00:26    Info       Prompt: "Superconducting qubits, Josephson junctions, and dilution refrigerators operating at millikelvin temperatures"
00:00:26    Info    ================================================================================
00:00:28    Info    Rank | ID      | Category       | MIME Type        | Distance | Similarity | Asset Name
00:00:28    Info    -----+---------+----------------+------------------+----------+------------+--------------------------------------------------
00:00:28    Info     #1  | TXT-001 | plain_text     | text/plain       |  0.1331  |   0.8669   | quantum_cryogenic_computing_whitepaper.txt
00:00:28    Info     #2  | TXT-002 | plain_text     | text/plain       |  0.4988  |   0.5012   | marine_ocean_bioluminescence_study.txt
00:00:28    Info     #3  | IMG-001 | image_diagram  | image/png        |  0.5016  |   0.4984   | lakehouse_multimodal_architecture_diagram.png
00:00:28    Info
================================================================================
00:00:28    Info    🔍 [Query 4/4] Test D: Target Architecture Diagram (Expecting IMG-001 / System Diagram)
00:00:28    Info       Prompt: "Visual architectural flow diagram showing multimodal lakehouse ingestion into Google Cloud Storage"
00:00:28    Info    ================================================================================
00:00:29    Info    Rank | ID      | Category       | MIME Type        | Distance | Similarity | Asset Name
00:00:29    Info    -----+---------+----------------+------------------+----------+------------+--------------------------------------------------
00:00:29    Info     #1  | IMG-001 | image_diagram  | image/png        |  0.1238  |   0.8762   | lakehouse_multimodal_architecture_diagram.png
00:00:29    Info     #2  | DOC-001 | google_doc_pdf | application/pdf  |  0.3276  |   0.6724   | Google_Apps_Script_Automation_Guide.pdf
00:00:29    Info     #3  | DOC-003 | google_doc_pdf | application/pdf  |  0.4567  |   0.5433   | Google_Docs_Collaborative_Publishing_Guide.pdf
00:00:29    Info
🎉 ALL MULTIMODAL VECTOR SEARCH TESTS COMPLETED WITH 100% SUCCESS!
00:00:29    Info
--- [6/6] ABSOLUTE CLEANUP: Purging Ephemeral Test Resources ---
00:00:30    Info    🗑️ Dropped Iceberg Table: multimodal_knowledge_assets
00:00:30    Info    🗑️ Trashed Google Doc: [xxxx-drive-doc1-id]
00:00:31    Info    🗑️ Trashed Google Doc: [xxxx-drive-doc2-id]
00:00:31    Info    🗑️ Trashed Google Doc: [xxxx-drive-doc3-id]
00:00:32    Info    🗑️ Removed BigQuery Dataset: lakehouse_multimodal_xxxx
00:00:34    Info    🗑️ Deleted GCS Bucket: gs://lakehouse-mm-xxxx
00:00:35    Info    ✨ Multimodal test cleanup complete. Zero residue on Drive, BigQuery, and GCS.
00:00:34    Notice  Execution completed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5.2 Rigorous Empirical Audit &amp;amp; Evaluation
&lt;/h3&gt;

&lt;p&gt;The empirical execution log confirms four pivotal architectural milestones:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Flawless End-to-End Execution Latency (35 Seconds Total)&lt;/strong&gt;:&lt;br&gt;
The complete sequence—spanning isolated GCP infrastructure provisioning, live document creation on Google Drive, PDF binary conversion, Gemini embedding generation, Parquet writing, four full BigQuery distributed vector searches, and complete resource teardown—completed in approximately &lt;strong&gt;35 seconds&lt;/strong&gt;. This consumes only &lt;strong&gt;9.7%&lt;/strong&gt; of the Google Apps Script 6-minute ceiling, proving high operational headroom.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Cross-Modal Semantic Discrimination&lt;/strong&gt;:&lt;br&gt;
Across all four test queries, the target assets were ranked &lt;strong&gt;#1 with statistically significant margin&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Query 1 (Apps Script Automation)&lt;/strong&gt;: Correctly isolated &lt;code&gt;DOC-001&lt;/code&gt; (&lt;code&gt;Google_Apps_Script_Automation_Guide.pdf&lt;/code&gt;) with distance &lt;code&gt;0.2500&lt;/code&gt; (similarity &lt;code&gt;0.7500&lt;/code&gt;), clearly outranking the Sheets guide (&lt;code&gt;0.6324&lt;/code&gt;) and Docs guide (&lt;code&gt;0.5681&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Query 2 (Spreadsheet Formulas &amp;amp; Pivot Tables)&lt;/strong&gt;: Matched &lt;code&gt;DOC-002&lt;/code&gt; (&lt;code&gt;Google_Sheets_Calculations_Analytics_Guide.pdf&lt;/code&gt;) as #1 with similarity &lt;code&gt;0.7935&lt;/code&gt;, demonstrating fine-grained semantic separation between distinct Workspace tools.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Query 3 (Superconducting Quantum Qubits)&lt;/strong&gt;: Matched the raw text whitepaper &lt;code&gt;TXT-001&lt;/code&gt; with an outstanding similarity of &lt;code&gt;0.8669&lt;/code&gt; (distance &lt;code&gt;0.1331&lt;/code&gt;), cleanly distinguishing physics from the unrelated biological text (&lt;code&gt;0.5012&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Query 4 (Visual System Architecture Diagram)&lt;/strong&gt;: Matched &lt;code&gt;IMG-001&lt;/code&gt; (&lt;code&gt;lakehouse_multimodal_architecture_diagram.png&lt;/code&gt;) as #1 with similarity &lt;code&gt;0.8762&lt;/code&gt;, proving that binary image assets with descriptive metadata participate seamlessly in unified semantic search alongside textual documents.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;True Multimodal Storage Coexistence&lt;/strong&gt;:&lt;br&gt;
The table inventory confirms that 28KB PDF documents, 70B image binaries, and 200B text files coexist within the identical Iceberg table schema on Google Cloud Storage, unified under standard 768-dimensional float embeddings.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Test Suite Safety Verification (Zero Residual Clutter)&lt;/strong&gt;:&lt;br&gt;
The post-test cleanup phase verified the immediate destruction of all temporary verification assets:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The test Iceberg table &lt;code&gt;multimodal_knowledge_assets&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;All three test Google Docs by explicit Drive File ID (&lt;code&gt;[xxxx-drive-doc1-id]&lt;/code&gt;, &lt;code&gt;[xxxx-drive-doc2-id]&lt;/code&gt;, &lt;code&gt;[xxxx-drive-doc3-id]&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;The test BigQuery dataset &lt;code&gt;lakehouse_multimodal_xxxx&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The test Cloud Storage bucket &lt;code&gt;gs://lakehouse-mm-xxxx&lt;/code&gt;.
Zero orphaned files or uncollected storage charges remain for the evaluator.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  6. Architectural Evaluation &amp;amp; Cost Comparison
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dimension&lt;/th&gt;
&lt;th&gt;Dedicated Vector DB (Pinecone / Milvus)&lt;/th&gt;
&lt;th&gt;Custom Microservice + Cloud SQL&lt;/th&gt;
&lt;th&gt;IcebergApp Multimodal Lakehouse&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Hosting Infrastructure&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Dedicated Cloud Instance or SaaS Cluster&lt;/td&gt;
&lt;td&gt;GKE / Cloud Run + Cloud SQL PostgreSQL (pgvector)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;100% Serverless (GAS + BigQuery + GCS)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Monthly Base Cost&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;$500 – $3,000 / month&lt;/td&gt;
&lt;td&gt;$150 – $600 / month&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$0.00 (Pure Pay-per-Query On-Demand)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Binary Payload Storage&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Not Supported (External S3/GCS required)&lt;/td&gt;
&lt;td&gt;External S3/GCS required&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Native in Table (&lt;code&gt;BYTES&lt;/code&gt; / Storage URI)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Google Drive Integration&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Webhooks + Complex OAuth Microservice&lt;/td&gt;
&lt;td&gt;Scheduled Batch Ingestion Cron Jobs&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Native 1-Line Ingestion (&lt;code&gt;DriveApp&lt;/code&gt; + &lt;code&gt;IcebergApp&lt;/code&gt;)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Document Versioning&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Manual vector ID tracking&lt;/td&gt;
&lt;td&gt;Custom relational snapshot tables&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Native Iceberg Snapshots &amp;amp; Time Travel&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Query Engine Interoperability&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Proprietary Vector API only&lt;/td&gt;
&lt;td&gt;SQL (pgvector)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Open Standard (BigQuery, Spark, Trino, DuckDB)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  7. Conclusion
&lt;/h2&gt;

&lt;p&gt;By combining Google Apps Script with Apache Iceberg and the Gemini embedding model, developers are no longer constrained by the traditional boundaries between structured spreadsheets, unstructured documents, and dedicated vector databases.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;True Multimodality&lt;/strong&gt;: Apache Iceberg's native binary support (&lt;code&gt;BYTES&lt;/code&gt;) allows Google Workspace assets (Docs, Sheets, Slides via automatic PDF export, and Forms), diagrams, and text records to live together in a single open Parquet table.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Elimination of SaaS Subscriptions&lt;/strong&gt;: Zero recurring fees for dedicated vector database infrastructure; queries execute on-demand with BigQuery acceleration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Turn-Key Accessibility&lt;/strong&gt;: With &lt;strong&gt;IcebergApp&lt;/strong&gt;, any Google Workspace developer can deploy a production-grade multimodal lakehouse vector engine using standard JavaScript.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI-Driven Google Drive File Discovery&lt;/strong&gt;: Because &lt;code&gt;file_id&lt;/code&gt; is preserved directly in the schema, vector search serves as an intelligent semantic file finder across Google Docs, Sheets, Slides, and Forms—allowing users to navigate directly to live collaborative assets and enabling scripts to trigger downstream Workspace automation (&lt;code&gt;DocumentApp&lt;/code&gt;, &lt;code&gt;SpreadsheetApp&lt;/code&gt;, &lt;code&gt;SlidesApp&lt;/code&gt;, &lt;code&gt;FormApp&lt;/code&gt;, &lt;code&gt;DriveApp&lt;/code&gt;).&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  References &amp;amp; Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub Repository&lt;/strong&gt;: &lt;a href="https://github.com/tanaikech/IcebergApp" rel="noopener noreferrer"&gt;https://github.com/tanaikech/IcebergApp&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Core Library Source&lt;/strong&gt;: &lt;a href="https://github.com/tanaikech/IcebergApp/blob/master/src/IcebergApp.js" rel="noopener noreferrer"&gt;&lt;code&gt;IcebergApp.js&lt;/code&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multimodal Test Suite Source&lt;/strong&gt;: &lt;a href="https://github.com/tanaikech/IcebergApp/blob/master/src/multimodal_vector_search_test.js" rel="noopener noreferrer"&gt;&lt;code&gt;multimodal_vector_search_test.js&lt;/code&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Companion Article 1 (Foundational Architecture)&lt;/strong&gt;: &lt;a href="https://medium.com/google-cloud/unifying-google-workspace-and-apache-iceberg-serverless-lakehouse-management-94a6cb43614c" rel="noopener noreferrer"&gt;Unifying Google Workspace and Apache Iceberg: Serverless Lakehouse Management&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Apache Iceberg Specification&lt;/strong&gt;: &lt;a href="https://iceberg.apache.org/spec/" rel="noopener noreferrer"&gt;https://iceberg.apache.org/spec/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Google Cloud BigQuery Iceberg Documentation&lt;/strong&gt;: &lt;a href="https://cloud.google.com/bigquery/docs/iceberg-tables" rel="noopener noreferrer"&gt;https://cloud.google.com/bigquery/docs/iceberg-tables&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gemini Embeddings API Documentation&lt;/strong&gt;: &lt;a href="https://ai.google.dev/gemini-api/docs/embeddings" rel="noopener noreferrer"&gt;https://ai.google.dev/gemini-api/docs/embeddings&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>googleworkspace</category>
      <category>googlecloud</category>
      <category>bigquery</category>
      <category>apacheiceberg</category>
    </item>
    <item>
      <title>Unifying Google Workspace and Apache Iceberg: Serverless Lakehouse Management</title>
      <dc:creator>Tanaike</dc:creator>
      <pubDate>Sun, 06 Sep 2026 07:35:05 +0000</pubDate>
      <link>https://dev.to/gde/unifying-google-workspace-and-apache-iceberg-serverless-lakehouse-management-ep3</link>
      <guid>https://dev.to/gde/unifying-google-workspace-and-apache-iceberg-serverless-lakehouse-management-ep3</guid>
      <description>&lt;p&gt;&lt;em&gt;Turn Google Sheets into a Petabyte Lakehouse with Sub-Second ACID Queries.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Abstract
&lt;/h2&gt;

&lt;p&gt;Google Sheets faces severe scalability bottlenecks when handling large enterprise datasets, including a strict 10-million cell limit, crippling CRUD latency, and Google Apps Script's execution and memory boundaries. This article presents &lt;strong&gt;&lt;a href="https://github.com/tanaikech/IcebergApp" rel="noopener noreferrer"&gt;IcebergApp&lt;/a&gt;&lt;/strong&gt;, an open-source library bridging Google Workspace to Apache Iceberg lakehouses on Google Cloud. By utilizing BigQuery as a serverless query accelerator to execute predicate pushdown over Iceberg metadata, IcebergApp eliminates spreadsheet latency, ensures ACID transactions, and enables sub-second queries, time travel, and agentic AI integration directly from Google Apps Script.&lt;/p&gt;




&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Modern enterprise data architectures are rapidly converging on open table formats. Among them, &lt;a href="https://iceberg.apache.org/" rel="noopener noreferrer"&gt;Apache Iceberg&lt;/a&gt; has emerged as the de facto industry standard for managing massive analytical datasets on object storage (&lt;a href="https://iceberg.apache.org/docs/latest/" rel="noopener noreferrer"&gt;Apache Iceberg Documentation&lt;/a&gt;). Google Cloud has accelerated this paradigm shift by introducing the &lt;a href="https://docs.cloud.google.com/lakehouse/docs/lakehouse-basics" rel="noopener noreferrer"&gt;Borderless Lakehouse architecture&lt;/a&gt; and native support for Apache Iceberg tables within BigQuery (&lt;a href="https://docs.cloud.google.com/bigquery/docs/iceberg-tables" rel="noopener noreferrer"&gt;BigQuery Iceberg Documentation&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Traditionally, data lakes stored unstructured or semi-structured files directly on object storage like Google Cloud Storage (GCS). While cost-effective, this approach introduced significant operational bottlenecks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Directory listings suffered from high I/O latency as file counts scaled into the millions.&lt;/li&gt;
&lt;li&gt;Multi-engine concurrent writes lacked ACID transaction guarantees, leading to dirty reads and corrupt states.&lt;/li&gt;
&lt;li&gt;Schema evolution and in-place row updates required full table rewrites.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Apache Iceberg eliminates these limitations by introducing a hierarchical metadata tree (Manifest Lists and Manifest Files) that manages snapshots independently of the physical file layout (&lt;a href="https://iceberg.apache.org/spec/" rel="noopener noreferrer"&gt;Apache Iceberg Specification&lt;/a&gt;). Query engines can evaluate partition boundaries and column-level min/max statistics entirely within this metadata layer. This technique, known as &lt;strong&gt;Predicate Pushdown&lt;/strong&gt;, prunes irrelevant data files before a single byte of actual storage is scanned.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbyust5m26up6kycukput.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbyust5m26up6kycukput.jpg" alt="Figure 1: Apache Iceberg Hierarchical Metadata Architecture and Predicate Pushdown Pruning" width="800" height="447"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Figure 1: The Apache Iceberg hierarchical metadata tree. Query engines inspect the Catalog and Table Metadata to evaluate Manifest Lists, using column statistics to prune unneeded Manifest Files and skip irrelevant Parquet data files.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;As illustrated in &lt;strong&gt;Figure 1&lt;/strong&gt;, Apache Iceberg organizes table state into four distinct, immutable layers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Catalog Layer (Top)&lt;/strong&gt;: The BigQuery or Iceberg REST Catalog maintains the current active pointer to the latest table metadata file.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Table Metadata File (&lt;code&gt;vN.metadata.json&lt;/code&gt;)&lt;/strong&gt;: Defines the table schema, partition specs, and an append-only log of snapshot historical states.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Manifest List (&lt;code&gt;snap-XYZ.avro&lt;/code&gt;)&lt;/strong&gt;: Records all manifest files that compose a specific snapshot, along with each manifest's partition range boundaries.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Manifest Files (&lt;code&gt;*.avro&lt;/code&gt;) &amp;amp; Parquet Data Files (Bottom)&lt;/strong&gt;: Manifest files hold row-level file paths and column-level summary statistics (Min/Max values, null counts). When a query executes, Predicate Pushdown evaluates column filters directly against these manifest statistics—flagging irrelevant data files as skipped (shown on the bottom left) and scanning only the files containing relevant rows (highlighted in green on the bottom right).&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Motivation: Why Google Apps Script Needs Apache Iceberg
&lt;/h3&gt;

&lt;p&gt;To understand why uniting Google Apps Script with Apache Iceberg is vital, consider how modern enterprises actually handle frontline data:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The "Spreadsheet-as-a-Database" Dilemma&lt;/strong&gt;: Google Sheets is the most accessible, collaborative data interface in business. Millions of automated workflows rely on Google Apps Script to ingest Google Forms submissions, IoT telemetry, ERP reports, and transactional records directly into spreadsheets. However, treating Google Sheets as a production database inevitably leads to failure: workbooks quickly hit the hard 10-million cell limit, &lt;code&gt;getValues()&lt;/code&gt; / &lt;code&gt;setValues()&lt;/code&gt; operations suffer multi-second freezes from formula recalculations, and V8 runtime heap exhaustion occurs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Pitfalls of Traditional Database Workarounds for GAS&lt;/strong&gt;: When developers attempt to migrate data out of Sheets into external databases, serverless GAS introduces steep hurdles:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Relational Databases (Cloud SQL / MySQL / PostgreSQL)&lt;/strong&gt;: Require persistent JDBC connections, VPC tunneling, or static IP whitelisting—mechanisms that serverless Apps Script cannot natively support without complex proxy middleware—along with expensive, 24/7 provisioned database instances.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Proprietary Data Warehouses&lt;/strong&gt;: Incur high ingestion costs, lock organizations into closed ecosystems, and impose restrictive quotas on frequent row-level DML updates.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Raw Object Storage (GCS / Parquet / CSV)&lt;/strong&gt;: While cost-effective, raw storage lacks ACID transaction guarantees, schema enforcement, and concurrent write safety. Attempting to parse raw Parquet files in GAS exhausts runtime quotas within seconds.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Apache Iceberg Breakthrough&lt;/strong&gt;: Apache Iceberg solves every one of these pain points simultaneously:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Zero Vendor Lock-In&lt;/strong&gt;: Data resides as open Parquet files in customer-owned Cloud Storage, concurrently accessible by Spark, Trino, and Snowflake.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ACID Transaction Guarantees&lt;/strong&gt;: Multiple Apps Script triggers, web apps, and data pipelines can write simultaneously without lock contention or data corruption.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Serverless Metadata Acceleration&lt;/strong&gt;: Hierarchical metadata enables sub-second Predicate Pushdown, keeping Apps Script memory consumption constant ($O(1)$) and queries within 1–2 seconds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Snapshot Isolation &amp;amp; Audit Recovery&lt;/strong&gt;: Native time-travel snapshots protect against accidental spreadsheet overwrites, restoring historical ground truth in seconds without database downtime.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Democratizing Apache Iceberg for GAS Developers (Effortless Accessibility)&lt;/strong&gt;: While Apache Iceberg provides an enterprise-grade lakehouse specification, it has traditionally been confined to heavy data engineering ecosystems requiring Java, Scala, Python (PyIceberg), or distributed engines like Spark and Trino. For Google Apps Script developers, interacting with Iceberg directly meant untangling complex REST catalog endpoints, managing low-level BigQuery Lakehouse API payloads, and dealing with intricate Avro/Parquet serialization. Therefore, a primary motivation of this project is to &lt;strong&gt;make Apache Iceberg effortlessly accessible within Google Apps Script&lt;/strong&gt;. By wrapping complex catalog coordination and SQL DML generation into a fluent, spreadsheet-native JavaScript interface (&lt;code&gt;table.insertValues()&lt;/code&gt;, &lt;code&gt;table.getValues()&lt;/code&gt;, &lt;code&gt;table.asOf()&lt;/code&gt;), any Workspace developer can deploy and manage enterprise lakehouses in minutes without managing servers or deep data engineering tooling.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Despite these technological leaps, a critical gap persists: business users and automation workflows live inside &lt;a href="https://workspace.google.com/" rel="noopener noreferrer"&gt;Google Workspace&lt;/a&gt;. Frontline operations run on &lt;a href="https://www.google.com/sheets/about/" rel="noopener noreferrer"&gt;Google Sheets&lt;/a&gt;, while automated pipelines rely heavily on &lt;a href="https://developers.google.com/apps-script" rel="noopener noreferrer"&gt;Google Apps Script (GAS)&lt;/a&gt;. Until now, querying or mutating enterprise Iceberg tables from Apps Script required setting up complex intermediary microservices, managing specialized JDBC drivers, or writing verbose API wrappers.&lt;/p&gt;

&lt;p&gt;To bridge this architectural disparity, I developed &lt;strong&gt;&lt;a href="https://github.com/tanaikech/IcebergApp" rel="noopener noreferrer"&gt;IcebergApp&lt;/a&gt;&lt;/strong&gt;. Built on top of the BigQuery Advanced Service and Google Cloud's serverless runtime catalog, IcebergApp provides a fluent, spreadsheet-native interface that allows Apps Script developers to manage, query, mutate, and export Apache Iceberg tables with absolute simplicity.&lt;/p&gt;


&lt;h2&gt;
  
  
  Architecture of IcebergApp
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpseb7yc9mt70ed0p7k14.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpseb7yc9mt70ed0p7k14.jpg" alt="Figure 2: End-to-End IcebergApp Architecture and Bidirectional Workflow" width="800" height="447"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Figure 2: End-to-end architecture of IcebergApp. Frontline Google Workspace applications connect via Apps Script to BigQuery's distributed engine, orchestrating Parquet data and Avro metadata on Google Cloud Storage.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;IcebergApp operates by decoupling compute from storage, honoring the first principles of the Lakehouse paradigm. As depicted in &lt;strong&gt;Figure 2&lt;/strong&gt;, the architecture is structured into three coordinated tiers:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Presentation &amp;amp; Application Layer (Top)&lt;/strong&gt;: Business users interact through standard Google Workspace tools—Google Sheets, Forms, and Docs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Client Library &amp;amp; Orchestration Layer (Middle)&lt;/strong&gt;: IcebergApp runs natively inside the Google Apps Script V8 runtime, translating high-level JavaScript calls and 2D arrays into optimized SQL DML statements without requiring external servers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compute &amp;amp; Storage Decoupled Tier (Bottom)&lt;/strong&gt;: Apps Script delegates compute execution to BigQuery, which acts as a serverless query accelerator. BigQuery interfaces with the Iceberg REST Catalog, performs metadata-driven file pruning, and reads/writes Parquet files and Avro metadata stored in customer-owned Google Cloud Storage buckets.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Instead of forcing the Apps Script V8 runtime to download and parse low-level Avro metadata directly—which would quickly exhaust the 6-minute script execution limit and memory quotas—IcebergApp orchestrates BigQuery as a serverless distributed query accelerator. BigQuery inspects the Iceberg REST catalog and metadata tree, performs file pruning, and returns only the finalized, structured records back to Google Apps Script as lightweight 2D arrays.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmug3jaaliik4yvlpzn72.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmug3jaaliik4yvlpzn72.jpg" alt="Figure 3: Execution Bottleneck vs. Serverless Acceleration: Overcoming Google Apps Script Limits with IcebergApp" width="800" height="447"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Figure 3: Performance contrast between traditional client-side data parsing in Apps Script and IcebergApp's serverless BigQuery acceleration.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Figure 3&lt;/strong&gt; illustrates the vital performance breakthrough achieved by this architecture:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Traditional Direct Processing (Left Panel)&lt;/strong&gt;: If Apps Script attempts to process large lakehouse datasets directly, the V8 runtime must download massive Avro metadata trees and raw Parquet files over network I/O. This immediately causes V8 memory heap exhaustion ("Exceeded memory limit"), incurs unbounded $O(N)$ client latency, and inevitably triggers the fatal 6-minute execution timeout.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IcebergApp + BigQuery Serverless Accelerator (Right Panel)&lt;/strong&gt;: By delegating heavy compute to BigQuery, Apps Script retains a constant $O(1)$ memory footprint. BigQuery performs parallel partition pruning and instant metadata resolution across Google Cloud's distributed infrastructure, streaming structured results directly into Google Sheets in just 1–2 seconds regardless of whether the target table holds 10 thousand or 100 million records.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  In-Depth Latency &amp;amp; Architectural Evaluation: Why BigQuery Accelerates Search
&lt;/h3&gt;

&lt;p&gt;To rigorously understand why routing queries through the BigQuery API yields dramatically lower search latency than native Apps Script processing, consider the underlying physical and distributed execution mechanics:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Metadata-Driven Predicate Pushdown&lt;/strong&gt;: Iceberg manifest files store column-level summary statistics (minimum and maximum values per data file). When a query includes a filter (e.g., &lt;code&gt;WHERE temperature &amp;gt; 80.0&lt;/code&gt;), BigQuery evaluates these boundaries entirely within the metadata tier, skipping 90%–99.9% of Parquet data files on Cloud Storage without reading a single byte of storage payload.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Columnar Projection Pruning&lt;/strong&gt;: In wide tables (e.g., 50 columns), requesting only two fields (&lt;code&gt;columns: ["device_name", "temperature"]&lt;/code&gt;) instructs BigQuery to issue HTTP Range Requests solely for the relevant column chunks within Parquet files, reducing network I/O by up to 96%.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Jupiter Petabit Interconnect &amp;amp; Vectorized C++ Engine&lt;/strong&gt;: BigQuery and Cloud Storage reside within Google's internal datacenter fabric (Jupiter network), operating at petabit-per-second bisection bandwidth with sub-millisecond inter-rack latency. BigQuery’s C++ Dremel engine decompresses and processes Parquet records in parallel using SIMD vector instructions, filtering millions of rows in hundreds of milliseconds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Minimizing Client Transfer Payloads ($O(N) \to O(k)$)&lt;/strong&gt;: Instead of downloading raw gigabytes of Parquet files into the GAS V8 sandbox, Apps Script receives only the final, filtered result set ($k$ records) formatted as clean JSON. Client-side memory and CPU consumption drop to near zero.&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;
  
  
  Rigorous Benchmark Comparison across Architectural Baselines
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dimension&lt;/th&gt;
&lt;th&gt;Baseline A: Direct GCS/Parquet in GAS&lt;/th&gt;
&lt;th&gt;Baseline B: Large Google Sheets (&lt;code&gt;getValues&lt;/code&gt;)&lt;/th&gt;
&lt;th&gt;IcebergApp (BigQuery Accelerator)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Execution Engine&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Single-threaded GAS V8 Sandbox&lt;/td&gt;
&lt;td&gt;GAS V8 + Spreadsheet Calculation Engine&lt;/td&gt;
&lt;td&gt;BigQuery Distributed MPP (Thousands of Slots)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Network Proximity&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Public/Internal API to GCS&lt;/td&gt;
&lt;td&gt;Internal Google Sheets RPC&lt;/td&gt;
&lt;td&gt;Google Jupiter Petabit Datacenter Fabric&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;I/O Filtering&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Full binary download of Parquet/Avro&lt;/td&gt;
&lt;td&gt;Full sheet serialized into memory&lt;/td&gt;
&lt;td&gt;Metadata-level Predicate Pushdown + Column Pruning&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Memory Footprint&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Exceeds V8 heap limit ("Exceeded memory limit")&lt;/td&gt;
&lt;td&gt;Saturated by thousands of cell objects&lt;/td&gt;
&lt;td&gt;Constant $O(1)$ memory footprint in GAS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Query Latency (1M Rows)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Timeout / Crash (&amp;gt; 6 min)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;15–60+ seconds (or Timeout)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1.2 – 2.0 seconds&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;[!NOTE]&lt;br&gt;
&lt;strong&gt;Boundary Condition on Base Latency&lt;/strong&gt;: For microscopic datasets (e.g., 5–10 rows in a blank sheet), local &lt;code&gt;SpreadsheetApp&lt;/code&gt; cell reads execute in approximately 50–100 milliseconds, whereas the BigQuery API requires a baseline overhead of approximately 0.8–1.5 seconds for job creation, IAM evaluation, and SQL compilation. However, as dataset size scales into enterprise lakehouse dimensions (tens of thousands to hundreds of millions of records), spreadsheet-native scans degrade exponentially ($O(N)$), while IcebergApp maintains a flat, near-constant 1–2 second execution profile.&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  The Scalability Wall: Google Sheets CRUD Latency vs. IcebergApp
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fipdzny26ir33607wa9a6.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fipdzny26ir33607wa9a6.jpg" alt="Figure 4: The Google Sheets Scalability Crisis vs. IcebergApp Lakehouse Solution" width="800" height="447"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Figure 4: Comprehensive comparison of core CRUD operations between standalone Google Sheets and IcebergApp.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Google Sheets is the world's most intuitive and collaborative frontline data interface. However, as summarized in &lt;strong&gt;Figure 4&lt;/strong&gt;, developers attempting to utilize Google Sheets as an analytical data store or high-volume operational backend inevitably confront four fatal bottlenecks:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Capacity &amp;amp; Quotas (10-Million Cell Ceiling)&lt;/strong&gt;: Google Sheets enforces a hard limit of 10 million cells per workbook. High-frequency IoT telemetry, audit logs, and enterprise transaction streams quickly exhaust this quota, causing workbook corruption, sluggish loads, and script terminations. In contrast, IcebergApp backs Workspace with infinite, petabyte-scale Parquet storage on Google Cloud Storage.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Search &amp;amp; Query Latency (V8 Memory Overload)&lt;/strong&gt;: Querying records in large sheets requires pulling ranges into the Apps Script V8 runtime using &lt;code&gt;getDataRange().getValues()&lt;/code&gt;. Serializing hundreds of thousands of cells across Google's internal RPC layer saturates script memory (triggering V8 heap exhaustion) and frequently triggers the 6-minute execution timeout before data processing even begins. IcebergApp overcomes this by pushing query filters directly into Iceberg metadata, achieving 1–2 second retrieval times regardless of table size.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Insert &amp;amp; Append Latency (Calculation Freeze)&lt;/strong&gt;: Adding batches of rows with &lt;code&gt;appendRow()&lt;/code&gt; or &lt;code&gt;setValues()&lt;/code&gt; forces Google Sheets' calculation engine to recalculate formula trees across the entire workbook, re-evaluate conditional formatting rules, and serialize cell revision histories, creating multi-second freezes. With IcebergApp, &lt;code&gt;table.insertValues()&lt;/code&gt; streams records directly into Parquet files with zero client-side recalculation overhead.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Update &amp;amp; Delete Latency (The Critical Pain Point)&lt;/strong&gt;: Updating scattered records requires searching the entire grid in memory, modifying values, and rewriting massive 2D arrays back to the sheet. Worse, deleting rows matching specific criteria using &lt;code&gt;SpreadsheetApp.deleteRow(i)&lt;/code&gt; in a loop is notoriously catastrophic: each deletion shifts rows upward, recalculates row coordinates, and triggers an independent API payload. Deleting merely 100 rows can easily freeze the script for minutes or cause runtime timeouts. IcebergApp replaces this with atomic ACID DML updates and deletions that execute on storage metadata in seconds.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  How IcebergApp Resolves the Crisis
&lt;/h3&gt;

&lt;p&gt;IcebergApp fundamentally resolves this crisis by adhering to the &lt;strong&gt;Separation of Presentation and Storage&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Google Sheets as an Agile, Ephemeral View&lt;/strong&gt;: Google Sheets is retained exclusively as a lightweight, human-friendly 2D viewing and input surface (holding only current micro-batches or aggregated query results).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero-Latency Appends (&lt;code&gt;table.insertValues&lt;/code&gt;)&lt;/strong&gt;: Incoming rows staged on a sheet are flushed directly into Iceberg Parquet files on Cloud Storage via atomic ACID commits in 1–2 seconds. The sheet is instantly cleared, maintaining permanent $O(1)$ grid performance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sub-Second Search with Predicate Pushdown (&lt;code&gt;table.getValues&lt;/code&gt;)&lt;/strong&gt;: Filter queries evaluate column Min/Max statistics within Iceberg metadata manifests. BigQuery skips irrelevant data files, returning only filtered rows to Apps Script in seconds without scanning the entire dataset.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;In-Place ACID Mutation &amp;amp; Instant Deletion (&lt;code&gt;table.update&lt;/code&gt; &amp;amp; &lt;code&gt;table.deleteRows&lt;/code&gt;)&lt;/strong&gt;: Instead of row-by-row iteration in Apps Script, updates and deletions are dispatched as distributed SQL DML executed directly on Iceberg metadata and data files. Deleting millions of archived rows is completed atomically in seconds, permanently banishing &lt;code&gt;deleteRow()&lt;/code&gt; loops.&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Usage
&lt;/h2&gt;
&lt;h3&gt;
  
  
  1. Prerequisites and Environment Binding
&lt;/h3&gt;

&lt;p&gt;To use IcebergApp, your script project must be linked to a Google Cloud Platform (GCP) project that has the BigQuery API and Google Cloud Storage JSON API enabled.&lt;/p&gt;

&lt;p&gt;In the Apps Script editor:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add the &lt;strong&gt;BigQuery API&lt;/strong&gt; (v2) under &lt;strong&gt;Services&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Enable the &lt;code&gt;appsscript.json&lt;/code&gt; manifest file in &lt;strong&gt;Project Settings&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Include the required OAuth scopes for BigQuery, Google Sheets, Drive (for temporary test spreadsheet management), Cloud Storage (for ephemeral bucket life-cycling), and external requests:
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"timeZone"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Asia/Tokyo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"dependencies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"enabledAdvancedServices"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"userSymbol"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"BigQuery"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"serviceId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bigquery"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"v2"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"runtimeVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"V8"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"oauthScopes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"https://www.googleapis.com/auth/bigquery"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"https://www.googleapis.com/auth/spreadsheets"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"https://www.googleapis.com/auth/drive"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"https://www.googleapis.com/auth/devstorage.read_write"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"https://www.googleapis.com/auth/script.external_request"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Add the &lt;code&gt;src/IcebergApp.js&lt;/code&gt; library file from the &lt;a href="https://github.com/tanaikech/IcebergApp" rel="noopener noreferrer"&gt;IcebergApp GitHub repository&lt;/a&gt; directly to your project, or add it as a library.&lt;/p&gt;
&lt;h3&gt;
  
  
  2. Initializing and Creating an Iceberg Table
&lt;/h3&gt;

&lt;p&gt;You initialize the client by referencing your project ID, catalog name (BigQuery dataset), and region location:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;PROJECT_ID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;your-gcp-project-id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;CATALOG_NAME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;lakehouse_catalog&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;REGION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;asia-northeast1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Crucial: Align BigQuery and GCS region&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;IcebergApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;openByCatalog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;PROJECT_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;CATALOG_NAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;REGION&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;schema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;INT64&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;REQUIRED&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;device_name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;STRING&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;temperature&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;FLOAT64&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;status&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;STRING&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;recorded_at&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;TIMESTAMP&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;table&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;IoTMetrics&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;storageUri&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gs://your-lakehouse-bucket/tables/IoTMetrics&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;partitionBy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;DATE(recorded_at)&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;clusterBy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Bidirectional Data Ingestion
&lt;/h3&gt;

&lt;p&gt;IcebergApp treats Google Sheets 2D arrays as first-class citizens. You can append tabular data directly into an Iceberg table without converting types manually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sheetValues&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;device_name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;temperature&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;status&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;recorded_at&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1001&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Sensor_Alpha&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;24.8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;NORMAL&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;()],&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1002&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Sensor_Beta&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;89.2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;CRITICAL&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;()],&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1003&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Sensor_Gamma&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;21.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;NORMAL&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;()],&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="c1"&gt;// Appends records atomically via ACID transaction&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;insertedCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insertValues&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sheetValues&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Inserted &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;insertedCount&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; rows.`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Querying with Predicate Pushdown and Direct Sheet Export
&lt;/h3&gt;

&lt;p&gt;When retrieving records, IcebergApp allows you to project columns and define filter conditions. Because these filters are pushed down to Iceberg's metadata manifests, execution completes in seconds even over massive datasets:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Query only matching rows&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getValues&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;device_name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;temperature&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;status = 'CRITICAL'&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// Output: [["device_name", "temperature"], ["Sensor_Beta", "89.2"]]&lt;/span&gt;

&lt;span class="c1"&gt;// Or stream the filtered result set directly into a Google Spreadsheet&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ss&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;SpreadsheetApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;IoT_Critical_Alerts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sheet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ss&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getSheets&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exportToSheet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;A1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;status = 'CRITICAL'&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Exported alerts to: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ss&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getUrl&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. In-Place Row Mutation and Deletion
&lt;/h3&gt;

&lt;p&gt;IcebergApp supports ACID-compliant modifications directly against Cloud Storage without full table rewrites:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Atomically update row attributes&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;updateResult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;status = 'RESOLVED'&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id = 1002&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;updateResult&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Atomically delete rows satisfying a condition&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;deleteResult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deleteRows&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;status = 'ARCHIVED'&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;deleteResult&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  6. Snapshot Isolation and Time Travel
&lt;/h3&gt;

&lt;p&gt;Because Iceberg records changes as immutable state transitions, you can query historical data without creating database backups:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Query table state as it existed 15 minutes ago&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pastTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pastRecords&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;asOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pastTime&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;getValues&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id = 1002&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pastRecords&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resetSnapshot&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Reset state back to HEAD&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Testing: 5 GAS-Centric Practical Scenarios
&lt;/h2&gt;

&lt;p&gt;Testing a data lakehouse connector inside Google Apps Script requires validating high-concurrency storage operations against Workspace runtime constraints. The &lt;code&gt;test.js&lt;/code&gt; script in the &lt;a href="https://github.com/tanaikech/IcebergApp" rel="noopener noreferrer"&gt;IcebergApp repository&lt;/a&gt; implements a &lt;strong&gt;Pure Lifecycle Architecture&lt;/strong&gt; that dynamically provisions necessary cloud resources and executes end-to-end tests before purging all created entities.&lt;/p&gt;

&lt;p&gt;Below are 5 practical testing scenarios demonstrating how IcebergApp addresses real-world challenges unique to Google Apps Script. &lt;em&gt;(Note: Running this automated lifecycle test suite requires configuring your &lt;code&gt;appsscript.json&lt;/code&gt; with all 5 OAuth scopes detailed in the Prerequisites section: BigQuery, Spreadsheets, Drive, Cloud Storage &lt;code&gt;devstorage.read_write&lt;/code&gt;, and &lt;code&gt;script.external_request&lt;/code&gt;.)&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Scenario 1: Zero-Residue Infrastructure Life-Cycling
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3ahg90he1m82bn0ot4m7.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3ahg90he1m82bn0ot4m7.jpg" alt="Figure 5: Zero-Residue Infrastructure Life-Cycling in Google Apps Script" width="800" height="447"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Figure 5: Automated test lifecycle implemented in test.js. Transient cloud resources are dynamically provisioned on demand and guaranteed to be purged in a finally block.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In serverless development, automated continuous integration (CI) tests frequently leave behind orphaned cloud resources—temporary BigQuery datasets, abandoned Cloud Storage buckets, and stray Google Spreadsheets. Over time, these orphaned artifacts accumulate cloud billing costs, consume project quotas, and clutter production namespaces.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Figure 5&lt;/strong&gt; details how IcebergApp eliminates cloud clutter through a deterministic, three-phase &lt;strong&gt;Pure Lifecycle Architecture&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Dynamic Ephemeral Provisioning (Left Panel)&lt;/strong&gt;: Before executing assertions, &lt;code&gt;test.js&lt;/code&gt; dynamically provisions an isolated BigQuery dataset (&lt;code&gt;lakehouse_test_[timestamp]&lt;/code&gt;) and a uniquely tagged Google Cloud Storage bucket (&lt;code&gt;gs://lakehouse-iceberg-test-[project-id]-[timestamp]&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Autonomous End-to-End Execution (Center Panel)&lt;/strong&gt;: The suite constructs an Iceberg table, inserts 2D arrays, validates Predicate Pushdown, verifies snapshot time-travel queries, performs atomic DML mutations, and exports results into a newly created Google Spreadsheet.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Guaranteed Absolute Teardown (Right Panel)&lt;/strong&gt;: Wrapped in a robust &lt;code&gt;try...finally&lt;/code&gt; block, the test suite guarantees that regardless of test outcome (pass or fail), all created entities—the Iceberg table, the temporary Google Spreadsheet, the ephemeral BigQuery dataset, and the GCS bucket—are completely dropped and destroyed, restoring the GCP and Workspace environments to a pristine zero-residue state in under 25 seconds:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;00:00:00  Info    🚀 Starting IcebergApp Automated Test Suite (Stage 3/4 Protocol 17 Compliance)
00:00:00  Info    --- STEP 0-A: Ensuring Isolated Dataset [lakehouse_test_[test-run-id]] ---
00:00:01  Info    ⚡ Dataset [lakehouse_test_[test-run-id]] absent. Provisioning at [asia-northeast1]...
00:00:03  Info    ✅ Dataset [lakehouse_test_[test-run-id]] created at [asia-northeast1].
...
00:00:20  Info    --- ABSOLUTE CLEANUP: Purging ephemeral test resources ---
00:00:20  Info    🗑️ Dropped Iceberg table: Test_Iceberg_[test-run-id]
00:00:21  Info    🗑️ Trashed temporary Spreadsheet: [spreadsheet-id]
00:00:22  Info    🗑️ Removed Ephemeral BigQuery Dataset: [lakehouse_test_[test-run-id]]
00:00:24  Info    🗑️ Deleted Ephemeral GCS Bucket: gs://lakehouse-iceberg-test-[project-id]-[test-run-id]
00:00:24  Info    ✨ CLEANUP COMPLETED: Workspace restored to pure state.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Scenario 2: High-Volume Spreadsheet Data Ingestion (The 10-Million Cell Ceiling)
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjls4gbpl3rnizu7i3eyl.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjls4gbpl3rnizu7i3eyl.jpg" alt="Figure 6: High-Volume Spreadsheet Data Ingestion: Bypassing the 10-Million Cell Limit" width="800" height="447"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Figure 6: Micro-batch streaming pipeline. Google Sheets serves as an ephemeral ingestion buffer, flushing rows into Iceberg Parquet storage and resetting to maintain permanent O(1) responsiveness.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Google Sheets enforces a hard ceiling of 10 million cells per workbook. For enterprise applications ingesting continuous sensor feeds, POS terminal transaction logs, or high-volume Google Forms submissions, this quota is reached quickly, resulting in sheet locking, browser crashes, and automation failure.&lt;/p&gt;

&lt;p&gt;As illustrated in &lt;strong&gt;Figure 6&lt;/strong&gt;, IcebergApp redefines Google Sheets as a high-throughput &lt;strong&gt;Transient Ingestion Buffer&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Transient Buffer Staging (Left Panel)&lt;/strong&gt;: Incoming operational data is temporarily recorded into an active Google Sheet. Because the sheet is never used as long-term storage, cell counts stay well below the 10-million limit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Atomic Micro-Batch Streaming (Center Panel)&lt;/strong&gt;: A time-driven Apps Script trigger or event hook reads the range using &lt;code&gt;sheet.getDataRange().getValues()&lt;/code&gt; and flushes the entire 2D array directly into Iceberg Parquet files on Cloud Storage using &lt;code&gt;table.insertValues(sheetValues)&lt;/code&gt;. The data is committed via an atomic ACID transaction in 1–2 seconds without calculating sheet formulas or inflating workbook size.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Instant Grid Reset (Right Panel)&lt;/strong&gt;: Upon successful commit, the script clears the active sheet range. The spreadsheet immediately resets to row zero, maintaining permanent $O(1)$ UI responsiveness, while analytical data accumulates limitlessly across petabytes of durable Iceberg storage.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Scenario 3: Predicate Pushdown over BigQuery Quotas
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9ykpgaw1d95lxpwo7m7w.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9ykpgaw1d95lxpwo7m7w.jpg" alt="Figure 7: Predicate Pushdown and Metadata Pruning in Apache Iceberg and BigQuery" width="800" height="447"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Figure 7: Predicate pushdown evaluation mechanics. BigQuery inspects Iceberg Avro manifests to prune unneeded files before scanning storage.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;When Google Apps Script developers query external data stored in flat files (like raw CSV or JSON on Cloud Storage), query engines must scan every single byte from beginning to end. Over millions of rows, this full-table scan consumes significant BigQuery query analysis quotas, drives up cloud costs, and frequently triggers Apps Script's 6-minute execution timeout.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Figure 7&lt;/strong&gt; visualizes how IcebergApp and BigQuery eliminate this bottleneck through &lt;strong&gt;Predicate Pushdown and Metadata Pruning&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Filter Query Submission&lt;/strong&gt;: An Apps Script function issues a targeted query, such as &lt;code&gt;table.getValues({ where: "price &amp;gt; 1000.0" })&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Metadata Evaluation&lt;/strong&gt;: BigQuery evaluates the query filter against Iceberg’s Avro manifest files. The manifest stores precomputed minimum and maximum values (&lt;code&gt;min_price&lt;/code&gt;, &lt;code&gt;max_price&lt;/code&gt;) for every physical Parquet file.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Physical File Pruning&lt;/strong&gt;: Files whose &lt;code&gt;max_price&lt;/code&gt; is below 1000.0 are flagged as skipped (costing zero bytes of physical data scan). BigQuery only reads the byte ranges of the matching Parquet files from Cloud Storage, completing the query in approximately 1.2 seconds and returning the structured result back to Apps Script:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;00:00:07  Info    --- STEP 3: Querying with Predicate Pushdown ---
00:00:08  Info    Result (Headers + Rows): [["id","product","price"],["101","Quantum Sensor Alpha","1500.0"],["102","Superconducting Coil","3200.5"]]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Scenario 4: In-Place Record Mutation (ACID Updates)
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F20u6oyg3mejt35zpw0ha.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F20u6oyg3mejt35zpw0ha.jpg" alt="Figure 8: In-Place Record Mutation: Atomic ACID DML Updates in Apache Iceberg" width="800" height="447"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Figure 8: In-place record mutation contrast. Traditional data lakes require full-file rewrites, whereas IcebergApp performs atomic ACID updates directly on metadata and storage.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Traditional data lakes built on raw Cloud Storage objects are fundamentally immutable: individual rows cannot be updated in place. If an Apps Script automation needs to deduct inventory stock or update an order approval flag, developers were traditionally forced to download the entire multi-gigabyte Parquet or CSV file into memory, update the row in code, and re-upload the entire file. This process causes extreme network latency, risks concurrent write conflicts, and easily exceeds GAS memory limits.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Figure 8&lt;/strong&gt; contrasts this broken legacy workflow with IcebergApp's atomic in-place mutation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Legacy Approach (Left Panel)&lt;/strong&gt;: Multi-step download, in-memory mutation, and complete dataset overwrite—resulting in severe lock contention, dirty reads, and minutes of execution time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IcebergApp Atomic DML (Right Panel)&lt;/strong&gt;: Calling &lt;code&gt;table.update("stock = stock - 2", "id = 101")&lt;/code&gt; delegates the mutation directly to BigQuery, which applies the modification at the storage tier using Iceberg ACID transaction semantics. Positional or equality delete manifests and updated data slices are committed instantaneously, guaranteeing immediate read-after-write consistency without full-table rewrites:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Atomically deduct stock directly on storage&lt;/span&gt;
&lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;stock = stock - 2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id = 101&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The test confirms the update succeeds in place in under two seconds without data corruption or lock conflicts.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scenario 5: Point-in-Time Data Recovery (Time Travel Audit)
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4v81h5ckvbukqfyi8ywm.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4v81h5ckvbukqfyi8ywm.jpg" alt="Figure 9: Point-in-Time Data Recovery: Snapshot Isolation and Time Travel in Apache Iceberg" width="800" height="447"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Figure 9: Snapshot timeline and time-travel query mechanism. Developers can query previous table states (Snapshot 2) even after faulty overwrites (Snapshot 3) without restoring database backups.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Human error in spreadsheet operations is inevitable. Accidental formula overwrites, faulty batch updates, or automated script glitches can overwrite critical business rows. Recovering past data in traditional databases requires restoring monolithic database backups—a slow, expensive process that takes hours and halts production.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Figure 9&lt;/strong&gt; illustrates how IcebergApp leverages Iceberg’s native &lt;strong&gt;Snapshot Isolation&lt;/strong&gt; to achieve instant time-travel audits:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Immutable Snapshot Timeline&lt;/strong&gt;: Every write operation (&lt;code&gt;INSERT&lt;/code&gt;, &lt;code&gt;UPDATE&lt;/code&gt;, &lt;code&gt;DELETE&lt;/code&gt;) creates an immutable snapshot milestone along a chronological timeline (Snapshot 1 at $t_0$, Snapshot 2 at $t_1$, Snapshot 3 at $t_2$).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Accidental Corruption Simulation&lt;/strong&gt;: When a faulty update or unintended overwrite occurs at $t_2$, the previous states remain fully intact on Cloud Storage.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sub-Second Historical Query (&lt;code&gt;asOf&lt;/code&gt;)&lt;/strong&gt;: By invoking &lt;code&gt;table.asOf(pastTime).getValues({ where: "id = 1002" })&lt;/code&gt;, Apps Script reaches back along the timeline to retrieve the exact table state as it existed at $t_1$. Historical ground truth is restored into Google Sheets in seconds without database downtime or backup recovery:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;00:00:08  Info    --- STEP 4: Verifying Snapshot Isolation (asOf) ---
00:00:12  Info    --- STEP 5: DML Update ---
00:00:14  Info    ✅ Table Test_Iceberg_[test-run-id] successfully updated.
00:00:15  Info    --- STEP 5-B: Executing Time Travel Query ---
00:00:15  Info    ✅ Time travel snapshot verified successfully.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Future Horizons: Advanced Enterprise Applications
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ff56o9j24nvr4lmc9j9lm.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ff56o9j24nvr4lmc9j9lm.jpg" alt="Figure 10: 5 Strategic Enterprise Horizons of IcebergApp &amp;amp; Google Workspace" width="800" height="447"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Figure 10: Five strategic enterprise horizons unlocked by unifying Google Workspace with Apache Iceberg lakehouses.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The ability to control Apache Iceberg natively from Google Apps Script introduces transformative possibilities for modern enterprise data architecture. As visually mapped out across the five connected strategic application nodes of &lt;strong&gt;Figure 10&lt;/strong&gt;, uniting the world's most accessible collaborative frontend (Google Workspace) with the open, high-performance analytical storage of Apache Iceberg unlocks five high-impact architectural horizons:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. IoT &amp;amp; Edge Telemetry Ingestion: The "Infinite Sheet" Architecture (Node 1)
&lt;/h3&gt;

&lt;p&gt;Frontline operations frequently encounter Google Sheets' strict 10-million cell limit when collecting continuous data from factory sensors, field logistics, smart meter endpoints, or high-volume Google Forms submissions.&lt;/p&gt;

&lt;p&gt;With &lt;strong&gt;IcebergApp&lt;/strong&gt;, Google Sheets evolves into a real-time ingestion buffer. Edge events and sensor pulses captured via Google Forms or Webhook web apps are staged in a transient operational sheet. A lightweight, time-driven Apps Script trigger flushes these micro-batches directly into Iceberg Parquet tables via &lt;code&gt;table.insertValues(values)&lt;/code&gt; and safely clears the active grid. The data immediately enters enterprise cold storage with automated daily partitioning, eliminating cell quota limitations while providing frontline staff with instantaneous query access to historical aggregations.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Time-Travel Financial &amp;amp; Audit Governance: Immutable Enterprise Ledgers (Node 2)
&lt;/h3&gt;

&lt;p&gt;Corporate financial reporting and supply chain logistics operate under demanding regulatory scrutiny (e.g., Sarbanes-Oxley Act [SOX], SEC Rule 17a-4, and IFRS). Traditional spreadsheet-centric tracking suffers from unintentional overwrites, unrecorded macro mutations, and missing point-in-time audit trails.&lt;/p&gt;

&lt;p&gt;IcebergApp solves this governance gap through Iceberg's native snapshot isolation. Because every &lt;code&gt;INSERT&lt;/code&gt;, &lt;code&gt;UPDATE&lt;/code&gt;, and &lt;code&gt;DELETE&lt;/code&gt; creates a deterministic, immutable snapshot in Cloud Storage, developers can construct automated compliance inspectors. Internal audit teams can inspect ledger states from the previous quarter, fiscal year-end, or preceding second simply by invoking &lt;code&gt;table.asOf(auditTimestamp).exportToSheet(sheet)&lt;/code&gt;. Point-in-time state recovery occurs without restoring monolithic database backups, transforming Workspace into an audit-proof financial control center.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Generative AI &amp;amp; Multimodal Vector Lakehouse Integration (Node 3)
&lt;/h3&gt;

&lt;p&gt;As enterprises deploy Large Language Models (LLMs) like Google Gemini and Vertex AI into their daily productivity workflows, ensuring AI agents access live, authoritative organizational ground truth is critical.&lt;/p&gt;

&lt;p&gt;IcebergApp establishes a serverless data pipeline connecting Workspace directly to enterprise AI platforms. Operational records accumulated in Apache Iceberg can be embedded into vector stores or queried directly by Gemini via BigQuery ML and BigLake integrations. An Apps Script trigger can query the lakehouse, invoke Gemini multimodal models with contextual enterprise data, and generate synthesized analytical executive briefs directly into Google Docs or formatted KPI dashboards in Google Sheets—entirely serverless and without manual data exports.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Multi-Engine Open Federation: Zero Vendor Lock-In (Node 4)
&lt;/h3&gt;

&lt;p&gt;The defining strength of Apache Iceberg is its complete independence from proprietary compute engines. When data is committed via IcebergApp, it is stored as open-standard Apache Parquet files and Avro metadata trees directly in customer-owned Google Cloud Storage buckets.&lt;/p&gt;

&lt;p&gt;This eliminates vendor lock-in. Data ingested from a simple Google Form via Apps Script is immediately readable and writable by &lt;strong&gt;Apache Spark&lt;/strong&gt;, &lt;strong&gt;Trino&lt;/strong&gt;, &lt;strong&gt;Snowflake&lt;/strong&gt;, &lt;strong&gt;Databricks&lt;/strong&gt;, and &lt;strong&gt;BigQuery&lt;/strong&gt; concurrently, with zero ETL translation and zero data duplication. Data engineers can perform heavy machine learning model training in Spark while business analysts interact with the exact same live dataset through Google Sheets in real time.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Agentic AI Orchestration via Model Context Protocol (MCP) &amp;amp; &lt;a href="https://github.com/tanaikech/adk-gas" rel="noopener noreferrer"&gt;GASADK&lt;/a&gt; (Node 5)
&lt;/h3&gt;

&lt;p&gt;The convergence of autonomous Generative AI agents and open lakehouse storage reaches its full operational potential when mediated by standardized tool-calling interfaces. The &lt;a href="https://modelcontextprotocol.io/" rel="noopener noreferrer"&gt;Model Context Protocol (MCP)&lt;/a&gt; has rapidly established itself as the open industry standard for connecting AI foundation models to external data sources and execution engines.&lt;/p&gt;

&lt;p&gt;By combining IcebergApp with &lt;strong&gt;&lt;a href="https://github.com/tanaikech/adk-gas" rel="noopener noreferrer"&gt;GASADK (Agent Development Kit for Google Apps Script)&lt;/a&gt;&lt;/strong&gt;, developers can seamlessly expose IcebergApp's lakehouse operations as deterministic MCP tool endpoints hosted directly inside Google Apps Script:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Autonomous Analytical Tool Calling&lt;/strong&gt;: AI agents powered by &lt;strong&gt;Google Gemini&lt;/strong&gt; (running across Gemini CLI, Google Antigravity, Claude Desktop, Cursor, or enterprise sidecars) can dynamically discover and execute IcebergApp methods such as &lt;code&gt;queryIceberg&lt;/code&gt;, &lt;code&gt;insertValues&lt;/code&gt;, &lt;code&gt;updateRecords&lt;/code&gt;, and &lt;code&gt;timeTravelAudit&lt;/code&gt; via standard MCP RPC.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Natural Language to Optimized Lakehouse DML&lt;/strong&gt;: Business users can instruct Gemini in plain natural language (e.g., &lt;em&gt;"Analyze sales anomalies across our APAC IoT sensor metrics, prune irrelevant partitions, and adjust the flagged calibration offsets"&lt;/em&gt;). Gemini formulates the query, pushes down metadata filters through IcebergApp, reasons over the returned 2D array payload, and issues atomic ACID DML updates without human SQL intervention.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Closed-Loop Workspace Automation&lt;/strong&gt;: When anomalies or business milestones are identified, the Gemini agent can simultaneously update the Iceberg lakehouse, append audit logs into a Google Spreadsheet, trigger automated Gmail alerts, and generate a synthesized executive summary in Google Docs—creating a fully autonomous, serverless enterprise data loop.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;This article introduced &lt;strong&gt;&lt;a href="https://github.com/tanaikech/IcebergApp" rel="noopener noreferrer"&gt;IcebergApp&lt;/a&gt;&lt;/strong&gt;, an open-source Google Apps Script library that seamlessly unifies Google Workspace with enterprise Apache Iceberg lakehouses on Google Cloud. By decoupling presentation from storage and orchestrating BigQuery as a serverless distributed query accelerator, IcebergApp permanently eliminates Google Sheets cell limits and CRUD latency, enabling developers to query, mutate, and manage petabyte-scale datasets directly from Apps Script.&lt;/p&gt;

&lt;p&gt;Key architectural takeaways from this article:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Bridging Workspace Automation and Enterprise Lakehouses&lt;/strong&gt;: Connects familiar Google Sheets and Apps Script workflows directly to open Apache Iceberg storage on Google Cloud without dedicated middleware, virtual machines, or JDBC proxies.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Eliminating GAS Timeouts and Memory Constraints&lt;/strong&gt;: Uses BigQuery to evaluate Iceberg metadata trees via Predicate Pushdown and Columnar Projection, maintaining a constant $O(1)$ memory footprint in Apps Script and returning filtered queries in 1–2 seconds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Overcoming the Google Sheets Scalability Ceiling&lt;/strong&gt;: Bypasses the 10-million cell limit and resolves crippling latency across search, append, update, and row deletion by delegating heavy mutations to serverless Lakehouse storage while using Sheets as a transient ingestion buffer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enterprise ACID Transactions and Snapshot Time Travel&lt;/strong&gt;: Delivers in-place DML updates and instant point-in-time snapshot recovery (&lt;code&gt;table.asOf()&lt;/code&gt;), enabling resilient disaster recovery from accidental spreadsheet overwrites without database downtime.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Future-Proof Ecosystem and Agentic AI Orchestration&lt;/strong&gt;: Unlocks open multi-engine federation (Spark, Trino, Snowflake) without vendor lock-in, and empowers autonomous Google Gemini agents to execute lakehouse workflows via the Model Context Protocol (MCP) and &lt;strong&gt;&lt;a href="https://github.com/tanaikech/adk-gas" rel="noopener noreferrer"&gt;GASADK&lt;/a&gt;&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For complete source code, installation steps, and implementation details, visit the &lt;strong&gt;&lt;a href="https://github.com/tanaikech/IcebergApp" rel="noopener noreferrer"&gt;IcebergApp GitHub Repository&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>googleworkspace</category>
      <category>googlecloud</category>
      <category>bigquery</category>
      <category>apacheiceberg</category>
    </item>
    <item>
      <title>Hearing the Mountain's Roar: How Antigravity CLI's AI Agents &amp; IoT Data Track Volcanic Shockwaves</title>
      <dc:creator>Tanaike</dc:creator>
      <pubDate>Wed, 02 Sep 2026 01:29:20 +0000</pubDate>
      <link>https://dev.to/gde/hearing-the-mountains-roar-how-antigravity-clis-ai-agents-iot-data-track-volcanic-shockwaves-13hp</link>
      <guid>https://dev.to/gde/hearing-the-mountains-roar-how-antigravity-clis-ai-agents-iot-data-track-volcanic-shockwaves-13hp</guid>
      <description>&lt;h2&gt;
  
  
  Turning 29k home weather stations and Gemini AI agents into a 15-minute volcanic warning shield
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Published Paper&lt;/strong&gt;: &lt;a href="https://essopenarchive.org/doi/full/10.22541/essoar.15008116/v1" rel="noopener noreferrer"&gt;Capturing Volcanic Lamb Waves and Blast Yields with Netatmo Weather Networks (ESS Open Archive)&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Abstract
&lt;/h3&gt;

&lt;p&gt;Explosive volcanic eruptions generate powerful atmospheric Lamb waves—acoustic-gravity waves trapped in the planetary boundary layer that travel horizontally across the globe at sound speed. Operating on a continuous background ingest where Google Apps Script (GAS) logs 20-minute weather records to Google Drive, we mapped two benchmark historical eruptions: the 2018 phreatic blast at Mt. Kusatsu-Shirane and the 2018 magmatic explosion at Mt. Shinmoedake. We framed these events within first-principles continuum mechanics inside the &lt;strong&gt;Antigravity CLI&lt;/strong&gt; environment powered by &lt;strong&gt;Gemini&lt;/strong&gt; and the autonomous R&amp;amp;D framework &lt;strong&gt;&lt;code&gt;tanaike-lab&lt;/code&gt;&lt;/strong&gt;. Across 29,334 authentic Netatmo observation records in Japan, our framework reconstructs temperature-dependent shockwave velocities (304.38 m/s in sub-zero winter air and 313.27 m/s in mild spring conditions, matching theoretical sound speeds within 98.5% precision), localizes unmonitored volcanic crater azimuths to within 1.78 degrees, quantifies explosive yields (178.8 to 1,041.1 tons of TNT equivalent), and delivers &lt;strong&gt;2.5 to 15 minutes of advance early warning&lt;/strong&gt; with &lt;strong&gt;100% false alarm elimination (0.0% False Positive Rate)&lt;/strong&gt; during violent storm baselines. These findings demonstrate how grounding generative AI in first-principles physics yields robust planetary sensing systems.&lt;/p&gt;




&lt;h3&gt;
  
  
  1. Introduction
&lt;/h3&gt;

&lt;p&gt;Conventional volcanic monitoring infrastructures (such as specialized infrasound microphones and seismographs operated by government agencies and global nuclear monitoring treaties) operate under a severe spatial limitation: observation stations are separated by 100 to 500 kilometers. This extreme spatial sparsity creates massive blind zones, suffers from severe spatial aliasing, and leaves isolated sensors vulnerable to wind turbulence.&lt;/p&gt;

&lt;p&gt;To overcome the spatial aliasing of sparse government arrays, we turn to crowdsourced consumer IoT barometers. By treating thousands of residential Netatmo stations across Japan as a continent-scale acoustic antenna (totaling &lt;strong&gt;29,334 authentic observation records&lt;/strong&gt;), we demonstrate that balcony-mounted pressure sensors can provide &lt;strong&gt;2.5 to 15 minutes of advance warning&lt;/strong&gt; before volcanic shockwaves and ash strike downstream urban centers.&lt;/p&gt;

&lt;p&gt;This research project advanced through an integrated, four-stage research workflow: combining background cloud archiving in Google Drive via Google Apps Script, historical event mapping across past volcanic eruptions, deductive physical theory construction, and Gemini AI multi-agent pattern mining inside the &lt;strong&gt;Antigravity CLI&lt;/strong&gt; terminal environment guided by the AI Co-Researcher framework &lt;strong&gt;&lt;code&gt;tanaike-lab&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;【Academic Note】&lt;/strong&gt;: The physical models, data interpretations, inferences, and precursor warning conclusions presented in this paper represent &lt;strong&gt;one of many academic perspectives and methodologies&lt;/strong&gt; regarding volcanic infrasound and atmospheric wave dynamics within the broad geophysics community. Given the inherent complexity of volcanic processes, these findings contribute a novel framework to the ongoing scientific discourse, inviting further empirical validation and community dialogue.&lt;/p&gt;

&lt;p&gt;In this article, written for AI practitioners, software architects, and technology leaders, we explore how human-AI collaboration on Antigravity CLI turned ultra-dense IoT sensor data into a proactive planetary defense platform.&lt;/p&gt;




&lt;h3&gt;
  
  
  2. The Automated Big-Data Pipeline &amp;amp; Research Project Workflow
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmt875nofhbde7c5gbrw6.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmt875nofhbde7c5gbrw6.jpg" alt="Volcanic-Lamb-IoT Framework: Turning Cities into a Giant Planetary Acoustic Antenna" width="800" height="447"&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;Figure 1: Executive Infographic of the Volcanic-Lamb-IoT Framework illustrating an explosive volcanic eruption generating 2D cylindrical Lamb waves traveling horizontally along the Earth's surface at 1,134 km/h (speed of sound), intercepted by thousands of residential IoT home weather stations across urban centers, and processed in real time by cloud analytics to compute blast yield in Tons of TNT and deliver a 2.5 to 15-minute early warning countdown.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The operational foundation of this research rests on a continuous, fully automated cloud ingestion system running in the background.&lt;/p&gt;

&lt;p&gt;Using &lt;strong&gt;Google Apps Script (GAS)&lt;/strong&gt;, an automated serverless cron trigger executes every 20 minutes, calling the &lt;strong&gt;Netatmo Connect API&lt;/strong&gt; to fetch multi-parametric meteorological records (Barometric Pressure, Outdoor Temperature, and Relative Humidity) from hundreds of residential IoT stations distributed across Japanese metropolitan areas and mountain foothills. These incoming data streams are continuously indexed, structured, and archived directly into a centralized &lt;strong&gt;Google Drive repository&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fv0hoipbifks5cm5rwwxa.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fv0hoipbifks5cm5rwwxa.jpg" alt="Automated IoT Big-Data Pipeline and Volcanic Research Project Workflow on Antigravity CLI" width="800" height="447"&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;Figure 2: Automated IoT Big-Data Pipeline and Volcanic Research Project Workflow on Antigravity CLI showing: (1) Automated Cloud Ingestion via Google Apps Script (GAS) every 20 minutes into Google Drive; (2) Historical Event Mapping extracting 24-hour observation windows (+/- 12 hours) across past volcanic eruptions; (3) Human Physical Theory &amp;amp; Logic Guidance injecting thermodynamic and wave mechanics constraints; and (4) Antigravity CLI Powered by Gemini AI where the tanaike-lab multi-agent matrix executes in a terminal CLI workspace to discover hidden precursor patterns and compute shockwave velocities with 0.0% false alarms.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Under this continuous data accumulation setup, our research project advanced through a structured 4-stage workflow:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Step 1: Automated Live Ingestion&lt;/strong&gt;: Google Apps Script operates as an autonomous background ingest engine, polling the Netatmo API every 20 minutes to maintain an uninterrupted, longitudinal meteorological archive in Google Drive.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Step 2: Historical Event Mapping&lt;/strong&gt;: We mapped past historical volcanic eruptions against this vast archive, extracting targeted 24-hour observation windows (from 12 hours before onset to 12 hours after: $T_0 - 12\text{ h} \to T_0 + 12\text{ h}$) for both the 2018 Mt. Kusatsu-Shirane steam blast and the 2018 Mt. Shinmoedake magmatic eruption, as well as baseline calm and stormy control days.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Step 3: Pre- and Post-Eruption Physical Anomaly Investigation&lt;/strong&gt;: By analyzing atmospheric time series before and after the blasts, we investigated whether subtle hydrothermal degassing anomalies emerge prior to rupture and how explosive atmospheric shockwaves propagate across urban centers after eruption.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Step 4: Theoretical Modeling &amp;amp; Validation on Antigravity CLI&lt;/strong&gt;: We constructed unified mathematical models (Bolton enthalpy, 2D compressible Lamb wave equations, slowness beamforming, and cylindrical energy flux integration) and supplied them to Gemini AI running inside Antigravity CLI to verify that volcanic early warnings and crater diagnostics operate with zero false alarms.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  3. Accessible Atmospheric Physics: 4 Intuitive Everyday Analogies
&lt;/h3&gt;

&lt;p&gt;How does an uncalibrated $150 weather gadget on an apartment balcony capture a volcanic explosion hundreds of kilometers away? The underlying physics can be understood through four intuitive real-world analogies:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fybq5889nwnl5yv30ncoo.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fybq5889nwnl5yv30ncoo.jpg" alt="Understanding Volcanic Shockwaves: 4 Intuitive Everyday Analogies" width="800" height="447"&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;Figure 3: Understanding Volcanic Shockwaves: 4 Intuitive Everyday Analogies. Panel 1 shows Lightning vs. Thunder (lightspeed cloud data outrunning 1,134 km/h sound waves to give early warning). Panel 2 shows Ripples in a Pond (2D cylindrical wave trapping along the Earth's surface). Panel 3 shows 150km Binaural Hearing (home weather sensors working like ears to pinpoint crater azimuth). Panel 4 shows the Speed Gate &amp;amp; Chorus Test (filtering out messy wind turbulence).&lt;/em&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  1. "Lightning vs. Thunder" — The Race Between Speed of Light and Speed of Sound
&lt;/h4&gt;

&lt;p&gt;When lightning strikes in the distance, you see the brilliant flash instantly, but the rumble of thunder arrives many seconds later. That is because light travels at 300,000 km/s, while sound crawls through the air at roughly 340 m/s.&lt;/p&gt;

&lt;p&gt;Our IoT early warning framework exploits this fundamental physical gap. When a volcano erupts, the atmospheric shockwave travels horizontally at acoustic sound speed (approximately 315 m/s, or 18.9 km per minute). Meanwhile, IoT weather stations sitting in the near-field (10 to 30 km from the crater) detect the initial pressure pulse within 30 to 90 seconds and beam their digital telemetry to the cloud at the speed of light.&lt;/p&gt;

&lt;p&gt;A perceptive question naturally arises: &lt;em&gt;If individual IoT stations report on 20-minute batch intervals, how is a 2.5 to 15-minute advance lead time operationally possible?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The answer lies in two interlocking physical and network principles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Acoustic Travel-Time Horizon Across Distance&lt;/strong&gt;: Atmospheric shockwaves require finite physical time to propagate across geography. For downstream infrastructure located 50 to 300 km away from the crater, the acoustic travel time creates an unavoidable physical delay: approximately 2.6 minutes for mountain communities at 50 km, 5.3 minutes at 100 km, 9.5 minutes for Tokyo at 180 km (from Mt. Kusatsu-Shirane), and 14.8 minutes (nearly 15 minutes) for the Fukuoka transit corridor at 280 km (from Mt. Shinmoedake). Because near-crater sensors transmit their detections to cloud servers in under a minute, downstream cities receive their alerts well before the physical wave arrives.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Asynchronous Temporal Staggering Across Dense Spatial Clusters&lt;/strong&gt;: In crowdsourced networks, individual home weather stations do not report in synchronized lockstep; their 20-minute transmission cycles are asynchronously distributed across time. Across a dense regional cluster of 100 stations, an updated packet reaches the cloud every 12 seconds on average ($\Delta t_{  ext{effective}} pprox 1,200    ext{ s} / 100 = 12  ext{ s}$). This dense staggering transforms discrete 20-minute station updates into a continuous, high-frequency spatial tracking stream, while modern edge-triggered webhooks can stream instantaneous sub-second bursts upon detecting steep pressure gradients ($\Delta P / \Delta t$).&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  2. "Ripples in a Pond" — 2D Cylindrical Wave Trapping
&lt;/h4&gt;

&lt;p&gt;Unconfined atmospheric explosions radiate energy spherically in three dimensions. As the wavefront expands, acoustic energy thins out rapidly, dissipating with the square of the distance ($\propto 1/r^2$).&lt;/p&gt;

&lt;p&gt;Volcanic Lamb waves behave differently. Trapped against the Earth's surface by gravity and stratified air density, they propagate as a two-dimensional cylindrical ring. Because acoustic energy cannot escape vertically, the pressure pulse decays much more slowly ($\propto 1/\sqrt{r}$). This acoustic waveguide effect preserves signal integrity across hundreds of kilometers, allowing uncalibrated balcony barometers in downtown Tokyo to record sharp micro-barometric pulses from craters 180 km away.&lt;/p&gt;

&lt;h4&gt;
  
  
  3. "Binaural Hearing Across 150 Kilometers" — Slowness Beamforming
&lt;/h4&gt;

&lt;p&gt;Humans can close their eyes and instantly point toward where a sound came from because the brain calculates the microsecond difference in arrival time between the left and right ears.&lt;/p&gt;

&lt;p&gt;Our system applies this exact principle across an entire region. By measuring millisecond arrival time differences across 25 to 88 IoT stations spaced over a 150-kilometer baseline, the slowness delay-and-sum beamforming algorithm overlays recorded waveforms across all 360 degrees of azimuth. The exact angle where all waveforms constructively reinforce each other pinpoints the volcanic crater with sub-degree accuracy, without requiring prior knowledge of the crater coordinates.&lt;/p&gt;

&lt;h4&gt;
  
  
  4. "The Speed Gate &amp;amp; The Chorus Test" — Silencing Violent Storm Gusts
&lt;/h4&gt;

&lt;p&gt;A critical instrumentation challenge immediately emerges: local wind turbulence. Under Bernoulli's principle, wind gusts blowing across an open balcony create sharp localized pressure drops. On stormy days, a single station can record hundreds of false pressure spikes.&lt;/p&gt;

&lt;p&gt;To eliminate false alarms, &lt;code&gt;tanaike-lab&lt;/code&gt; established two physical hurdles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Speed Gate&lt;/strong&gt;: Meteorological wind gusts travel with the weather front at 10 to 25 m/s (36 to 90 km/h), whereas authentic volcanic Lamb waves travel at sound speed (300 to 330 m/s, or ~1,134 km/h). Any signal moving slower than sound is instantly rejected.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Regional Chorus Test&lt;/strong&gt;: A wind gust is a localized, uncorrelated event hitting one home at a time. A volcanic shockwave is a massive orchestral wavefront that strikes dozens of homes across an entire prefecture in geometric cadence.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By requiring multi-station phase coherence (Semblance greater than or equal to 0.70), all 378 storm gust spikes recorded during severe gales were filtered out, achieving &lt;strong&gt;100% false alarm elimination (0.0% False Positive Rate)&lt;/strong&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  4. Data Processing Pipeline &amp;amp; The Philosophy of AI Pattern Discovery
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F75rl9r217wvrk85d35o2.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F75rl9r217wvrk85d35o2.jpg" alt="Multi-Parametric IoT Data Processing Pipeline: From Balcony Sensors to Planetary Intelligence" width="800" height="447"&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;Figure 4: Multi-Parametric IoT Data Processing Pipeline showing the 5-step transition from residential weather measurements to planetary intelligence: (1) Raw IoT Weather Ingestion (Pressure, Temp, Humidity), (2) Zero-Phase Waveform Filtering (removing tidal drift), (3) Sound Speed &amp;amp; Temperature Inversion (reconstructing 304-313 m/s velocities), (4) Binaural Beamforming (isolating crater bearing within 1.78 degrees), and (5) Blast Energy &amp;amp; Ash Shadow (computing TNT explosive tons and mapping ash cooling).&lt;/em&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  The Core Philosophy: Guiding AI's Pattern Discovery with Physical Logic
&lt;/h4&gt;

&lt;p&gt;Generative AI excels at high-dimensional pattern recognition. Yet, when applied blindly to noisy meteorological time series, unconstrained models risk latching onto spurious correlations and generating ungrounded artifacts.&lt;/p&gt;

&lt;p&gt;Transforming raw AI output into verifiable scientific discovery requires a hybrid approach: coupling pattern-mining agents directly with &lt;strong&gt;first-principles theoretical frameworks and domain logic&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Guiding Paradigm&lt;/strong&gt;: When we supply Generative AI not only with raw numbers, but with the exact physical laws, thermodynamic equations, and geometric constraints governing the phenomenon, we provide the AI with a structured cognitive lens. This dramatically magnifies the probability of discovering essential physical patterns while filtering out spurious noise.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A Unified Methodology Across Planetary Disasters&lt;/strong&gt;: This philosophy is the common intellectual thread uniting all our published disaster prediction breakthroughs:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In &lt;strong&gt;Earthquake Prediction (Unified LAIC-AGW Theory)&lt;/strong&gt;, we supplied AI with lithosphere-atmosphere coupling equations and seismic moment tensors to discover pre-seismic radon ionization and thermodynamic enthalpy spikes ($\delta \theta_e = 5.96\text{--}15.47\text{ K}$) hours before fault rupture.&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;Urban Torrential Rain Forecasting (LFD-TRP Framework)&lt;/strong&gt;, we supplied AI with 3D Navier-Stokes momentum and thermodynamic helicity equations to capture localized convective initiation 45 minutes in advance.&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;Volcanic Early Warning (Volcanic-Lamb-IoT Framework)&lt;/strong&gt;, we supplied AI with 2D compressible Lamb wave equations, Bolton steam enthalpy, and slowness beamforming constraints to isolate volcanic shockwave patterns and crater bearings from 29,334 noisy IoT records.&lt;/p&gt;

&lt;h4&gt;
  
  
  The 6-Stage Multi-Parametric Pipeline
&lt;/h4&gt;

&lt;p&gt;Driven by this human-guided AI paradigm, our processing pipeline executes an end-to-end derivation:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Pre-Eruption Hydrothermal Enthalpy Tracking&lt;/strong&gt;: Outdoor temperature, humidity, and barometric pressure are converted into Equivalent Potential Temperature using Bolton's formulation. Subtracting regional baseline trends reveals subtle thermal-steam anomalies (plus 1.5 to 3.2 Kelvin) downwind of the volcano hours before rupture.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero-Phase Digital Filtering&lt;/strong&gt;: Continuous barometric pressure series are conditioned using bidirectional zero-phase Butterworth bandpass filters (0.0005 to 0.02 Hz), removing 24-hour diurnal tidal pressure cycles while preserving exact wave arrival timestamps without phase distortion.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Temperature-Dependent Travel-Time Inversion&lt;/strong&gt;: Rather than assuming a constant speed of sound, the model performs line-integrals over local temperature fields ($c = \sqrt{\gamma R T}$), accounting for sound speed deceleration across freezing mountain plateaus.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Slowness Delay-and-Sum Beamforming&lt;/strong&gt;: Waveforms from regional sub-arrays are shifted and stacked across a 360-degree slowness grid to compute semblance coherence, isolating back-azimuth bearings.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cylindrical Acoustic Energy Flux Integration&lt;/strong&gt;: Integrating pressure variance over the 2D cylindrical surface and scaling by atmospheric scale height (approximately 8.4 km) quantifies total acoustic energy and converts it to TNT explosive yields.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Volcanic Ash Shadow Cooling Tracking&lt;/strong&gt;: Dense suburban temperature sensors detect direct solar radiation shielding under drifting ash clouds (causing local temperature drops of minus 4.10 to minus 6.13 degrees Celsius).&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  5. Empirical Results Across 29,334 Authentic Netatmo Records
&lt;/h3&gt;

&lt;p&gt;Our study validated the Volcanic-Lamb-IoT Framework using &lt;strong&gt;29,334 authentic 20-minute observation records&lt;/strong&gt; across two major volcanic eruptions and two severe weather control events:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Event &amp;amp; Scenario&lt;/th&gt;
&lt;th&gt;Analysis Window ($\pm 12\text{ h}$)&lt;/th&gt;
&lt;th&gt;Station Count &amp;amp; Region&lt;/th&gt;
&lt;th&gt;Inverted Wave Speed ($c_{\text{app}}$)&lt;/th&gt;
&lt;th&gt;Crater Azimuth Error&lt;/th&gt;
&lt;th&gt;Acoustic Energy Yield ($E_{\text{acoustic}}$)&lt;/th&gt;
&lt;th&gt;Ash Shadow Cooling ($\Delta T$)&lt;/th&gt;
&lt;th&gt;False Positive Rate (FPR)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;① Mt. Kusatsu-Shirane (Phreatic Blast)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Jan 22 22:00 – Jan 23 22:00 JST&lt;/td&gt;
&lt;td&gt;88 stations (Kanto / Chubu)&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;304.38 $\pm$ 4.73 m/s&lt;/strong&gt; ($R^2=0.980$)&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;0.10$^\circ$&lt;/strong&gt; ($S_c=0.88$)&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;7.48 $\times 10^{11}$ J&lt;/strong&gt; (178.8 t TNT)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$-$6.13 $^\circ$C&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0.0% (Detected)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;② Mt. Shinmoedake (Magmatic Blast)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Mar 06 02:00 – Mar 07 03:00 JST&lt;/td&gt;
&lt;td&gt;59 stations (Kyushu / Fukuoka)&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;313.27 $\pm$ 4.20 m/s&lt;/strong&gt; ($R^2=0.990$)&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;1.78$^\circ$&lt;/strong&gt; ($S_c=0.90$)&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;4.36 $\times 10^{12}$ J&lt;/strong&gt; (1,041.1 t TNT)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$-$4.10 $^\circ$C&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0.0% (Detected)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;③ Winter Quiet Baseline (Control 1)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Jan 15 00:00 – Jan 15 23:59 JST&lt;/td&gt;
&lt;td&gt;229 stations (Tokyo &amp;amp; Kyushu)&lt;/td&gt;
&lt;td&gt;No coherent wave detected&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;Background noise&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0.0% (Zero Alert)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;④ Spring Storm Baseline (Control 2)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Mar 01 00:00 – Mar 01 23:59 JST&lt;/td&gt;
&lt;td&gt;146 stations (Tokyo &amp;amp; Kyushu)&lt;/td&gt;
&lt;td&gt;Non-coherent wind turbulence&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;Background noise&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0.0% (Zero Alert)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fiwei4k3shv0rwfx461kx.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fiwei4k3shv0rwfx461kx.jpg" alt="Empirical Breakthroughs: Freezing Steam Blast vs. Magmatic Blast and Crater Pinpointing" width="800" height="447"&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;Figure 5: Empirical Breakthroughs: Freezing Steam Blast vs. Magmatic Blast. Panel A illustrates Mt. Kusatsu-Shirane's phreatic explosion generating 304 m/s shockwaves in sub-zero winter air (-5 deg C) with 178 Tons of TNT energy. Panel B illustrates Mt. Shinmoedake's magmatic explosion generating 313 m/s shockwaves in 12 deg C spring air with 1,041 Tons of TNT energy, while residential IoT beamforming pinpoints the crater bearing within 1.78 degrees.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;As shown in Figure 5 and Table 1, our framework achieved decisive empirical breakthroughs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Resolving Temperature-Dependent Wave Speeds&lt;/strong&gt;: During the Mt. Kusatsu-Shirane phreatic event, the observed shockwave propagated at $304.38 \pm 4.73\text{ m/s}$. While standard sea-level sound speed is approximately $340\text{ m/s}$, sound slows in cold, dense air according to $c = \sqrt{\gamma R T}$. On that sub-zero January morning ($-5^\circ\text{C}$ across the Gunma highlands), theoretical acoustic velocity drops to $304.8\text{ m/s}$. Our uncalibrated IoT array recovered this local thermal deceleration with 99.8% empirical accuracy ($R^2 = 0.980$).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pinpointing Unknown Craters from 150 km Away&lt;/strong&gt;: At Mt. Shinmoedake, beamforming across Kyushu stations localized the crater bearing to 169.49 degrees (true bearing 167.72 degrees), an error of &lt;strong&gt;just 1.78 degrees&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Quantifying Steam vs. Magma Blast Yields&lt;/strong&gt;: Total radiated acoustic energy was 7.48 x 10^11 Joules (&lt;strong&gt;178.8 tons TNT equivalent&lt;/strong&gt;) for the Kusatsu-Shirane steam blast, and 4.36 x 10^12 Joules (&lt;strong&gt;1,041.1 tons TNT equivalent&lt;/strong&gt;) for the Shinmoedake magmatic blast—demonstrating that magmatic explosions release roughly 6 times more atmospheric energy than phreatic events.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjsyesupgurs88tuqeq5c.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjsyesupgurs88tuqeq5c.jpg" alt="Speed Gate and Regional Chorus Test for 100% False Alarm Elimination" width="800" height="447"&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;Figure 6: The Speed Gate and Regional Chorus Test: 100% False Alarm Elimination. Illustrates the dual-filtering mechanics: (1) The Speed Gate rejects slow 10-25 m/s wind gusts while accepting 300-330 m/s volcanic shockwaves, and (2) The Regional Chorus Test filters out isolated single-house wind noise while verifying coherent multi-station wavefronts (Semblance &amp;gt; 0.70), guaranteeing zero false alarms.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;As illustrated in Figure 6, the Popperian falsification audit across 15,000+ control records and 378 storm gust spikes confirmed a &lt;strong&gt;0.0% false positive rate&lt;/strong&gt;, verifying that the network avoids false alarms during severe atmospheric disturbances.&lt;/p&gt;




&lt;h3&gt;
  
  
  6. Actionable Infrastructure Mitigation: 3 Real-World Applications
&lt;/h3&gt;

&lt;p&gt;Transforming residential weather stations into real-time geophysical intelligence unlocks three transformative societal protection capabilities:&lt;/p&gt;

&lt;h4&gt;
  
  
  Application 1: Aviation Safety &amp;amp; Urban Infrastructure Early Warning
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fie8pa92srvtaxkpuk0hw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fie8pa92srvtaxkpuk0hw.png" alt="Aviation Early Warning and Urban Infrastructure Protection Network" width="800" height="447"&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;Figure 7: Application 1: Aviation Safety and Urban Infrastructure Protection Network illustrating automated aircraft plume rerouting, subway intake louvers sealing, hospital HEPA filter cycling, and high-speed rail pre-deceleration triggered 2.5 to 15 minutes before shockwave arrival.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Volcanic ash melts inside jet turbine engines (above 1,400 degrees Celsius), turning into liquid glass and causing total engine failure. Our IoT network estimates explosive yield and crater coordinates within seconds, sending immediate alerts to Air Traffic Control (ATC) and Flight Management Systems (FMS) to dynamically reroute aircraft away from ascending plumes. Downwind cities receive a 5 to 15-minute countdown to automatically seal subway ventilation shafts, switch hospital HVAC systems to internal HEPA filtration, and pause outdoor construction.&lt;/p&gt;

&lt;h4&gt;
  
  
  Application 2: Volcanic Tsunami Early Detection &amp;amp; Metropolitan Shield
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Faiij0bamkqz8qzrttfls.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Faiij0bamkqz8qzrttfls.png" alt="Volcanic Tsunami Early Detection and Multi-Hazard Urban Shield" width="800" height="447"&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;Figure 8: Application 2: Volcanic Tsunami Early Detection and Metropolitan Shield illustrating coastal IoT barometers capturing atmospheric shockwaves outrunning deep-sea tsunamis, delivering 15 to 45 minutes of advance coastal evacuation lead time.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;When submarine calderas explode or island flanks collapse (e.g., Krakatoa in 1883, Hunga Tonga in 2022), they generate deadly tsunamis. While deep-ocean tsunamis travel at roughly 200 m/s (slowing to 30–50 m/s in shallow coastal waters), atmospheric Lamb waves travel at 315 m/s—outrunning the ocean wave. Coastal IoT barometers detecting the atmospheric shockwave deliver &lt;strong&gt;15 to 45 minutes of advance tsunami evacuation lead time&lt;/strong&gt; before the physical water arrives, triggering automated seawall gate closures and coastal rail halts.&lt;/p&gt;

&lt;h4&gt;
  
  
  Application 3: Blind Crater Localization for Dormant &amp;amp; Unmonitored Volcanoes
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4ozen72gemzmahrik5ig.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4ozen72gemzmahrik5ig.png" alt="Blind Crater Localization and Precursor Back-Tracking for Dormant Volcanoes" width="800" height="447"&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;Figure 9: Application 3: Blind Crater Localization for Dormant Volcanoes illustrating pre-eruption hydrothermal plume back-tracking via adjoint advection-diffusion and post-eruption multi-array cross-bearing triangulation.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The greatest hazard in volcanology is the surprise eruption from dormant mountains or unmonitored flank fissures where no government instruments exist. In our framework:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pre-Eruption Hydrothermal Tracking&lt;/strong&gt;: Prior to rupture, escaping steam creates subtle localized equivalent potential temperature anomalies (plus 1.5 to 3.2 Kelvin). Solving the adjoint advection-diffusion equation tracks the plume back to its source, mapping the coordinates of impending hydrothermal degassing days in advance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Post-Eruption Multi-Array Triangulation&lt;/strong&gt;: When an uncataloged explosion occurs, independent regional IoT sub-arrays (e.g., Kanto, Nagoya, Kyushu) calculate cross-bearing azimuth lines (within 0.48 degrees error), pinpointing the active crater coordinates to within 840 meters within minutes.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  7. Inside the AI Co-Researcher Framework (&lt;code&gt;tanaike-lab&lt;/code&gt;) on Antigravity CLI
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjqz3y98zye8ysb9erlxm.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjqz3y98zye8ysb9erlxm.jpg" alt="Autonomous Virtual R&amp;amp;D Laboratory Workflow on Antigravity CLI" width="800" height="447"&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;Figure 10: Autonomous Virtual R&amp;amp;D Laboratory Workflow on Antigravity CLI powered by Gemini AI and &lt;code&gt;tanaike-lab&lt;/code&gt;. Illustrates the Human PI providing high-level vision and initial research prompts, orchestrating the Data Ingestion Agent (29,334 authentic records), Modeling &amp;amp; Simulation Agent (2D wave mechanics), Automated Code Developer (Python scripts with assertion hooks), Popperian Falsification Agent, and Automated Manuscript Peer Review Panel.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Executing complex continuum mechanics equations, ingesting 29,334 authentic IoT records, running Popperian falsification checks, and preparing publication-ready LaTeX manuscripts was accomplished using &lt;strong&gt;Antigravity CLI&lt;/strong&gt; powered by &lt;strong&gt;Gemini&lt;/strong&gt; and the auxiliary R&amp;amp;D framework &lt;strong&gt;&lt;code&gt;tanaike-lab&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  1. How the Project Was Started, Executed, and Completed on Antigravity CLI
&lt;/h4&gt;

&lt;p&gt;The end-to-end execution of this research on Antigravity CLI followed a structured, 5-phase lifecycle:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Human Strategic Ignition &amp;amp; Comprehensive Initial Prompting&lt;/strong&gt;: Rather than initiating the study with an open-ended prompt, the human PI provided Antigravity CLI with a mathematically constrained research charter. This prompt established six non-negotiable architectural requirements: (i) construct a micro-barometric Lamb wave detection framework using citizen-science IoT time series; (ii) require 24-hour observation windows ($\pm 12\text{ h}$) across the 2018 Kusatsu-Shirane and Shinmoedake eruptions, backed by storm baselines to test falsifiability; (iii) apply Bolton's equivalent potential temperature ($\theta_e$) to isolate pre-eruptive hydrothermal heat fluxes; (iv) enforce bidirectional zero-phase bandpass filtering to preserve exact wavefront phase timestamps; (v) invert propagation velocities using spatially varying temperature fields; and (vi) implement slowness delay-and-sum beamforming to calculate crater azimuths and blast yields directly from multi-station arrival differentials.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Autonomous Plan Audit &amp;amp; Mathematical Dry-Runs&lt;/strong&gt;: Specialized subagents within &lt;code&gt;tanaike-lab&lt;/code&gt; (&lt;code&gt;plan_audit_dryrun_agent&lt;/code&gt; and &lt;code&gt;theoretical_feasibility_evaluator&lt;/code&gt;) audited the research plan, verifying mathematical consistency, validating the 2D cylindrical energy flux integration model, and formulating the temperature path-integral equation to account for mountain topography.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automated Pipeline Development &amp;amp; Popperian Assertion Hooks&lt;/strong&gt;: The code development subagent (&lt;code&gt;experiment_code_developer&lt;/code&gt;) constructed isolated Python processing pipelines inside the CLI environment. Automated assertion hooks were embedded into every script, ensuring that data ingestion from Google Drive archives via OAuth2, zero-phase bandpass filtering, and slowness beamforming executed with 100% authentic data and zero synthetic mock arrays.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Empirical Analysis &amp;amp; Agent-to-Agent (A2A) Peer Discussions&lt;/strong&gt;: Specialized auditing subagents (&lt;code&gt;experimental_results_auditor&lt;/code&gt;, &lt;code&gt;data_integrity_sentinel&lt;/code&gt;, &lt;code&gt;theory_discussion_reviewer&lt;/code&gt;) executed A2A discussions across the 29,334 records. They verified the 304.38 m/s freezing mountain sound speed match, evaluated crater back-azimuth accuracy, and confirmed 100% false alarm rejection across the 378 storm gust spikes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Manuscript Peer Review &amp;amp; Publication Delivery&lt;/strong&gt;: The 5-axis simulated peer review panel, &lt;code&gt;style_compliance_officer&lt;/code&gt;, and &lt;code&gt;typesetting_linter&lt;/code&gt; performed pre-submission audits, ensuring 100% 1-to-1 in-text citation matching, compiling complete LaTeX manuscripts, and producing high-resolution, color-universal infographics before final Human PI authorization.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  2. Real-Time Self-Crystallization &amp;amp; CLI Auto-Reinstallation Engine
&lt;/h4&gt;

&lt;p&gt;Unlike static agent scripts, &lt;code&gt;tanaike-lab&lt;/code&gt; features an active &lt;strong&gt;Real-Time Self-Crystallization &amp;amp; CLI Auto-Reinstallation Engine&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Real-Time Directive Capture&lt;/strong&gt;: Human steering directives and verified execution lessons are captured on the fly during project runs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SKILL.md Auto-Crystallization&lt;/strong&gt;: Lessons are written immediately into &lt;code&gt;SKILL.md&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CLI Plugin Auto-Reinstallation&lt;/strong&gt;: An automated export engine exports and reinstalls the updated skill matrix directly into the local Antigravity CLI plugin directory on the fly, performing instant Git remote sync to keep the framework continuously evolving.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  3. Academic and Systemic Positioning: The Human-AI Synergy Model
&lt;/h4&gt;

&lt;p&gt;Within modern technology R&amp;amp;D, &lt;code&gt;tanaike-lab&lt;/code&gt; is positioned as a &lt;strong&gt;"Human-Centric Dynamic Virtual R&amp;amp;D Laboratory OS"&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Positioning Against "AI Slop" vs. Human-AI Synergy&lt;/strong&gt;: Unsupervised AI generation frequently suffers from hallucinations and superficial text ("AI slop"). &lt;code&gt;tanaike-lab&lt;/code&gt; enforces a &lt;strong&gt;Human-AI Synergy Model&lt;/strong&gt;, where the human PI retains strategic direction while AI subagent matrices accelerate logical formulation, code execution, empirical auditing, and multi-axis peer reviews.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Positioning as a Cognitive Friction Eliminator&lt;/strong&gt;: Traditional scientific workflows consume massive cognitive bandwidth on operational friction—debugging scripts, interpolating non-uniform temporal grids, adjusting graphics for color universal design, and fixing LaTeX compilation errors. &lt;code&gt;tanaike-lab&lt;/code&gt; acts as a &lt;strong&gt;cognitive accelerator&lt;/strong&gt;, eliminating operational friction so human researchers can focus on high-level strategic reasoning.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Triple-Domain Architecture&lt;/strong&gt;: &lt;code&gt;tanaike-lab&lt;/code&gt; operates across three interconnected domain pillars: (1) &lt;strong&gt;Academic &amp;amp; Natural Sciences&lt;/strong&gt; (geophysical modeling, computational fluid dynamics, quantum chemistry, and material science), (2) &lt;strong&gt;Generative AI &amp;amp; LLM Engineering&lt;/strong&gt; (prompt architecture, RAG vector retrieval pipelines, Multi-Agent orchestration, token budget compression, and LLM benchmarks), and (3) &lt;strong&gt;Google Ecosystem&lt;/strong&gt; (Google Apps Script libraries, Google Workspace automation for Drive, Sheets, Docs, Gmail, Forms, and Google Cloud/Workspace APIs with quota limit management).&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  8. Summary
&lt;/h3&gt;

&lt;p&gt;The release of our manuscript &lt;a href="https://essopenarchive.org/doi/full/10.22541/essoar.15008116/v1" rel="noopener noreferrer"&gt;&lt;em&gt;Capturing Volcanic Lamb Waves and Blast Yields with Netatmo Weather Networks&lt;/em&gt;&lt;/a&gt; on ESS Open Archive marks a major advance in volcanology and planetary sensing. By proving that atmospheric Lamb waves and blast energies can be captured using crowdsourced IoT weather networks with 100% false alarm elimination (0.0% False Positive Rate), this research demonstrates how residential weather gadgets can be transformed into a continent-scale early warning shield.&lt;/p&gt;

&lt;p&gt;The analytical results, physical interpretations, and conclusions presented in this study represent &lt;strong&gt;one of many diverse scientific perspectives and theoretical approaches&lt;/strong&gt; within the evolving domain of volcanic physics. Continuous empirical validation and open community dialogue remain essential to building upon these findings.&lt;/p&gt;

&lt;p&gt;Beyond geophysics, the real-time self-evolving Human-AI synergy framework embodied by &lt;code&gt;tanaike-lab&lt;/code&gt; on Antigravity CLI provides a scalable blueprint for AI engineers and researchers across climate adaptation, LLM multi-agent engineering, and enterprise Google API automation. We invite the global geophysics, meteorology, generative AI, and smart-city engineering communities to read the full open-access paper on ESS Open Archive.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Taking Advantage of Gemini Managed Agents with Google Apps Script</title>
      <dc:creator>Tanaike</dc:creator>
      <pubDate>Sun, 30 Aug 2026 07:45:24 +0000</pubDate>
      <link>https://dev.to/gde/taking-advantage-of-gemini-managed-agents-with-google-apps-script-5cdp</link>
      <guid>https://dev.to/gde/taking-advantage-of-gemini-managed-agents-with-google-apps-script-5cdp</guid>
      <description>&lt;h3&gt;
  
  
  Breaking the Limits of GAS with Direct Cloud-to-Cloud Streaming in Persistent Linux Sandboxes
&lt;/h3&gt;




&lt;h2&gt;
  
  
  Abstract
&lt;/h2&gt;

&lt;p&gt;While Google Apps Script (GAS) is a powerful tool for Google Workspace automation, platform and computational constraints often limit its ability to handle advanced workloads. Gemini Managed Agents provide remote Linux sandboxes equipped with bash execution. This article introduces an architecture integrating GAS with a Linux sandbox to execute tasks beyond the capabilities of Apps Script alone. By streaming generated artifacts directly from within the Linux sandbox to Google Drive, this approach bypasses API payload limits, eliminates token overhead, and achieves high-throughput cloud automation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Recently, Martin Hawksey published an inspiring article on AppsScriptPulse exploring the potential of Gemini Managed Agents and the Google Workspace CLI within Google Workspace automation. &lt;a href="https://pulse.appsscript.info/p/2026/08/adding-a-spark-of-intelligence-to-google-workspace-exploring-gemini-managed-agents-and-the-google-workspace-cli/" rel="noopener noreferrer"&gt;Ref&lt;/a&gt; Gemini Managed Agents (part of the Gemini v1beta Interactions and Environments API) allow developers to provision and interact with remote Linux sandbox environments capable of autonomous code execution, shell commands, and package management. &lt;a href="https://ai.google.dev/gemini-api/docs/agents" rel="noopener noreferrer"&gt;Ref&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While Google Apps Script (GAS) is widely used for automating Google Workspace workflows, it operates as a lightweight, restricted serverless runtime without OS-level access, inherently preventing developers from executing various advanced computational workloads. Common platform bottlenecks include restricted low-level network and protocol controls, the absence of headless browser environments for dynamic web rendering, the inability to run native binaries for media transcoding or signal processing, the lack of modern compilers and build toolchains, and strict platform quotas on execution duration and payload sizes. The objective of this article is to introduce a generalized architecture that bridges GAS with a full-featured Linux sandbox provisioned by Gemini Managed Agents, demonstrating how developers can seamlessly offload otherwise impossible workloads to a dedicated cloud compute environment with high throughput and complete autonomy.&lt;/p&gt;

&lt;p&gt;By integrating Google Apps Script with Gemini Managed Agents, GAS gains access to a dedicated Linux container (4 vCPU, 16 GB RAM) featuring Python 3.12, Node.js 22, and standard Linux package managers (&lt;code&gt;apt&lt;/code&gt;, &lt;code&gt;npm&lt;/code&gt;, &lt;code&gt;pip&lt;/code&gt;). In this article, I present an end-to-end architecture and client library that enables GAS to orchestrate complex tasks inside a persistent Linux sandbox, eliminating local processing overhead by streaming generated artifacts directly to Google Drive via the &lt;a href="https://github.com/tanaikech/ggsrun" rel="noopener noreferrer"&gt;ggsrun&lt;/a&gt; CLI tool.&lt;/p&gt;

&lt;h2&gt;
  
  
  Architectural Paradigm: Why Direct Cloud-to-Cloud Streaming?
&lt;/h2&gt;

&lt;p&gt;When generating large files (such as high-resolution screenshots, audio waveforms, or bundled JavaScript) inside a Managed Agent sandbox and transferring them to Google Drive, returning raw binary data as Base64 strings through the Gemini API response to GAS introduces severe platform bottlenecks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GAS URL Fetch Response Limit&lt;/strong&gt;: Google Apps Script enforces a strict 50 MB response payload limit on &lt;code&gt;UrlFetchApp&lt;/code&gt;. &lt;a href="https://developers.google.com/apps-script/guides/services/quotas" rel="noopener noreferrer"&gt;Ref&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Code Execution Output Buffer Truncation&lt;/strong&gt;: The Gemini Interactions API code execution environment imposes standard output (stdout) buffer limits, truncating multi-megabyte Base64 payloads mid-stream. &lt;a href="https://ai.google.dev/gemini-api/docs/code-execution" rel="noopener noreferrer"&gt;Ref&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rate Limits and Conversational Token Inflation&lt;/strong&gt;: Gemini Managed Agents enforce a 200,000 Tokens Per Minute (TPM) quota. &lt;a href="https://ai.google.dev/gemini-api/docs/rate-limits" rel="noopener noreferrer"&gt;Ref&lt;/a&gt; Base64 encoding inflates binary size by ~33%. In multi-turn sessions, accumulating previous Base64 output strings in conversation history rapidly exhausts input token quotas, triggering immediate &lt;code&gt;429 Quota Exceeded&lt;/code&gt; errors.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CPU and Memory Overhead on GAS&lt;/strong&gt;: Decoding multi-megabyte Base64 strings and creating Drive blobs inside Apps Script consumes valuable execution time and script memory.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn0ayg6rt22x2yfz0z88n.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn0ayg6rt22x2yfz0z88n.jpg" alt="Figure 1: Architectural comparison between Base64 API transfer and direct cloud-to-cloud streaming via ggsrun" width="800" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To eliminate these bottlenecks, the optimal approach is to execute the Go CLI tool &lt;a href="https://github.com/tanaikech/ggsrun" rel="noopener noreferrer"&gt;ggsrun&lt;/a&gt; directly inside the Linux sandbox using a dynamically injected OAuth access token (&lt;code&gt;ScriptApp.getOAuthToken()&lt;/code&gt;). This allows the sandbox to stream binary artifacts directly to Google Drive over Google Cloud's internal backbone network at speeds exceeding 2 MB/s, completely bypassing Apps Script memory, API response size limits, and token quota exhaustion.&lt;/p&gt;

&lt;h3&gt;
  
  
  Drastic Input Token Savings via Bi-directional Streaming
&lt;/h3&gt;

&lt;p&gt;The advantages of direct cloud-to-cloud streaming extend far beyond outbound artifact uploads. When bringing large external datasets (high-resolution images, audio, video files, multi-gigabyte CSV/JSON datasets, or machine learning models) into the sandbox for processing, direct inbound downloads provide an equally critical advantage.&lt;/p&gt;

&lt;p&gt;Embedding large binary or structured datasets directly into API prompts as Base64 strings or serialized text rapidly consumes input token quotas, instantly hitting the 200,000 Tokens Per Minute (TPM) limit and triggering immediate &lt;code&gt;429 Quota Exceeded&lt;/code&gt; errors. In contrast, by streaming files directly from Google Drive into the sandbox via &lt;a href="https://github.com/tanaikech/ggsrun" rel="noopener noreferrer"&gt;ggsrun&lt;/a&gt;, the prompt requires only a concise instruction (e.g., "Download target dataset from Drive and analyze it"). This architecture &lt;strong&gt;reduces input token consumption to virtually zero, completely preventing rate-limit exhaustion&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Process Cost Reduction via Shared Persistent Sandboxes
&lt;/h3&gt;

&lt;p&gt;Furthermore, sharing a single persistent Linux sandbox (&lt;code&gt;environmentId&lt;/code&gt;) across multiple clients—including Google Apps Script, local Node.js workstations, Python scripts, and CI/CD pipelines—dramatically lowers operational process costs.&lt;/p&gt;

&lt;p&gt;By staging common master datasets, corpora, libraries, or pre-trained models inside the persistent sandbox filesystem (&lt;code&gt;/workspace/&lt;/code&gt;), any client can immediately leverage those shared assets to generate content and execute complex processing. This eliminates the redundant overhead of uploading or re-initializing datasets on every execution turn, &lt;strong&gt;significantly reducing execution latency, network bandwidth, and cumulative API overhead&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Furthermore, provisioning a single persistent Linux sandbox and sharing its unique &lt;code&gt;environmentId&lt;/code&gt; across multiple script executions, Google Apps Script projects, and local developer workstations eliminates redundant initialization overhead and allows multiple tasks to reuse shared working files and pre-installed packages seamlessly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Workflow
&lt;/h2&gt;

&lt;p&gt;The following diagram illustrates the complete end-to-end architecture where Google Apps Script and local Node.js workstations orchestrate a single persistent Linux sandbox using a shared &lt;code&gt;environmentId&lt;/code&gt;, leveraging bi-directional streaming (Inbound download / Outbound upload) and shared master datasets for instant content generation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbu5qnevkq5zmvtd55oqb.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbu5qnevkq5zmvtd55oqb.jpg" alt="Figure 2: End-to-end bi-directional workflow and shared persistent sandbox architecture" width="800" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Figure 2 Narrative&lt;/strong&gt;: The diagram outlines the data integration and execution pipelines across cloud and local environments:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Multi-Client Orchestration&lt;/strong&gt;: Cloud-based Google Apps Script (synchronous trigger, dynamic OAuth token) and local Node.js workstations (real-time SSE streaming, &lt;code&gt;gcloud&lt;/code&gt; CLI auth) orchestrate the exact same remote container via a shared &lt;code&gt;environmentId&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shared Data Repository &amp;amp; Pre-installed Toolchains&lt;/strong&gt;: The persistent sandbox (4 vCPU / 16 GB RAM) retains shared master datasets and build tools (Playwright, FFmpeg, esbuild), enabling instant content generation without redundant data re-upload overhead.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inbound Direct Download (&lt;code&gt;ggsrun download&lt;/code&gt;)&lt;/strong&gt;: Streams large external datasets directly from Google Drive into the sandbox, eliminating prompt data embedding and preserving input token quotas (200k TPM safe).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Outbound Direct Upload (&lt;code&gt;ggsrun upload&lt;/code&gt;)&lt;/strong&gt;: Streams generated binary deliverables directly to Google Drive at 2+ MB/s, completely bypassing GAS 50 MB payload limits and stdout buffer truncation.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Repository
&lt;/h2&gt;

&lt;p&gt;All source code, GAS classes, Node.js stream clients, test suites, and raw execution logs are available in the GitHub repository:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/tanaikech/managed-agents-gas" rel="noopener noreferrer"&gt;GitHub Repository: tanaikech/managed-agents-gas&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Usage
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Obtain Gemini API Key
&lt;/h3&gt;

&lt;p&gt;Generate an API key from Google AI Studio. &lt;a href="https://ai.google.dev/gemini-api/docs/api-key" rel="noopener noreferrer"&gt;Ref&lt;/a&gt; This API key authenticates requests to the Gemini v1beta Interactions and Environments APIs.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Create Google Apps Script Project
&lt;/h3&gt;

&lt;p&gt;Create a Google Apps Script project using either of the following methods: &lt;a href="https://developers.google.com/apps-script/guides/projects" rel="noopener noreferrer"&gt;Ref&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Standalone Project&lt;/strong&gt;: Visit &lt;a href="https://script.google.com" rel="noopener noreferrer"&gt;script.google.com&lt;/a&gt; and click &lt;strong&gt;New project&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Container-bound Project&lt;/strong&gt;: Open a Google Sheet, Doc, or Form, click &lt;strong&gt;Extensions&lt;/strong&gt;, and select &lt;strong&gt;Apps Script&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Deploy Client Scripts &amp;amp; Set Script Properties
&lt;/h3&gt;

&lt;p&gt;Copy the following files from the repository into your Apps Script editor:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/tanaikech/managed-agents-gas/blob/main/gas-src/ManagedAgentSandboxClient.js" rel="noopener noreferrer"&gt;&lt;code&gt;ManagedAgentSandboxClient.js&lt;/code&gt;&lt;/a&gt;: Core client class managing sandbox lifecycle, dynamic environment variables, session persistence in &lt;code&gt;PropertiesService&lt;/code&gt;, and intelligent 429 rate-limit backoff.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/tanaikech/managed-agents-gas/blob/main/gas-src/tests.js" rel="noopener noreferrer"&gt;&lt;code&gt;tests.js&lt;/code&gt;&lt;/a&gt;: Master test suite covering sandbox provisioning, tooling verification, media processing, web scraping, and performance benchmarks.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Navigate to &lt;strong&gt;Project Settings&lt;/strong&gt; &amp;gt; &lt;strong&gt;Script Properties&lt;/strong&gt; and add your API key: &lt;a href="https://developers.google.com/apps-script/guides/properties#manage_script_properties_manually" rel="noopener noreferrer"&gt;Ref&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Property: &lt;code&gt;GEMINI_API_KEY&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Value: &lt;em&gt;Your Gemini API Key&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Required Authorization Scopes
&lt;/h3&gt;

&lt;p&gt;Ensure your project manifest (&lt;code&gt;appsscript.json&lt;/code&gt;) includes the necessary OAuth scopes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;https://www.googleapis.com/auth/script.external_request&lt;/code&gt;: Required for &lt;code&gt;UrlFetchApp&lt;/code&gt; API communication.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;https://www.googleapis.com/auth/drive&lt;/code&gt;: Required for creating destination folders and uploading artifacts. (If using existing folders without &lt;code&gt;DriveApp.createFolder()&lt;/code&gt;, &lt;code&gt;https://www.googleapis.com/auth/drive.file&lt;/code&gt; can be used).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Testing on Cloud (Google Apps Script)
&lt;/h2&gt;

&lt;p&gt;Execution logs for all tests can be verified in &lt;a href="https://github.com/tanaikech/managed-agents-gas/blob/main/gas-src/execution-logs.md" rel="noopener noreferrer"&gt;gas-src/execution-logs.md&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Provisioning a Unified Linux Sandbox
&lt;/h3&gt;

&lt;p&gt;Executing &lt;code&gt;provisionSharedSandbox()&lt;/code&gt; initializes a new remote Linux container, installs all required CLI utilities and dependencies, configures destination Google Drive paths, and saves the resulting &lt;code&gt;environmentId&lt;/code&gt; in &lt;code&gt;PropertiesService&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F394mrwk322oplua0wyn5.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F394mrwk322oplua0wyn5.jpg" alt="Figure 3: Technical infographic of provisioning a unified persistent Linux sandbox via Google Apps Script" width="800" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Figure 3 Narrative&lt;/strong&gt;: The infographic details the 4-step provisioning pipeline. In Step 1, Google Drive creates destination directory &lt;code&gt;ManagedAgent_Artifacts_YYYYMMDD&lt;/code&gt;. In Step 2, a 4 vCPU / 16 GB RAM Linux container bootstraps &lt;code&gt;ggsrun&lt;/code&gt;, &lt;code&gt;ffmpeg&lt;/code&gt;, &lt;code&gt;sox&lt;/code&gt;, &lt;code&gt;jq&lt;/code&gt;, &lt;code&gt;typescript&lt;/code&gt;, &lt;code&gt;esbuild&lt;/code&gt;, and Playwright (Chromium). In Step 3, the sandbox validates installed binaries and emits a &lt;code&gt;READY&lt;/code&gt; status. In Step 4, the unique &lt;code&gt;environmentId&lt;/code&gt; is persisted under &lt;code&gt;SHARED_SANDBOX_SESSION&lt;/code&gt; in &lt;code&gt;PropertiesService&lt;/code&gt; for multi-test and cross-client reuse.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Step 1: Destination folder &lt;code&gt;ManagedAgent_Artifacts_YYYYMMDD&lt;/code&gt; is created in Google Drive.&lt;/li&gt;
&lt;li&gt;Step 2: An initialization prompt dispatches commands to download &lt;code&gt;ggsrun&lt;/code&gt;, install &lt;code&gt;ffmpeg&lt;/code&gt;, &lt;code&gt;sox&lt;/code&gt;, &lt;code&gt;jq&lt;/code&gt;, &lt;code&gt;typescript&lt;/code&gt;, &lt;code&gt;esbuild&lt;/code&gt;, and configure headless Chromium via Playwright.&lt;/li&gt;
&lt;li&gt;Step 3: The sandbox validates tool installations and returns a &lt;code&gt;READY&lt;/code&gt; status.&lt;/li&gt;
&lt;li&gt;Step 4: The persistent &lt;code&gt;environmentId&lt;/code&gt; is stored under &lt;code&gt;SHARED_SANDBOX_SESSION&lt;/code&gt; in &lt;code&gt;PropertiesService&lt;/code&gt; for subsequent test reuse.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Running &lt;code&gt;testListSandboxes()&lt;/code&gt; queries the Environments API to confirm active sandbox status and metadata.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Test 1: User-Agent Customization &amp;amp; POSIX Socket Verification (&lt;code&gt;runTest1_UserAgentComparison&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;This test demonstrates that while GAS &lt;code&gt;UrlFetchApp&lt;/code&gt; automatically overwrites custom HTTP &lt;code&gt;User-Agent&lt;/code&gt; headers with Google's proxy identity string, the Managed Agent sandbox preserves arbitrary header configurations via raw POSIX sockets and native &lt;code&gt;curl&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Faoic3y7t6xxtz4pf0f29.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Faoic3y7t6xxtz4pf0f29.jpg" alt="Figure 4: Technical infographic of HTTP User-Agent header behavior comparison between Google Apps Script and Linux Sandbox" width="800" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Figure 4 Narrative&lt;/strong&gt;: The diagram illustrates the request and response paths when sending a custom &lt;code&gt;User-Agent: sample user agent&lt;/code&gt; header to &lt;code&gt;httpbin.org/anything&lt;/code&gt;. In Google Apps Script (left), platform proxy policies enforce header substitution (❌). In contrast, the Linux sandbox using &lt;code&gt;curl&lt;/code&gt; (right) retains the exact custom header string via raw POSIX socket transmission (✅). An autonomous inline Python script compares the reflected JSON payloads and outputs the verification matrix.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Step 1: GAS sends an HTTP GET request to &lt;code&gt;https://httpbin.org/anything&lt;/code&gt; specifying &lt;code&gt;User-Agent: sample user agent&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Step 2: The sandbox executes an identical &lt;code&gt;curl&lt;/code&gt; request to the same endpoint and compares the reflected JSON payloads using an inline Python script.&lt;/li&gt;
&lt;li&gt;Summary of Execution: The comparison confirms that GAS replaced the header with &lt;code&gt;Mozilla/5.0 (compatible; Google-Apps-Script; beanserver; ...)&lt;/code&gt;, whereas the Linux sandbox preserved the exact &lt;code&gt;sample user agent&lt;/code&gt; header string.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Test 2: &lt;code&gt;ggsrun&lt;/code&gt; Deployment &amp;amp; Drive Direct Access Verification (&lt;code&gt;runTest2_GgsrunDirectDeployment&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;This test validates Google Drive authentication and direct access via &lt;a href="https://github.com/tanaikech/ggsrun" rel="noopener noreferrer"&gt;ggsrun&lt;/a&gt; inside the sandbox by dynamically injecting a fresh OAuth access token (&lt;code&gt;ScriptApp.getOAuthToken()&lt;/code&gt;) into the execution turn.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F02vqf0hikjf0cjli3c01.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F02vqf0hikjf0cjli3c01.jpg" alt="Figure 5: Technical infographic of dynamic OAuth token injection and ggsrun direct Google Drive deployment" width="800" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Figure 5 Narrative&lt;/strong&gt;: The infographic outlines the three execution steps of dynamic authentication and CLI offloading. In Step 1, GAS extracts &lt;code&gt;ScriptApp.getOAuthToken()&lt;/code&gt; and dynamically injects it into the execution turn's &lt;code&gt;GGSRUN_AT&lt;/code&gt; environment variable (eliminating 1-hour token expiration risks). In Step 2, the sandbox generates a verification file and uploads it via &lt;code&gt;ggsrun upload&lt;/code&gt;. In Step 3, &lt;code&gt;ggsrun searchfiles&lt;/code&gt; executes a folder query, confirming all 9 artifacts in 12.1 seconds.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Step 1: A verification file &lt;code&gt;00_ggsrun_verification.txt&lt;/code&gt; is created inside &lt;code&gt;/workspace/test2/&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Step 2: &lt;code&gt;ggsrun upload&lt;/code&gt; uploads the file directly to the designated Google Drive folder using non-blocking overwrite mode (&lt;code&gt;--nc --cm OverwriteIfNewer -j&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Step 3: &lt;code&gt;ggsrun searchfiles&lt;/code&gt; queries the destination folder to confirm file existence and returns structured metadata.&lt;/li&gt;
&lt;li&gt;Summary of Execution: The file was created, uploaded, and verified in Google Drive in 12.1 seconds, confirming full workspace interoperability without persisting sensitive access tokens across sessions.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Test 3: Playwright Headless Scraping to Direct Drive Upload (&lt;code&gt;runTest3_PlaywrightDirectUpload&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;This test executes an automated headless Chromium browser session to scrape dynamic JavaScript content and capture multi-viewport screenshots.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Feri5x2cx5s6bg5ogtsi4.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Feri5x2cx5s6bg5ogtsi4.jpg" alt="Figure 6: Technical infographic of headless browser scraping with Playwright and bulk direct upload to Google Drive" width="800" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Figure 6 Narrative&lt;/strong&gt;: The diagram depicts headless Chromium (Playwright) rendering dynamic JavaScript pages within the sandbox to capture multi-viewport screenshots (Desktop 1280x800: 92.5 KB, Mobile 375x812: 51.6 KB, Paginated Page 2: 171.9 KB) alongside structured quote JSON (4.1 KB), totaling ~320 KB across 4 artifacts. Bypassing Base64 API conversion, all files are streamed directly to Google Drive via &lt;code&gt;ggsrun upload&lt;/code&gt; in a single command, completing in 20.4 seconds.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Step 1: A Node.js Playwright script navigates to a JavaScript-rendered quote website (&lt;code&gt;quotes.toscrape.com/js/&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Step 2: Playwright captures full-page Desktop (1280x800) and Mobile iPhone emulation (375x812) screenshots of Page 1.&lt;/li&gt;
&lt;li&gt;Step 3: Playwright clicks pagination controls, captures a Desktop screenshot of Page 2, and extracts structured quote data into &lt;code&gt;02_Page2_Quotes.json&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Step 4: &lt;code&gt;ggsrun upload&lt;/code&gt; transfers all 3 PNG images and the JSON dataset directly to Google Drive in a single command.&lt;/li&gt;
&lt;li&gt;Summary of Execution: All 4 artifacts (totaling ~320 KB) were generated and uploaded in 20.4 seconds, successfully rendering client-side JavaScript that GAS cannot parse natively.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  5. Test 4: FFmpeg Audio Synthesis &amp;amp; Transcoding to Direct Drive Upload (&lt;code&gt;runTest4_FFmpegAudioDirectUpload&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;This test executes native digital signal processing inside the sandbox using FFmpeg and SoX to synthesize multi-tone audio chords.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftarzj21uu30yio063b7t.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftarzj21uu30yio063b7t.jpg" alt="Figure 7: Technical infographic of multi-tone audio synthesis with FFmpeg and direct Google Drive upload" width="800" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Figure 7 Narrative&lt;/strong&gt;: The infographic illustrates the digital signal processing (DSP) pipeline inside the Linux sandbox. Three sine wave generators (440 Hz / A4, 554.37 Hz / C#5, 659.25 Hz / E5) are combined through the &lt;code&gt;ffmpeg&lt;/code&gt; &lt;code&gt;amix&lt;/code&gt; filter complex into a 3-second harmonic major chord MP3 (73.4 KB), while &lt;code&gt;ffprobe&lt;/code&gt; extracts stream metadata into JSON (1.8 KB). Both binary audio and JSON analysis are streamed directly to Google Drive via &lt;code&gt;ggsrun&lt;/code&gt; in 9.1 seconds.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Step 1: &lt;code&gt;ffmpeg&lt;/code&gt; synthesizes a 3-second harmonic major chord MP3 by combining three sine waves (440 Hz, 554.37 Hz, and 659.25 Hz) through an &lt;code&gt;amix&lt;/code&gt; audio filter complex.&lt;/li&gt;
&lt;li&gt;Step 2: &lt;code&gt;ffprobe&lt;/code&gt; analyzes the output stream and extracts waveform metadata into &lt;code&gt;03_Audio_Analysis.json&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Step 3: &lt;code&gt;ggsrun upload&lt;/code&gt; uploads &lt;code&gt;03_Chord_Major.mp3&lt;/code&gt; (73.4 KB) and &lt;code&gt;03_Audio_Analysis.json&lt;/code&gt; (1.8 KB) directly to Google Drive.&lt;/li&gt;
&lt;li&gt;Summary of Execution: High-fidelity audio synthesis, metadata extraction, and Drive upload completed in 9.1 seconds.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  6. Test 5: TypeScript AST Extraction &amp;amp; &lt;code&gt;esbuild&lt;/code&gt; Bundling to Direct Drive Upload (&lt;code&gt;runTest5_TypeScriptASTDirectUpload&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;This test demonstrates modern JavaScript/TypeScript build tooling inside the sandbox environment.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F26t483upeugv07v5clsk.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F26t483upeugv07v5clsk.jpg" alt="Figure 8: Technical infographic of TypeScript AST extraction and high-speed esbuild compilation with direct Drive upload" width="800" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Figure 8 Narrative&lt;/strong&gt;: The diagram outlines the dual build toolchains operating on TypeScript source code (&lt;code&gt;matrix.ts&lt;/code&gt;). The first branch employs the official TypeScript Compiler API to parse the Abstract Syntax Tree (AST) and export interface schemas (&lt;code&gt;04_TypeScript_AST.json&lt;/code&gt;: 152 B). The second branch leverages &lt;code&gt;esbuild&lt;/code&gt; to compile a standalone IIFE bundle (&lt;code&gt;04_Matrix_Bundle.iife.js&lt;/code&gt;: 1.2 KB) in just 13 milliseconds. Both deliverables are offloaded to Google Drive via &lt;code&gt;ggsrun&lt;/code&gt; in 10.0 seconds.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Step 1: A TypeScript module (&lt;code&gt;matrix.ts&lt;/code&gt;) defining generic classes and interfaces is written to &lt;code&gt;/workspace/test5/&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Step 2: A Node.js script utilizes the official TypeScript Compiler API to parse the AST and export interface and method schemas into &lt;code&gt;04_TypeScript_AST.json&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Step 3: &lt;code&gt;esbuild&lt;/code&gt; bundles and minifies &lt;code&gt;matrix.ts&lt;/code&gt; into a standalone IIFE JavaScript bundle (&lt;code&gt;04_Matrix_Bundle.iife.js&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Step 4: &lt;code&gt;ggsrun upload&lt;/code&gt; transfers both the AST schema and the bundled JavaScript to Google Drive.&lt;/li&gt;
&lt;li&gt;Summary of Execution: AST parsing, bundle compilation (13 ms build time), and Drive upload completed in 10.0 seconds.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  7. Test 6: Performance Benchmark: Direct &lt;code&gt;ggsrun&lt;/code&gt; Upload vs. Base64 via GAS (&lt;code&gt;runTest6_DriveUploadPerformanceComparison&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;This benchmark evaluates transferring a binary payload (10,000 bytes) from the sandbox to Google Drive across two distinct methods:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4zie5w0570q383c6racu.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4zie5w0570q383c6racu.jpg" alt="Figure 9: Performance benchmark comparison infographic: Direct ggsrun streaming vs. Base64 transfer via Gemini API" width="800" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Figure 9 Narrative&lt;/strong&gt;: The benchmark infographic compares Approach A (direct &lt;code&gt;ggsrun&lt;/code&gt; streaming) against Approach B (Base64 transfer via API -&amp;gt; GAS decode). Approach A finished in 16.20 seconds (0.60 KB/s, zero GAS CPU usage), proving to be &lt;strong&gt;1.98x faster&lt;/strong&gt; than Approach B (32.13 seconds, 0.30 KB/s, 1.23 s GAS CPU). Approach A completely eliminates Base64 payload inflation (~33%) and prevents multi-turn conversational token exhaustion.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Approach A (Direct &lt;code&gt;ggsrun&lt;/code&gt; Upload)&lt;/strong&gt;: The sandbox generates a 10 KB binary file from &lt;code&gt;/dev/urandom&lt;/code&gt; and streams it directly to Google Drive via &lt;code&gt;ggsrun&lt;/code&gt; in a single interaction turn (&lt;code&gt;freshInteraction: true&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Approach B (Base64 Transfer via API -&amp;gt; GAS Blob Save)&lt;/strong&gt;: The sandbox encodes the 10 KB binary into Base64, returns it through the Gemini API response text, and GAS decodes the string and saves the file to Drive.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;================================================================================
PERFORMANCE BENCHMARK REPORT: 10,000 BYTES FILE TRANSFER TO GOOGLE DRIVE
================================================================================
| Metric                       | Approach A: Direct ggsrun Upload | Approach B: Base64 via Gemini API -&amp;gt; GAS |
| :--------------------------- | :------------------------------- | :--------------------------------------- |
| Transfer Method              | Direct Sandbox-to-Drive (Go CLI) | Base64 Stream -&amp;gt; GAS -&amp;gt; Drive            |
| Drive File Name              | benchmark_10kb_ggsrun.bin        | benchmark_10kb_gas.bin                   |
| Verified File Size           | 10,000 bytes (9.77 KB)           | 10,000 bytes (9.77 KB)                   |
| API Turns Required           | 1 Turn (Direct Offload)          | 1 Turn (Base64 Retrieval)                |
| Local GAS Processing Time    | 0.00 s (Zero CPU overhead)       | 1.23 s (Base64 Decode &amp;amp; Blob Creation)   |
| Total End-to-End Duration    | 16.20 s                          | 32.13 s                                  |
| Effective Throughput         | 0.60 KB/s                        | 0.30 KB/s                                |
| Performance Multiplier       | 1.98x FASTER                     | Baseline (Higher Latency &amp;amp; Token Usage)  |
================================================================================
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Summary of Benchmark Findings: Direct streaming via &lt;code&gt;ggsrun&lt;/code&gt; was &lt;strong&gt;1.98x faster&lt;/strong&gt;, eliminated 100% of Apps Script CPU/memory decoding overhead, and prevented conversational token quota consumption. For multi-megabyte payloads, this direct streaming architecture is essential to prevent &lt;code&gt;429 Quota Exceeded&lt;/code&gt; errors.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing on Local Workstations (Node.js Stream Runner)
&lt;/h2&gt;

&lt;p&gt;To demonstrate cross-platform interoperability enabling developers to control the exact same persistent Linux sandbox from both Google Apps Script and local workstations, a high-performance Node.js client powered by Server-Sent Events (SSE) streaming was implemented. &lt;a href="https://github.com/tanaikech/managed-agents-gas/tree/main/local-node.js-src" rel="noopener noreferrer"&gt;Ref&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Purpose and Advantages of the Local Stream Runner
&lt;/h3&gt;

&lt;p&gt;While Google Apps Script operates under a synchronous blocking execution model where agent events are aggregated at the end of the HTTP request, the local Node.js runner (built with the &lt;code&gt;@google/genai&lt;/code&gt; SDK) provides significant developer benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Real-Time Lifecycle Visibility (SSE Streaming)&lt;/strong&gt;: Streams internal reasoning steps (&lt;code&gt;thought&lt;/code&gt;), executed shell commands (&lt;code&gt;code_execution_call&lt;/code&gt;), sandbox standard output/error (&lt;code&gt;code_execution_result&lt;/code&gt;), and model text (&lt;code&gt;model_output&lt;/code&gt;) live to the terminal with ANSI color coding.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Interactive Hybrid Development Workflow&lt;/strong&gt;: Enables developers to prototype, debug, and calibrate agent prompts and toolchains locally with real-time feedback before deploying them into automated, hands-off Google Apps Script triggers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero-Friction Sandbox Sharing (GAS ↔ Local)&lt;/strong&gt;: By simply setting &lt;code&gt;ENVIRONMENT_ID&lt;/code&gt; in a local &lt;code&gt;.env&lt;/code&gt; file to the identifier generated during Apps Script provisioning, the local client immediately attaches to the existing container, sharing all pre-installed packages, compiled binaries, and workspace files without re-installation overhead.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automated OAuth Token Integration&lt;/strong&gt;: Dynamically extracts fresh Google OAuth access tokens via the Google Cloud SDK (&lt;code&gt;gcloud auth print-access-token&lt;/code&gt;) and injects them into &lt;code&gt;GGSRUN_AT&lt;/code&gt;, executing direct-to-Drive file uploads identically to Apps Script without manual credential copying.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Local Setup and Test Execution
&lt;/h3&gt;

&lt;p&gt;Local test suites can be executed through the following straightforward steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Step 1: Clone the repository and install dependencies by running &lt;code&gt;npm install&lt;/code&gt; inside the &lt;code&gt;local-node.js-src&lt;/code&gt; directory.&lt;/li&gt;
&lt;li&gt;Step 2: Copy &lt;code&gt;.env.example&lt;/code&gt; to &lt;code&gt;.env&lt;/code&gt; and specify &lt;code&gt;GEMINI_API_KEY&lt;/code&gt;, the persistent &lt;code&gt;ENVIRONMENT_ID&lt;/code&gt;, and the destination &lt;code&gt;TARGET_FOLDER_ID&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Step 3: Run &lt;code&gt;npm test&lt;/code&gt; (or individual tests &lt;code&gt;npm run test:1&lt;/code&gt; through &lt;code&gt;test:6&lt;/code&gt;) to monitor agent execution in real-time.&lt;/li&gt;
&lt;li&gt;Step 4: When testing is complete, run &lt;code&gt;npm run test:teardown&lt;/code&gt; to safely purge the remote sandbox environment and release cloud resources.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Full raw execution transcripts with live streaming outputs can be reviewed in &lt;a href="https://github.com/tanaikech/managed-agents-gas/blob/main/local-node.js-src/execution-logs.md" rel="noopener noreferrer"&gt;local-node.js-src/execution-logs.md&lt;/a&gt;, confirming 100% functional parity with Google Apps Script executions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Appendix: Gemini Managed Agents API Usage Patterns
&lt;/h2&gt;

&lt;p&gt;The following patterns summarize common interaction models when working with the Gemini v1beta Interactions and Environments API:&lt;/p&gt;

&lt;h3&gt;
  
  
  Base Endpoint
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;POST https://generativelanguage.googleapis.com/v1beta/interactions?key=${API_KEY}
Content-Type: application/json
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Scenario 1: Sharing a Single Persistent Sandbox Across Multiple Clients
&lt;/h3&gt;

&lt;p&gt;Provision a remote environment once by setting &lt;code&gt;environment.type&lt;/code&gt; to &lt;code&gt;"remote"&lt;/code&gt;. Save the returned &lt;code&gt;environment_id&lt;/code&gt; and pass it as a string in subsequent requests across any client (GAS, Node.js, Python, or CI/CD).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"agent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"antigravity-preview-05-2026"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"input"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Run task in shared container..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"environment"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"environments/env-12345"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Scenario 2: Using Isolated Sandboxes per Execution
&lt;/h3&gt;

&lt;p&gt;Set &lt;code&gt;environment.type&lt;/code&gt; to &lt;code&gt;"remote"&lt;/code&gt; on every call when tasks require a completely fresh, isolated Linux environment.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"agent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"antigravity-preview-05-2026"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"input"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Execute client-specific isolated task..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"environment"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"remote"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Scenario 3: Preserving Multi-turn Conversational Context
&lt;/h3&gt;

&lt;p&gt;Include &lt;code&gt;previous_interaction_id&lt;/code&gt; when the agent must retain knowledge of prior reasoning, variables, or command outputs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"agent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"antigravity-preview-05-2026"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"input"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Based on the previous output, proceed to step 2..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"environment"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"environments/env-12345"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"previous_interaction_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"interaction-prev-67890"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Scenario 4: Reusing Sandbox with Fresh Context (&lt;code&gt;freshInteraction&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;Specify the existing &lt;code&gt;environment_id&lt;/code&gt; and &lt;strong&gt;omit &lt;code&gt;previous_interaction_id&lt;/code&gt;&lt;/strong&gt;. This preserves all files and installed tools on the Linux container while resetting conversation history to zero tokens, preventing TPM rate-limit exhaustion.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"agent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"antigravity-preview-05-2026"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"input"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Execute a completely new task in the existing sandbox..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"environment"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"environments/env-12345"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Summary Matrix
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqf9xubltaftfdrchpxti.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqf9xubltaftfdrchpxti.jpg" alt="Figure 10: Summary matrix of Gemini Managed Agents API interaction scenarios and context management models" width="800" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;This article introduced an enterprise-grade architecture integrating Google Apps Script with Gemini Managed Agents (Linux sandboxes) to fundamentally transcend traditional serverless runtime constraints. By combining persistent remote sandboxes with bi-directional direct cloud-to-cloud streaming via &lt;a href="https://github.com/tanaikech/ggsrun" rel="noopener noreferrer"&gt;ggsrun&lt;/a&gt;, developers can achieve advanced processing capabilities previously impossible in Apps Script while avoiding API payload limitations and conversational token rate quotas.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Overcame Apps Script Limits&lt;/strong&gt;: Enabled advanced workloads requiring native Linux environments—such as headless browser scraping (Playwright), audio synthesis (FFmpeg), and TypeScript compilation (&lt;code&gt;esbuild&lt;/code&gt;)—directly from Google Apps Script.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Autonomous Fusion of AI Reasoning &amp;amp; Linux Execution&lt;/strong&gt;: Transcended static external command execution by uniting Gemini's cognitive reasoning with native Linux shell autonomy, enabling dynamic command synthesis, runtime code execution, and autonomous self-correction across complex workflows.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bi-directional Streaming Token &amp;amp; Payload Optimization&lt;/strong&gt;: In addition to direct artifact uploads, streaming large input datasets directly from Drive into the sandbox eliminates prompt data embedding and Base64 conversion, minimizing input/output token usage to bypass GAS 50 MB limits and the 200,000 TPM rate quota.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Process Cost Reduction via Shared Sandboxes&lt;/strong&gt;: Staging and sharing the container filesystem and common master datasets across clients eliminates redundant data re-upload and tooling setup overhead per task, substantially reducing execution latency and bandwidth costs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Empirically Proven 2x Performance Acceleration &amp;amp; Zero Memory Footprint&lt;/strong&gt;: Validated through benchmarks that direct cloud-to-cloud CLI streaming is 1.98x faster than traditional API Base64 retrieval while imposing zero CPU decoding load or local memory consumption on Google Apps Script.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>googleappsscript</category>
      <category>gemini</category>
      <category>antigravity</category>
      <category>linux</category>
    </item>
    <item>
      <title>Redefining the Role of Google Apps Script in the Era of Generative AI</title>
      <dc:creator>Tanaike</dc:creator>
      <pubDate>Fri, 21 Aug 2026 06:41:03 +0000</pubDate>
      <link>https://dev.to/gde/redefining-the-role-of-google-apps-script-in-the-era-of-generative-ai-43ke</link>
      <guid>https://dev.to/gde/redefining-the-role-of-google-apps-script-in-the-era-of-generative-ai-43ke</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fa5hzp48taek1jrwpwove.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fa5hzp48taek1jrwpwove.jpg" alt="Google Apps Script &amp;amp; Gemini in Google Workspace: Architectural Topologies and Ecosystem" width="800" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Abstract
&lt;/h2&gt;

&lt;p&gt;Generative AI and autonomous agents do not obsolete Google Apps Script (GAS); they elevate it into an indispensable deterministic execution substrate. This article establishes an enterprise hybrid architecture dividing responsibilities between AI's probabilistic reasoning (the brain) and GAS's secure, zero-cost, event-driven execution (the nervous system). Through 12 production use cases—spanning MCP servers, deterministic guardrails, and hybrid batching—we formalize four foundational principles for engineering resilient, scalable Google Workspace automations.&lt;/p&gt;




&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://workspace.google.com/" rel="noopener noreferrer"&gt;Google Workspace&lt;/a&gt; is a cloud-native groupware suite provided by Google for enterprise organizations, educational institutions, and individuals alike. By seamlessly integrating essential productivity tools—including Gmail, Google Drive, Calendar, Docs, and Sheets—it enables secure real-time collaboration and streamlined workflows worldwide.&lt;/p&gt;

&lt;p&gt;For over a decade, the backbone of automation across this ecosystem has been &lt;a href="https://developers.google.com/apps-script?utm_campaign=deveco_gdemembers&amp;amp;utm_source=deveco" rel="noopener noreferrer"&gt;Google Apps Script&lt;/a&gt; (GAS). &lt;a href="https://developers.google.com/apps-script?utm_campaign=deveco_gdemembers&amp;amp;utm_source=deveco" rel="noopener noreferrer"&gt;Ref&lt;/a&gt;&lt;br&gt;&lt;br&gt;
As a serverless JavaScript runtime, GAS internally encapsulates Google's robust OAuth 2.0 authentication machinery. Developers can orchestrate cross-service workflows spanning Sheets, Docs, Drive, and Gmail with zero infrastructure provisioning, zero credential leakage, and zero server maintenance costs. Furthermore, GAS's integration capabilities extend far beyond Google Workspace; through Advanced Google Services and REST APIs, it seamlessly interfaces with the broader Google APIs ecosystem—including Google Analytics (GA4), BigQuery, YouTube Data API, Google Maps, and Cloud Translation.&lt;/p&gt;

&lt;p&gt;The recent exponential surge in Generative AI has brought the Workspace automation paradigm to a historic turning point. Intuitive prompt-based solutions and autonomous agents are emerging that promise end-to-end task execution without traditional coding:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://workspace.google.com/intl/en/studio/" rel="noopener noreferrer"&gt;Google Workspace Studio&lt;/a&gt;&lt;/strong&gt;: Intuitive natural-language AI workflow orchestration &lt;a href="https://workspace.google.com/intl/en/studio/" rel="noopener noreferrer"&gt;Ref&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://gemini.google/overview/agent/spark/" rel="noopener noreferrer"&gt;Gemini Spark&lt;/a&gt;&lt;/strong&gt;: Autonomous AI agents managing 24/7 background tasks and tool execution &lt;a href="https://gemini.google/overview/agent/spark/" rel="noopener noreferrer"&gt;Ref&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://developers.google.com/workspace/guides/configure-mcp-servers" rel="noopener noreferrer"&gt;Google Workspace MCP Server and Gemini Integration&lt;/a&gt;&lt;/strong&gt;: Protocol-driven, context-aware autonomous tool invocation &lt;a href="https://developers.google.com/workspace/guides/configure-mcp-servers" rel="noopener noreferrer"&gt;Ref&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Faced with these capabilities, engineers and IT leaders frequently ask: &lt;em&gt;Has Google Apps Script been made redundant by Generative AI? Is writing script code a thing of the past?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The answer is an unequivocal "No."&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
In fact, &lt;strong&gt;the rise of flexible AI agents has brought the distinct technical advantages and irreplaceable domain of GAS into sharper focus than ever before.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Compared to pure natural-language agents and LLM-centric automations, GAS retains fundamental architectural strengths:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Deterministic Reproducibility, Sub-Second Latency, and Zero Inference Cost&lt;/strong&gt;
GAS eliminates hallucination risks, enforcing strict mathematical rules, financial transactions, and rigid business logic with 100% deterministic precision. It incurs zero token costs and zero model inference latency for pure computational tasks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;True Background Event Triggers and Deep UI Integration (Custom Functions)&lt;/strong&gt;
Through time-driven triggers (cron schedules) and event triggers (form submissions, spreadsheet edits, file uploads), GAS operates completely autonomously in the background without requiring continuous human presence or active browser sessions. Furthermore, features like Spreadsheet Custom Functions execute computational logic directly inside spreadsheet formula cells.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flexible External API Orchestration via &lt;code&gt;UrlFetchApp&lt;/code&gt;&lt;/strong&gt;
With &lt;code&gt;UrlFetchApp&lt;/code&gt;, GAS provides fine-grained control over HTTP headers, authentication payloads, and REST methods (GET, POST, PUT, DELETE, PATCH). Through &lt;code&gt;doGet&lt;/code&gt; and &lt;code&gt;doPost&lt;/code&gt; Web Apps, GAS functions simultaneously as a secure webhook listener and a serverless API gateway.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enterprise-Grade Governance as a Core Service&lt;/strong&gt;
Google Apps Script has been promoted to a &lt;strong&gt;Google Workspace Core Service&lt;/strong&gt; under General Availability (GA). It inherits enterprise data protection agreements, administrator policy controls, and standard technical support guarantees. &lt;a href="https://developers.google.com/apps-script/release-notes#June_22_2026" rel="noopener noreferrer"&gt;Ref&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Accelerated Development via Native Gemini in the Script Editor&lt;/strong&gt;
Gemini is now natively integrated into the Apps Script editor sidebar, enabling context-aware code generation, automated refactoring, and inline debugging. &lt;a href="https://developers.google.com/apps-script/guides/gemini" rel="noopener noreferrer"&gt;Ref&lt;/a&gt; This drastically lowers the entry barrier while accelerating delivery for both professional engineers and citizen developers.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  Workflow Comparison: Direct Natural-Language Execution vs. Deterministic Script Execution
&lt;/h3&gt;

&lt;p&gt;When comparing direct natural-language Workspace execution (via Google Workspace Studio or Gemini Spark) with the Gemini-assisted Google Apps Script paradigm, distinct workflow topologies emerge:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Approach 1: Direct Natural-Language Workspace Execution (Workspace Studio / Gemini Spark)&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;User provides natural-language prompt&lt;/li&gt;
&lt;li&gt;LLM dynamically interprets prompt, reasons about API execution order, and sequentially calls Google Workspace APIs&lt;/li&gt;
&lt;li&gt;System returns output&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Approach 2: Script-Fixed Execution (Google Apps Script with Gemini)&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;User provides natural-language prompt&lt;/li&gt;
&lt;li&gt;Gemini synthesizes and verifies Google Apps Script code (e.g., via the built-in Gemini side panel in the Script Editor)&lt;/li&gt;
&lt;li&gt;Google Apps Script engine executes the fixed script directly&lt;/li&gt;
&lt;li&gt;System returns output&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In Approach 1, because the LLM performs probabilistic reasoning on every single execution, subtle interpretation fluctuations can introduce non-deterministic behavior and inference latency overhead.&lt;br&gt;
In Approach 2, because natural-language instructions are compiled once into concrete GAS code, &lt;strong&gt;100% deterministic reproducibility is guaranteed on every subsequent run&lt;/strong&gt;, barring external network anomalies. Furthermore, because runtime execution bypasses LLM inference entirely, &lt;strong&gt;execution latency is dramatically lower&lt;/strong&gt; than direct natural-language API dispatching. Additionally, human engineers can seamlessly write, inspect, or modify the code directly, preserving full developer control.&lt;/p&gt;

&lt;p&gt;The contemporary imperative is not an "AI vs. Code" dichotomy, but the systematic engineering of &lt;strong&gt;Hybrid Architectures&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Development Phase&lt;/strong&gt;: Leveraging generative LLMs to synthesize, lint, and test GAS code at lightning speed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Runtime Phase&lt;/strong&gt;: Pairing the unstructured comprehension and reasoning of AI models with the deterministic validation, state persistence, event dispatching, and secure API execution of GAS.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This article delivers an exhaustive guide to the strategic positioning, architectural taxonomy, and &lt;strong&gt;12 highly practical use cases&lt;/strong&gt; of Google Apps Script in the generative AI era.&lt;/p&gt;


&lt;h2&gt;
  
  
  Google Apps Script Architecture and Project Design
&lt;/h2&gt;

&lt;p&gt;To architect resilient systems, developers must first master the architectural differences between &lt;strong&gt;Standalone Scripts&lt;/strong&gt; and &lt;strong&gt;Container-bound Scripts&lt;/strong&gt;. These project types differ not only in storage location but also in security boundaries, permission scopes, and lifecycle management.&lt;/p&gt;
&lt;h3&gt;
  
  
  1. Standalone Scripts
&lt;/h3&gt;

&lt;p&gt;A Standalone Script is an independent project stored directly in Google Drive, decoupled from any specific Workspace document. &lt;a href="https://developers.google.com/apps-script/guides/projects?hl=en#create-standalone" rel="noopener noreferrer"&gt;Ref&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Creation&lt;/strong&gt;: Created via Google Drive: &lt;strong&gt;[New] &amp;gt; [More] &amp;gt; [Google Apps Script]&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cross-Service Orchestration&lt;/strong&gt;: Coordinates data pipelines spanning multiple files, folders, and domains.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Autonomous Cron Automation&lt;/strong&gt;: Executes scheduled background jobs via time-driven installable triggers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;REST Endpoints &amp;amp; MCP Servers&lt;/strong&gt;: Hosts serverless Web Apps (&lt;code&gt;doGet&lt;/code&gt; / &lt;code&gt;doPost&lt;/code&gt;), webhook receivers, and Model Context Protocol (MCP) servers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enterprise SaaS Integration&lt;/strong&gt;: Acts as a secure integration hub connecting platforms like Slack, GitHub, Stripe, and Jira.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shared Code Libraries&lt;/strong&gt;: Encapsulates reusable business logic and utility modules across an organization.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security &amp;amp; Access Control&lt;/strong&gt;: Because it has no parent document, the script's access permissions are managed independently. Source code, Script Properties, and sensitive credentials remain completely hidden from end users, making it the ideal architecture for background administrative tasks and public API endpoints.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  2. Container-bound Scripts
&lt;/h3&gt;

&lt;p&gt;A Container-bound Script is embedded directly within a specific Google Workspace host file (Sheets, Docs, Slides, or Forms). &lt;a href="https://developers.google.com/apps-script/guides/projects?hl=en#create-from-docs-sheets-slides" rel="noopener noreferrer"&gt;Ref&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Creation&lt;/strong&gt;: Opened from the host file menu: &lt;strong&gt;[Extensions] &amp;gt; [Apps Script]&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;In-Document Data Processing&lt;/strong&gt;: Executes sheet macros, custom formatting, and batch cell transformations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Spreadsheet Custom Functions&lt;/strong&gt;: Defines bespoke calculation formulas callable directly inside spreadsheet cells.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Document UI Extensions&lt;/strong&gt;: Builds custom menu bars, modal dialogs, and interactive sidebars.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Immediate Local Event Handlers&lt;/strong&gt;: Responds instantly to user interactions via &lt;code&gt;onEdit&lt;/code&gt;, &lt;code&gt;onOpen&lt;/code&gt;, and &lt;code&gt;onFormSubmit&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security &amp;amp; Operational Model&lt;/strong&gt;: Access permissions are strictly inherited from the parent file. Users with edit access to the document can view and execute the script. The script can bind directly to active document instances (e.g., &lt;code&gt;SpreadsheetApp.getActiveSpreadsheet()&lt;/code&gt;) without requiring explicit resource IDs, making it exceptionally convenient for document-centric workflows.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  3. Project Type Comparison Matrix
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Evaluation Dimension&lt;/th&gt;
&lt;th&gt;Standalone Script&lt;/th&gt;
&lt;th&gt;Container-bound Script&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Primary Use Cases&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Web Apps, REST endpoints, MCP servers, cross-file batch jobs, SaaS integration hubs&lt;/td&gt;
&lt;td&gt;Custom Functions, sheet macros, document UI extensions (sidebars/menus)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Permission Management&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Managed independently per script (optimal for hiding source code and API keys)&lt;/td&gt;
&lt;td&gt;Inherited directly from the parent host document&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Resource Binding&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Explicit ID or URL required (e.g., &lt;code&gt;SpreadsheetApp.openById(id)&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Direct contextual access (e.g., &lt;code&gt;SpreadsheetApp.getActiveSpreadsheet()&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Public API / Web Apps&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Highly recommended (clean separation of concerns for API hosting)&lt;/td&gt;
&lt;td&gt;Possible, but tightly coupled to the host document&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;


&lt;h2&gt;
  
  
  Diverse Execution Triggers and Modalities in GAS
&lt;/h2&gt;

&lt;p&gt;GAS is far more than a simple macro engine; it is a full-fledged serverless execution runtime with diverse invocation mechanisms:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Script Editor (Manual / Debug Execution)&lt;/strong&gt;: Interactive testing, profiling, and Gemini-assisted code authoring.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simple &amp;amp; Installable Triggers&lt;/strong&gt;: Fully autonomous, zero-touch execution triggered by time schedules (cron), form submissions, spreadsheet edits, or calendar events.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Custom Functions&lt;/strong&gt;: Direct formula-level computation and inference within Google Sheets cells.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Custom Menus &amp;amp; Document Buttons&lt;/strong&gt;: On-demand interactive macros triggered by end users via sheet buttons or top menu bars.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sidebars &amp;amp; Modal Dialogs (HTML Service)&lt;/strong&gt;: Embedded interactive web interfaces within Workspace applications for guided human-in-the-loop workflows.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Web Apps (&lt;code&gt;doGet&lt;/code&gt; / &lt;code&gt;doPost&lt;/code&gt;)&lt;/strong&gt;: Public or organization-restricted REST API endpoints, webhook receivers, and MCP servers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Google Apps Script API&lt;/strong&gt;: Remote invocation and deployment from external CI/CD pipelines (GitHub Actions) or local developer tooling (&lt;code&gt;clasp&lt;/code&gt;, &lt;code&gt;ggsrun&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Google Workspace Add-ons&lt;/strong&gt;: Enterprise-wide or global distribution through the Google Workspace Marketplace.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;em&gt;For an exhaustive breakdown of execution mechanisms, see &lt;a href="https://medium.com/google-cloud/report-how-to-run-google-apps-script-b57bb153c796" rel="noopener noreferrer"&gt;Report: How to Run Google Apps Script&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Configuration Note: Centralized Gemini API Key&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
In accordance with security best practices, the scripts in this guide retrieve API credentials dynamically via &lt;code&gt;PropertiesService&lt;/code&gt; rather than hardcoding keys. Before executing the examples, open the Apps Script editor, navigate to &lt;strong&gt;&lt;a href="https://dev.togear%20icon"&gt;Project Settings&lt;/a&gt; &amp;gt; [Script Properties]&lt;/strong&gt;, and add a property named &lt;code&gt;GEMINI_API_KEY&lt;/code&gt; containing your valid Gemini API key.&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  12 Highly Practical Use Cases of Google Apps Script in the AI Era
&lt;/h2&gt;

&lt;p&gt;The following 12 categories detail the definitive, battle-tested roles of GAS in the generative AI landscape, complete with official references, production-ready code samples, architecture diagrams, security analyses, and advanced extension patterns.&lt;/p&gt;


&lt;h3&gt;
  
  
  1. Deterministic Custom Functions with External API Integration and In-Memory Caching
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frq56ap5va0a3jug62mht.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frq56ap5va0a3jug62mht.jpg" alt="Figure 1: Data flow of deterministic custom functions integrating external APIs with CacheService" width="800" height="447"&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;Figure 1: Deterministic custom function data flow integrating external APIs with CacheService — Illustrates cell input ingestion, sub-millisecond in-memory cache lookup, open API execution via UrlFetchApp on cache miss, and deterministic multi-column spill array propagation.&lt;/em&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  Technical Overview and Official References
&lt;/h4&gt;

&lt;p&gt;Google Sheets Custom Functions enable developers to define JavaScript functions in Apps Script that can be called directly within spreadsheet cells just like standard functions (&lt;code&gt;SUM&lt;/code&gt;, &lt;code&gt;VLOOKUP&lt;/code&gt;). They execute custom computational logic, fetch real-time data from external REST APIs via &lt;code&gt;UrlFetchApp&lt;/code&gt;, and populate calculations seamlessly across cells.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Official Reference&lt;/strong&gt;: &lt;a href="https://developers.google.com/apps-script/guides/sheets/functions" rel="noopener noreferrer"&gt;Custom Functions in Google Sheets&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;h4&gt;
  
  
  Concrete Example: Fetching Authoritative Country Data with Array Spilling and CacheService
&lt;/h4&gt;

&lt;p&gt;While LLM-powered spreadsheet formulas excel at freeform text generation and fuzzy summarization, they are unsuited for authoritative factual lookups (statistical data, ISO codes, master catalogs) where zero hallucination is required.&lt;/p&gt;

&lt;p&gt;As illustrated in Figure 1, the deterministic data flow executes through five coordinated steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;User enters a custom formula (e.g., &lt;code&gt;=GET_COUNTRY_INFO("US")&lt;/code&gt;) in a Google Sheets cell.&lt;/li&gt;
&lt;li&gt;GAS checks &lt;code&gt;CacheService&lt;/code&gt; to immediately return cached results without consuming network bandwidth if available.&lt;/li&gt;
&lt;li&gt;On a cache miss, &lt;code&gt;UrlFetchApp&lt;/code&gt; executes a secure HTTPS GET request to the public REST Countries API.&lt;/li&gt;
&lt;li&gt;GAS parses and structures the JSON payload into a clean 2D array and stores it in &lt;code&gt;CacheService&lt;/code&gt; (6-hour TTL).&lt;/li&gt;
&lt;li&gt;The function deterministically spills "Country Name," "Capital," "Region," and "Population" across four adjacent columns.&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;
  
  
  Production Script
&lt;/h4&gt;

&lt;p&gt;Paste the following script into your container-bound editor. In any spreadsheet cell, enter &lt;code&gt;=GET_COUNTRY_INFO("US")&lt;/code&gt; or &lt;code&gt;=GET_COUNTRY_INFO(A2)&lt;/code&gt; to dynamically populate four columns without requiring an API key:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
 * Custom function to fetch authoritative country metadata by ISO code and spill across 4 columns.
 * @param {string|number} countryCode 2-letter or 3-letter ISO country code (e.g., "US", "JP", "FR", "DE").
 * @return {Array&amp;lt;Array&amp;lt;string|number&amp;gt;&amp;gt;} 2D array: [[Name, Capital, Region, Population]]
 * @customfunction
 */&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;GET_COUNTRY_INFO&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;countryCode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;countryCode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;]];&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;code&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;countryCode&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;CacheService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getScriptCache&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cacheKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`country_info_&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// 1. Retrieve from in-memory cache if available (6-hour TTL)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cachedData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cacheKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cachedData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cachedData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cacheKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// 2. Fetch authoritative data from public REST API&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://restcountries.com/v3.1/alpha/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;encodeURIComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;UrlFetchApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;muteHttpExceptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;Accept&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getResponseCode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Error: Not Found&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;-&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;-&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;-&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]];&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getContentText&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Error: Invalid Response&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;-&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;-&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;-&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]];&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;country&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;country&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;common&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;-&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;capital&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;country&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;capital&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;country&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;capital&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;-&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;country&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;-&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;population&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;country&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;population&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;capital&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;region&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;population&lt;/span&gt;&lt;span class="p"&gt;]];&lt;/span&gt;

    &lt;span class="c1"&gt;// 3. Cache the structured result for 6 hours (21,600 seconds)&lt;/span&gt;
    &lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cacheKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;21600&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="s2"&gt;`Error: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;-&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;-&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;-&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]];&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Key Advantages
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;100% Deterministic Accuracy&lt;/strong&gt;: Relies exclusively on authoritative REST APIs, eliminating hallucination risks inherent in LLM-generated facts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero API Cost &amp;amp; Sub-Second Latency&lt;/strong&gt;: &lt;code&gt;CacheService&lt;/code&gt; caches identical queries in memory for up to 6 hours, preventing redundant quota consumption.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dynamic 2D Array Spilling&lt;/strong&gt;: Automatically populates multiple adjacent columns from a single cell formula without manual dragging.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Limitations and Operational Considerations
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;30-Second Execution Limit&lt;/strong&gt;: Custom functions must return within 30 seconds, or Google Sheets will throw a &lt;code&gt;#ERROR!&lt;/code&gt; timeout.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Read-Only Restrictions&lt;/strong&gt;: Custom functions cannot modify other cells, alter sheet formatting, or invoke services requiring sensitive OAuth write scopes.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Advanced Patterns and Extensions
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Financial Master Sync&lt;/strong&gt;: Fetch real-time foreign exchange rates or stock quotes from financial APIs and spill price, volume, and moving averages.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Postal Code Geocoding&lt;/strong&gt;: Resolve postal codes to standardized prefecture, city, and street addresses with multi-tier caching.&lt;/li&gt;
&lt;/ul&gt;

&lt;h5&gt;
  
  
  Related Articles and References
&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://tanaikech.github.io/2023/11/10/batch-processing-with-google-sheets-custom-functions/" rel="noopener noreferrer"&gt;Batch Processing with Google Sheets Custom Functions&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  2. Event-Driven Zero-Touch Autonomous AI Pipelines
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2sgaue7e5pnphbay980a.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2sgaue7e5pnphbay980a.jpg" alt="Figure 2: Autonomous AI event pipeline triggered by Google Forms submission" width="800" height="447"&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;Figure 2: Autonomous AI event pipeline triggered by Google Forms submission — Illustrates end-to-end autonomous execution from Form submission (onFormSubmit) to Gemini priority classification, real-time Sheets logging, and automatic Gmail response draft creation.&lt;/em&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  Technical Overview and Official References
&lt;/h4&gt;

&lt;p&gt;GAS Installable Triggers monitor Workspace state changes—such as Google Forms submissions (&lt;code&gt;onFormSubmit&lt;/code&gt;), spreadsheet cell edits (&lt;code&gt;onEdit&lt;/code&gt;), time intervals, and Calendar updates—executing background logic with elevated user authorization without requiring manual intervention.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Official Reference&lt;/strong&gt;: &lt;a href="https://developers.google.com/apps-script/guides/triggers/installable" rel="noopener noreferrer"&gt;Installable Triggers in Google Apps Script&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;h4&gt;
  
  
  Concrete Example 1: Form Ingestion, Sentiment &amp;amp; Urgency Classification, and Gmail Draft Synthesis
&lt;/h4&gt;

&lt;p&gt;As shown in Figure 2, the end-to-end autonomous event pipeline operates through five zero-touch stages:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Customer submits an inquiry through a public Google Form.&lt;/li&gt;
&lt;li&gt;An installable &lt;code&gt;onFormSubmit&lt;/code&gt; trigger automatically wakes up in the background.&lt;/li&gt;
&lt;li&gt;GAS dispatches inquiry text via &lt;code&gt;UrlFetchApp&lt;/code&gt; to Gemini 3.6 Flash for urgency classification, sentiment analysis, and response drafting.&lt;/li&gt;
&lt;li&gt;Structured classification metadata is appended in real time to the centralized Google Sheet.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GmailApp&lt;/code&gt; automatically generates a contextual reply draft in the support mailbox or dispatches urgent notifications to team channels.&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;
  
  
  Production Script 1 (Form Text Ingestion)
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
 * Installable trigger executed upon Google Forms submission.
 * Extracts inquiry text, classifies urgency via Gemini, and generates a Gmail draft.
 */&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;onFormSubmitTrigger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;namedValues&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Execution bypassed: Trigger event object (e.namedValues) is undefined.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userEmail&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;namedValues&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Email Address&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;namedValues&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Email Address&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;namedValues&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;namedValues&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Customer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;inquiry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;namedValues&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Inquiry Details&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;namedValues&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Inquiry Details&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;userEmail&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;inquiry&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;apiKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;PropertiesService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getScriptProperties&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;getProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GEMINI_API_KEY&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`You are a professional enterprise customer support specialist.
Analyze the following customer inquiry, evaluate its urgency, and compose a polite, professional reply.

Output requirements:
Return strictly a valid JSON object matching this schema:
{"urgency": "High" | "Medium" | "Low", "replySubject": "Subject line", "replyBody": "Full email body"}

Customer Name: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;userName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;
Inquiry Details:
&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;inquiry&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;
`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://generativelanguage.googleapis.com/v1beta/models/gemini-3.6-flash:generateContent?key=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;UrlFetchApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;post&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;contentType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;prompt&lt;/span&gt; &lt;span class="p"&gt;}]&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
      &lt;span class="na"&gt;generationConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;responseMimeType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="na"&gt;muteHttpExceptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getResponseCode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Gemini API error: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getContentText&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;json&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getContentText&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;aiOutput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;candidates&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Synthesize Gmail draft for human agent review&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;draftBody&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;aiOutput&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replyBody&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;

---
[AI Evaluation: Urgency &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;aiOutput&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;urgency&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;]`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;GmailApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createDraft&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userEmail&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;aiOutput&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replySubject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;draftBody&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;Logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Draft synthesized successfully for: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;userEmail&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; (Urgency: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;aiOutput&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;urgency&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;)`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Concrete Example 2: Multimodal Invoice Extraction from Gmail PDF Attachments
&lt;/h4&gt;

&lt;p&gt;Expanding beyond plain text, GAS can ingest binary PDF and image attachments from unread emails, convert their raw bytes to Base64, and pass them as &lt;code&gt;inlineData&lt;/code&gt; directly to Gemini 3.6 Flash for structured financial extraction and ledger recording.&lt;/p&gt;
&lt;h4&gt;
  
  
  Production Script 2 (Multimodal Attachment Processing)
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
 * Autonomous pipeline to scan unread emails for PDF invoices,
 * extract line items via Gemini Multimodal API, and log to Google Sheets.
 */&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;processInvoicePdfMultimodal&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;apiKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;PropertiesService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getScriptProperties&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;getProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GEMINI_API_KEY&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ss&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;SpreadsheetApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getActiveSpreadsheet&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;sheet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ss&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getSheetByName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;InvoiceLedger&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;sheet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ss&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insertSheet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;InvoiceLedger&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendRow&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;IssueDate&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Vendor&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;InvoiceNumber&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;TotalAmount&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Items&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;LoggedAt&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;threads&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;GmailApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;label:inbox is:unread has:attachment filename:pdf "Invoice"&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;thread&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;threads&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;thread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getMessages&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;msg&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isUnread&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;attachments&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getAttachments&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;att&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;attachments&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;att&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getContentType&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/pdf&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="c1"&gt;// 1. Convert file Blob to Base64 encoding&lt;/span&gt;
          &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;base64Data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Utilities&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;base64Encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;att&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBytes&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

          &lt;span class="c1"&gt;// 2. Dispatch multimodal payload to Gemini 3.6 Flash&lt;/span&gt;
          &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://generativelanguage.googleapis.com/v1beta/models/gemini-3.6-flash:generateContent?key=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
          &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`Extract all invoice details from this document and return strictly a JSON object:
Keys: invoiceNumber (string), vendor (string), issueDate (YYYY-MM-DD), totalAmount (number), items (array of strings)`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

          &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
              &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;prompt&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
                  &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="na"&gt;inlineData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                      &lt;span class="na"&gt;mimeType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/pdf&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                      &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;base64Data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="p"&gt;},&lt;/span&gt;
                  &lt;span class="p"&gt;},&lt;/span&gt;
                &lt;span class="p"&gt;],&lt;/span&gt;
              &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="na"&gt;generationConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;responseMimeType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="p"&gt;};&lt;/span&gt;

          &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;UrlFetchApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;post&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;contentType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="na"&gt;muteHttpExceptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="p"&gt;});&lt;/span&gt;

          &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getResponseCode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
              &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getContentText&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nx"&gt;candidates&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;
            &lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="c1"&gt;// 3. Record structured metadata directly into the ledger&lt;/span&gt;
            &lt;span class="nx"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendRow&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
              &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;issueDate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vendor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;invoiceNumber&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;totalAmount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
              &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="p"&gt;]);&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;markRead&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h4&gt;
  
  
  Key Advantages
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Zero-Touch Automation&lt;/strong&gt;: Operates 24/7 in the cloud without requiring active browser tabs, local daemons, or server hosting.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multimodal Binary Ingestion&lt;/strong&gt;: Direct conversion of PDFs and images (&lt;code&gt;Blob&lt;/code&gt; ➔ &lt;code&gt;Base64&lt;/code&gt;) allows seamless OCR and structured reasoning in a single pass.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  Limitations and Operational Considerations
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Installable Trigger Authorization&lt;/strong&gt;: When configuring triggers programmatically, ensure execution scope grants are verified.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;File Size Boundaries&lt;/strong&gt;: &lt;code&gt;UrlFetchApp&lt;/code&gt; request payloads are limited to 50 MB, which easily accommodates standard documents but requires chunking for massive media files.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  Advanced Patterns and Extensions
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Customer Feedback Escalation&lt;/strong&gt;: Automatically classify Google Form feedback into categories (Bug, Feature Request, Praise), sending immediate Slack alerts to engineering leads for high-priority bugs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automated Resume Screening&lt;/strong&gt;: Parse candidate resumes submitted via Form, extract skills and years of experience via Gemini, and compile structured applicant rankings in Sheets.&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;
  
  
  Related Articles and References
&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://tanaikech.github.io/2024/06/15/unlock-smart-invoice-management-gemini-gmail-and-google-apps-script-integration/" rel="noopener noreferrer"&gt;Unlock Smart Invoice Management: Gemini, Gmail, and Google Apps Script Integration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tanaikech.github.io/2024/12/05/streamlining-gmail-processing-including-attachment-files-using-gemini-with-google-apps-script/" rel="noopener noreferrer"&gt;Streamlining Gmail Processing Including Attachment Files Using Gemini with Google Apps Script&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tanaikech.github.io/2024/09/19/flexible-labeling-for-gmail-using-gemini-api-with-google-apps-script-part-3/" rel="noopener noreferrer"&gt;Flexible Labeling for Gmail using Gemini API with Google Apps Script Part 3&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;h3&gt;
  
  
  3. Serverless Web API Endpoints via Web Apps (&lt;code&gt;doGet&lt;/code&gt; / &lt;code&gt;doPost&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fexpzkzdvs38i28smchmq.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fexpzkzdvs38i28smchmq.jpg" alt="Figure 3: Serverless REST API endpoint architecture powered by GAS Web Apps" width="800" height="447"&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;Figure 3: Serverless REST API endpoint architecture powered by GAS Web Apps — Illustrates secure ingestion of external HTTPS requests, Bearer token verification, Gemini background processing, and deterministic JSON response generation via ContentService.&lt;/em&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  Technical Overview and Official References
&lt;/h4&gt;

&lt;p&gt;By implementing &lt;code&gt;doGet(e)&lt;/code&gt; or &lt;code&gt;doPost(e)&lt;/code&gt; handlers and deploying a project as a &lt;strong&gt;Web App&lt;/strong&gt;, GAS functions as an enterprise-grade, serverless REST API endpoint. It parses incoming query parameters, headers, and JSON payloads, processes internal Workspace resources, and returns structured &lt;code&gt;ContentService.MimeType.JSON&lt;/code&gt; responses.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Official Reference&lt;/strong&gt;: &lt;a href="https://developers.google.com/apps-script/guides/web" rel="noopener noreferrer"&gt;Web Apps Guide in Google Apps Script&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;h4&gt;
  
  
  Concrete Example: RESTful Ingestion Gateway for External Microservices and AI Agents
&lt;/h4&gt;

&lt;p&gt;As illustrated in Figure 3, the serverless Web API endpoint architecture operates through four structured steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;External clients, autonomous agents, or third-party SaaS platforms dispatch HTTPS &lt;code&gt;doGet&lt;/code&gt; or &lt;code&gt;doPost&lt;/code&gt; requests to the public Web App URL.&lt;/li&gt;
&lt;li&gt;GAS intercepts incoming requests, verifying the Bearer token or authorization header to block unauthorized traffic.&lt;/li&gt;
&lt;li&gt;Upon validation, the script executes business logic, queries Workspace databases, or triggers Gemini API calls.&lt;/li&gt;
&lt;li&gt;GAS packages data into &lt;code&gt;ContentService.createTextOutput&lt;/code&gt; with &lt;code&gt;MimeType.JSON&lt;/code&gt;, returning deterministic responses with zero server maintenance.&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;
  
  
  Production Script
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
 * HTTP POST Handler for GAS Web App.
 * Ingests external JSON payloads, validates bearer tokens, and persists records.
 */&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;doPost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;postData&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;postData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;createJsonResponse&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Empty request payload.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// 1. Validate custom authorization token&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;expectedToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;PropertiesService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getScriptProperties&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;getProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;API_AUTH_TOKEN&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;incomingToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parameter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;expectedToken&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;incomingToken&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;expectedToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;createJsonResponse&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Unauthorized access: Invalid token.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;401&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// 2. Parse and validate JSON body&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;postData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;category&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;summary&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;details&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;category&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;summary&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;createJsonResponse&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Missing required fields: category and summary.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// 3. Persist record into spreadsheet database&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sheet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;SpreadsheetApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getActiveSpreadsheet&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;getSheetByName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;IncomingLogs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendRow&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nx"&gt;category&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;summary&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;details&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SUCCESS&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;createJsonResponse&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;success&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Payload logged and processed successfully.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toISOString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;createJsonResponse&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * Utility helper to construct standard ContentService JSON output
 */&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;createJsonResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dataObject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;ContentService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createTextOutput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dataObject&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;setMimeType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;ContentService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MimeType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h4&gt;
  
  
  Deployment &amp;amp; Verification via &lt;code&gt;curl&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;Deploy via &lt;strong&gt;[Deploy] &amp;gt; [New deployment] &amp;gt; [Web app]&lt;/strong&gt; with access set to "Anyone". Test the endpoint from your local terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-L&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"https://script.google.com/macros/s/{DEPLOYMENT_ID}/exec?token=YOUR_API_AUTH_TOKEN"&lt;/span&gt;   &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt;   &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"category":"SecurityAlert","summary":"Unauthorized access attempt detected","details":"IP: 192.168.1.1"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(Note: The &lt;code&gt;-L&lt;/code&gt; flag is mandatory to follow Google's HTTP 302 authentication redirect).&lt;/em&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Key Advantages
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Zero Infrastructure Serverless&lt;/strong&gt;: Provides a permanent HTTPS REST endpoint without provisioning virtual machines, configuring load balancers, or managing SSL certificates.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Native Workspace Bridge&lt;/strong&gt;: Ingested data is immediately available to Google Sheets, Drive, and BigQuery connectors.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Limitations and Operational Considerations
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Concurrent Execution Limits&lt;/strong&gt;: Standard Google accounts allow up to 30 concurrent Web App executions (Google Workspace accounts allow more), making it ideal for webhook ingestion rather than massive high-frequency streaming.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HTTP 302 Redirection&lt;/strong&gt;: Clients must be configured to follow redirects (&lt;code&gt;curl -L&lt;/code&gt; or standard HTTP client redirect followers).&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Advanced Patterns and Extensions
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Webhook Ingestion Hub for Stripe / GitHub&lt;/strong&gt;: Receive payment confirmations or Git push events, summarize commit messages with Gemini, and update project tracking sheets.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Autonomous Agent Tool API&lt;/strong&gt;: Expose specific business functions (e.g., &lt;code&gt;createCalendarEvent&lt;/code&gt;, &lt;code&gt;searchDrive&lt;/code&gt;) as REST endpoints for external agent frameworks.&lt;/li&gt;
&lt;/ul&gt;

&lt;h5&gt;
  
  
  Related Articles and References
&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developers.google.com/apps-script/guides/web" rel="noopener noreferrer"&gt;Web Apps in Google Apps Script&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developers.google.com/apps-script/reference/content/" rel="noopener noreferrer"&gt;Content Service Reference&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  4. Augmenting Autonomous Agents (Gemini Spark, Antigravity CLI) via MCP &amp;amp; A2A Multi-Agent Protocol
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9qg8be5baia3b46mpu28.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9qg8be5baia3b46mpu28.jpg" alt="Figure 4: Autonomous agent tool execution via GAS Web App and Model Context Protocol (MCP)" width="800" height="447"&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;Figure 4: Autonomous agent tool execution via GAS Web App and Model Context Protocol (MCP) — Illustrates autonomous agents (Gemini Spark / Antigravity CLI) invoking serverless GAS tools with encapsulated credentials to manipulate Workspace resources.&lt;/em&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Technical Overview and Official References
&lt;/h4&gt;

&lt;p&gt;Autonomous agents interact with enterprise environments through emerging open protocols: the &lt;strong&gt;Model Context Protocol (MCP)&lt;/strong&gt; for granular tool invocation and the &lt;strong&gt;Agent-to-Agent (A2A)&lt;/strong&gt; protocol for hierarchical multi-agent collaboration. By deploying MCP and A2A servers directly on Google Apps Script (GAS) Web Apps, organizations transform GAS into an enterprise-grade execution substrate that encapsulates OAuth tokens, manages complex business rules, and exposes deterministic Workspace capabilities to autonomous agents (e.g., Gemini Spark, Gemini CLI, Antigravity CLI).&lt;/p&gt;

&lt;p&gt;Crucially, in large-scale enterprise automation, loading dozens of disparate tools directly into a single primary agent causes &lt;strong&gt;Tool Space Interference (TSI)&lt;/strong&gt;—a failure mode where the LLM misinterprets parameters, suffers tool selection degradation, and exhausts context token limits. &lt;/p&gt;

&lt;p&gt;Hosting an &lt;strong&gt;A2A Server on GAS&lt;/strong&gt; resolves TSI through &lt;strong&gt;Hierarchical Task Delegation&lt;/strong&gt;: the primary agent (such as the Gemini CLI or an agentic framework) delegates high-level sub-goals (e.g., &lt;em&gt;"Audit last month's financial spreadsheets and compile an executive summary document"&lt;/em&gt;) to a dedicated GAS subagent. The GAS subagent orchestrates internal Workspace tools within its own isolated execution context, returning only the synthesized, deterministic outcome to the primary agent.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Protocol Connectivity &amp;amp; Future Roadmap Note&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Under current specifications, &lt;strong&gt;Antigravity CLI&lt;/strong&gt; and &lt;strong&gt;Gemini Spark&lt;/strong&gt; connect directly to external &lt;strong&gt;MCP (Model Context Protocol) servers&lt;/strong&gt; for tool execution. While direct connection to external A2A servers is not supported at present, this limitation may be resolved in future framework updates as the multi-agent ecosystem matures. Currently, hierarchical subagent delegation via the &lt;strong&gt;A2A Protocol&lt;/strong&gt; is leveraged by the Gemini CLI and custom A2A clients communicating with the GAS A2A Server.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Google Workspace MCP Server Overview&lt;/strong&gt;: &lt;a href="https://developers.google.com/workspace/guides/configure-mcp-servers" rel="noopener noreferrer"&gt;Official Guide&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GASADK (Agent Development Kit for GAS)&lt;/strong&gt;: &lt;a href="https://github.com/tanaikech/adk-gas" rel="noopener noreferrer"&gt;GitHub (Kanshi Tanaike)&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ggsrun CLI Repository&lt;/strong&gt;: &lt;a href="https://github.com/tanaikech/ggsrun" rel="noopener noreferrer"&gt;GitHub (Kanshi Tanaike)&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GoogleApiApp Library&lt;/strong&gt;: &lt;a href="https://github.com/tanaikech/GoogleApiApp" rel="noopener noreferrer"&gt;GitHub (Kanshi Tanaike)&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;gas-fakes Offline Mock Engine&lt;/strong&gt;: &lt;a href="https://github.com/brucemcpherson/gas-fakes" rel="noopener noreferrer"&gt;GitHub (Bruce McPherson)&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GASADK MCP/A2A Server Samples&lt;/strong&gt;: &lt;a href="https://github.com/tanaikech/adk-gas/tree/master/samples/googleapiapp-mcp-server" rel="noopener noreferrer"&gt;GitHub (Kanshi Tanaike)&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h4&gt;
  
  
  Concrete Example 1: Gemini Spark &amp;amp; GASADK MCP Server for GA4 Analytics &amp;amp; Gmail Ingestion
&lt;/h4&gt;

&lt;p&gt;As illustrated in Figure 4, the autonomous agent tool-execution architecture operates across four synchronized stages:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Cloud-native agents (Gemini Spark) or local terminal agents (Antigravity CLI) receive high-level natural language goals from users.&lt;/li&gt;
&lt;li&gt;Agents dispatch tool-invocation requests to the GAS Web App endpoint (MCP server) via the Model Context Protocol (MCP).&lt;/li&gt;
&lt;li&gt;GAS internally encapsulates OAuth 2.0 tokens and API keys, securely manipulating Google Workspace applications (Sheets, Docs, Gmail) and GA4 datasets.&lt;/li&gt;
&lt;li&gt;Deterministic results are returned to the agent as clean JSON, ensuring reliable task fulfillment without prompt bloat.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Gemini Spark MCP Architecture
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6m73yzcavinuu68cbjxd.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6m73yzcavinuu68cbjxd.jpg" alt="Figure 4-1: Gemini Spark and GASADK MCP Server Architecture" width="800" height="447"&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;Figure 4-1: Gemini Spark and GASADK MCP Server Architecture — Illustrates cloud-native agent orchestration invoking GAS-hosted tools over JSON-RPC 2.0 to perform GA4 analysis and Gmail monitoring.&lt;/em&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Deployment Workflow
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Configure Manifest (&lt;code&gt;appsscript.json&lt;/code&gt;)&lt;/strong&gt;: Register &lt;code&gt;GASADK&lt;/code&gt;, &lt;code&gt;GoogleApiApp&lt;/code&gt;, and required Advanced Services (&lt;code&gt;AnalyticsData&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deploy MCP/A2A Endpoint&lt;/strong&gt;: Include &lt;a href="https://github.com/tanaikech/adk-gas/blob/master/samples/googleapiapp-mcp-server/DeployMcpServer.js" rel="noopener noreferrer"&gt;&lt;code&gt;DeployMcpServer.js&lt;/code&gt;&lt;/a&gt; and publish as a Web App accessible to "Anyone".&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Register in Gemini Spark&lt;/strong&gt;: Add the Web App URL (&lt;code&gt;https://script.google.com/macros/s/{DEPLOYMENT_ID}/exec?accessKey=sample&lt;/code&gt;) as a Custom Extension.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Autonomous Execution&lt;/strong&gt;: Prompt Gemini Spark naturally: &lt;em&gt;"@gas-mcp Extract yesterday's GA4 bounce rates and generate a summary report in Google Docs."&lt;/em&gt;
&lt;/li&gt;
&lt;/ol&gt;




&lt;h4&gt;
  
  
  Concrete Example 2: Antigravity CLI and the 3-Tier Workspace Orchestration Matrix
&lt;/h4&gt;

&lt;p&gt;The &lt;strong&gt;Antigravity CLI (&lt;code&gt;agy&lt;/code&gt;)&lt;/strong&gt; provides a Go-based, sub-millisecond local agent runtime. Operating within a local sandbox (&lt;code&gt;--sandbox&lt;/code&gt;), it orchestrates Google Workspace across three distinct operational tiers:&lt;/p&gt;

&lt;h4&gt;
  
  
  3-Tier Orchestration Architecture
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0sba8j8xxgjzakx2uxqa.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0sba8j8xxgjzakx2uxqa.jpg" alt="Figure 4-2: Antigravity CLI 3-Tier (Local/Hybrid/Cloud) Workspace Orchestration Architecture" width="800" height="447"&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;Figure 4-2: Antigravity CLI 3-Tier (Local/Hybrid/Cloud) Workspace Orchestration Architecture — Illustrates local dry-run testing with gas-fakes, rapid terminal execution with ggsrun, and long-running cloud task delegation with GASADK.&lt;/em&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Execution Flow
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Local Tier (Offline Dry-Run)&lt;/strong&gt;: AI-generated logic is executed locally against &lt;a href="https://github.com/brucemcpherson/gas-fakes" rel="noopener noreferrer"&gt;&lt;code&gt;gas-fakes&lt;/code&gt;&lt;/a&gt; to verify syntax and types with zero cloud quota cost.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hybrid Tier (Synchronous CLI Execution)&lt;/strong&gt;: Rapid queries and single-function executions invoke GAS directly from the terminal via &lt;a href="https://github.com/tanaikech/ggsrun" rel="noopener noreferrer"&gt;&lt;code&gt;ggsrun&lt;/code&gt;&lt;/a&gt; with immediate stdout feedback.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cloud Tier (Long-Running Delegation)&lt;/strong&gt;: Massive data processing and scheduled batch tasks are delegated to &lt;a href="https://github.com/tanaikech/adk-gas" rel="noopener noreferrer"&gt;&lt;code&gt;GASADK&lt;/code&gt;&lt;/a&gt; running cloud-natively on GAS.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Example of sandboxed autonomous orchestration via Antigravity CLI&lt;/span&gt;
agy &lt;span class="nt"&gt;--sandbox&lt;/span&gt; &lt;span class="s2"&gt;"Fetch last month's sales sheet via ggsrun, identify outliers, and draft an executive briefing document."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h4&gt;
  
  
  Concrete Example 3: A2A Protocol for Remote GAS Subagent Collaboration
&lt;/h4&gt;

&lt;p&gt;Primary orchestrator agents (such as Gemini CLI or multi-agent frameworks) deploy an &lt;strong&gt;A2A Server&lt;/strong&gt; on GAS to delegate complex document processing tasks to remote specialized subagents (while Antigravity CLI interacts via external MCP servers).&lt;/p&gt;

&lt;h4&gt;
  
  
  A2A Protocol and TSI Resolution Architecture
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fquz322rcuogkjhvuowsl.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fquz322rcuogkjhvuowsl.jpg" alt="Figure 4-3: A2A Protocol and Tool Space Interference (TSI) Resolution Architecture" width="800" height="447"&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;Figure 4-3: A2A Protocol and Tool Space Interference (TSI) Resolution Architecture — Illustrates hierarchical task delegation from primary agents to remote GAS subagents, eliminating tool collision and prompt bloating (clarifying protocol differentiation between MCP-enabled tools and A2A subagent delegation).&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;TSI Elimination and Context Isolation&lt;/strong&gt;: The primary agent does not need to load dozens of individual Sheet/Doc manipulation tools into its prompt context. Instead, it dispatches a single high-level JSON-RPC 2.0 task to the remote GAS subagent (&lt;code&gt;Workspace Manager Agent&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Serverless Multi-Agent Infrastructure&lt;/strong&gt;: Hosting A2A communication on GAS Web Apps eliminates the need to provision and maintain 24/7 Node.js or Python backend servers.&lt;/li&gt;
&lt;/ul&gt;




&lt;h4&gt;
  
  
  Key Advantages
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Zero-Infrastructure Multi-Agent &amp;amp; Tool Hosting&lt;/strong&gt;: Deploy production MCP and A2A servers directly on Google Cloud infrastructure without server provisioning.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Root-Level TSI Resolution&lt;/strong&gt;: Offloading sub-tasks to remote GAS subagents prevents prompt bloat and tool confusion in primary agents.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Complete Credential Encapsulation&lt;/strong&gt;: OAuth 2.0 scopes and API secrets remain strictly isolated inside GAS, never exposed to agent prompt contexts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Natural Language Task Delegation&lt;/strong&gt;: End-to-end multi-step tasks (reporting, auditing, alerting) are orchestrated autonomously through plain natural language.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Seamless Local-to-Cloud Flexibility&lt;/strong&gt;: Developers fluidly balance instant terminal execution (&lt;code&gt;ggsrun&lt;/code&gt;) with scalable serverless delegation (&lt;code&gt;GASADK&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Limitations and Operational Considerations
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;6-Minute Execution Limit&lt;/strong&gt;: Long-running cloud agent executions must complete within the 6-minute window, using trigger continuation patterns for massive datasets.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Concurrent Web App Quotas&lt;/strong&gt;: Coordinate simultaneous agent calls to respect standard concurrency limits (typically 30 concurrent executions).&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Advanced Patterns and Extensions
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Natural Language BigQuery Visualizer&lt;/strong&gt;: Autonomous agents query enterprise datasets via GAS and automatically render interactive charts in Sheets.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cross-Drive Semantic Research Agent&lt;/strong&gt;: An agent searches Drive folders via GAS MCP/A2A, compiles cross-document findings, and synthesizes executive briefings.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Autonomous Multi-Calendar Scheduler&lt;/strong&gt;: Agents coordinate meeting schedules across organizational boundaries with deterministic availability checks.&lt;/li&gt;
&lt;/ul&gt;

&lt;h5&gt;
  
  
  Related Articles and References
&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://medium.com/google-cloud/unlocking-infinite-automation-integrating-google-apps-script-with-gemini-spark-9b5407e4286c" rel="noopener noreferrer"&gt;Unlocking Infinite Automation: Integrating Google Apps Script with Gemini Spark&lt;/a&gt; (&lt;a href="https://tanaikech.github.io/2026/08/03/unlocking-infinite-automation-integrating-google-apps-script-with-gemini-spark/" rel="noopener noreferrer"&gt;Blog Edition&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://medium.com/google-cloud/orchestrating-google-workspace-with-antigravity-cli-a-high-performance-agentic-framework-499cae446161" rel="noopener noreferrer"&gt;Orchestrating Google Workspace with Antigravity CLI: A High-Performance Agentic Framework&lt;/a&gt; (&lt;a href="https://tanaikech.github.io/2026/06/15/orchestrating-google-workspace-with-antigravity-cli-a-high-performance-agentic-framework/" rel="noopener noreferrer"&gt;Blog Edition&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tanaikech.github.io/2025/05/08/building-model-context-protocol-mcp-server-with-google-apps-script/" rel="noopener noreferrer"&gt;Building Model Context Protocol (MCP) Server with Google Apps Script&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tanaikech.github.io/2026/05/18/agent-development-kit-for-google-apps-script/" rel="noopener noreferrer"&gt;Agent Development Kit for Google Apps Script (GASADK)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tanaikech.github.io/2025/08/22/streamlining-web-page-insights-with-natural-language-using-gemini-cli-google-analytics-and-mcp/" rel="noopener noreferrer"&gt;Streamlining Web Page Insights with Natural Language using Gemini CLI, Google Analytics, and MCP&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  5. Secure Internal AI Portals via Web Apps + HTML Service &amp;amp; A2UI
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F69kzs1uh9egmvcag9irw.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F69kzs1uh9egmvcag9irw.jpg" alt="Figure 5: Secure enterprise AI portal powered by HTML Service and organizational authentication" width="800" height="447"&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;Figure 5: Secure enterprise AI portal powered by HTML Service and organizational authentication — Illustrates single sign-on (SSO) protected web UI communicating asynchronously with backend GAS and Gemini via google.script.run.&lt;/em&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Technical Overview and Official References
&lt;/h4&gt;

&lt;p&gt;GAS &lt;strong&gt;HTML Service&lt;/strong&gt; allows developers to build full-stack web applications hosted directly inside Google Workspace. By combining frontend HTML/CSS/JS with backend GAS functions via &lt;code&gt;google.script.run&lt;/code&gt;, organizations can deliver internal AI tools protected by Google Workspace SSO without managing external authentication providers.&lt;/p&gt;

&lt;p&gt;Furthermore, adopting the &lt;strong&gt;Agent-to-User Interface (A2UI)&lt;/strong&gt; paradigm allows AI models to dynamically return UI cards, interactive action buttons, and dynamic input forms rather than static text.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;HTML Service Official Guide&lt;/strong&gt;: &lt;a href="https://developers.google.com/apps-script/guides/html" rel="noopener noreferrer"&gt;Create and Serve HTML&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A2UI with Gemini&lt;/strong&gt;: &lt;a href="https://tanaikech.github.io/2026/01/19/bringing-a2ui-to-google-workspace-with-gemini/" rel="noopener noreferrer"&gt;Bringing A2UI to Google Workspace with Gemini&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Task-Driven Agentic Interfaces&lt;/strong&gt;: &lt;a href="https://tanaikech.github.io/2026/02/03/beyond-chatbots-building-task-driven-agentic-interfaces-in-google-workspace-with-a2ui-and-gemini/" rel="noopener noreferrer"&gt;Building Interfaces with A2UI&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A2UI for Google Apps Script&lt;/strong&gt;: &lt;a href="https://tanaikech.github.io/2026/01/08/a2ui-for-google-apps-script/" rel="noopener noreferrer"&gt;Architecture Guide&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h4&gt;
  
  
  Concrete Example: Enterprise AI Proofreading and Translation Portal
&lt;/h4&gt;

&lt;p&gt;As illustrated in Figure 5, the enterprise AI portal architecture functions through five integrated steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Internal employees access the GAS Web App URL via desktop browsers.&lt;/li&gt;
&lt;li&gt;Google Workspace Single Sign-On (SSO) automatically enforces organization-level access control, blocking external unauthorized requests.&lt;/li&gt;
&lt;li&gt;The HTML Service frontend asynchronously triggers server-side GAS functions using &lt;code&gt;google.script.run&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;GAS securely retrieves the Gemini API key from &lt;code&gt;PropertiesService&lt;/code&gt; and checks in-memory &lt;code&gt;CacheService&lt;/code&gt; to prevent duplicate API billing.&lt;/li&gt;
&lt;li&gt;Adhering to the A2UI framework, dynamic UI feedback and action cards render instantly on the client browser.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Full-Stack Implementation
&lt;/h4&gt;

&lt;h5&gt;
  
  
  1. Backend Server Script (&lt;code&gt;Code.gs&lt;/code&gt;)
&lt;/h5&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;doGet&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;HtmlService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createHtmlOutputFromFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Index&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setTitle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Corporate AI Proofreading Portal&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setXFrameOptionsMode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;HtmlService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;XFrameOptionsMode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ALLOWALL&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * Server-side AI execution function invoked via google.script.run
 */&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;callGeminiProofread&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inputText&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;inputText&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;inputText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Input text cannot be empty.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// 1. Check in-memory cache using MD5 hash&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rawHash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Utilities&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;computeDigest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;Utilities&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DigestAlgorithm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MD5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;inputText&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;Utilities&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Charset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;UTF_8&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hashKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rawHash&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;256&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;padStart&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cacheKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`proof_&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;hashKey&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cached&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;CacheService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getScriptCache&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cacheKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cached&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;cached&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;apiKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;PropertiesService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getScriptProperties&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;getProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GEMINI_API_KEY&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://generativelanguage.googleapis.com/v1beta/models/gemini-3.6-flash:generateContent?key=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`You are an expert enterprise editor. Proofread and refine the following business text for clarity, grammatical precision, and professional tone. Provide bulleted improvement notes at the end.

Source Text:
&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;inputText&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;prompt&lt;/span&gt; &lt;span class="p"&gt;}]&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
    &lt;span class="na"&gt;generationConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;temperature&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.3&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;UrlFetchApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;post&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;contentType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;muteHttpExceptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getResponseCode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Gemini API Error: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getResponseCode&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt; - &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getContentText&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;json&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getContentText&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;outputText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;candidates&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Cache result for 2 hours (7,200 seconds)&lt;/span&gt;
  &lt;span class="nx"&gt;CacheService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getScriptCache&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cacheKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;outputText&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;outputText&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  2. Frontend Interface (&lt;code&gt;Index.html&lt;/code&gt;)
&lt;/h5&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;base&lt;/span&gt; &lt;span class="na"&gt;target=&lt;/span&gt;&lt;span class="s"&gt;"_top"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"utf-8"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;-apple-system&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;BlinkMacSystemFont&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;"Segoe UI"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Roboto&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;sans-serif&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#f8f9fa&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;30px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#202124&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nc"&gt;.card&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;max-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;800px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="nb"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#ffffff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;30px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;12px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;box-shadow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;4px&lt;/span&gt; &lt;span class="m"&gt;16px&lt;/span&gt; &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0.08&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nt"&gt;h2&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#1a73e8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;margin-top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nt"&gt;textarea&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;180px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;box-sizing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;border-box&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="m"&gt;#dadce0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;12px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;14px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;inherit&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;resize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;vertical&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#1a73e8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#fff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;12px&lt;/span&gt; &lt;span class="m"&gt;24px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;15px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;font-weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;500&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;6px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;pointer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;margin-top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;15px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#1557b0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="nd"&gt;:disabled&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#dadce0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;not-allowed&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nf"&gt;#output&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;margin-top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;16px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#e8f0fe&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;border-left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="m"&gt;#1a73e8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;white-space&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;pre-wrap&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nc"&gt;.error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#fce8e6&lt;/span&gt; &lt;span class="cp"&gt;!important&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;border-left-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#d93025&lt;/span&gt; &lt;span class="cp"&gt;!important&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#c5221f&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"card"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;✨ Enterprise AI Proofreading Portal&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;textarea&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"inputText"&lt;/span&gt; &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Enter text to proofread..."&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/textarea&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"submitBtn"&lt;/span&gt; &lt;span class="na"&gt;onclick=&lt;/span&gt;&lt;span class="s"&gt;"runProofread()"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Execute AI Proofreading&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"output"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
      &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;runProofread&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;inputText&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Please enter text.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;btn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;submitBtn&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;output&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;btn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;disabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;btn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Analyzing text...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;output&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;block&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;output&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;className&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;output&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Gemini is reviewing your content...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="nx"&gt;google&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;script&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;run&lt;/span&gt;
          &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;withSuccessHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;output&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nx"&gt;btn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;disabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nx"&gt;btn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Execute AI Proofreading&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
          &lt;span class="p"&gt;})&lt;/span&gt;
          &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;withFailureHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;output&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;className&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nx"&gt;output&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Error: &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nx"&gt;btn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;disabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nx"&gt;btn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Execute AI Proofreading&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
          &lt;span class="p"&gt;})&lt;/span&gt;
          &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;callGeminiProofread&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Key Advantages
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Zero-Infrastructure Organizational SSO&lt;/strong&gt;: Restrict access to internal Workspace accounts with a single configuration toggle—no Auth0 or Firebase Auth setup required.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A2UI Extensibility&lt;/strong&gt;: Seamlessly upgrade from static text responses to dynamic adaptive forms and task cards generated on the fly by AI.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Limitations and Operational Considerations
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Iframe Sandbox Constraints&lt;/strong&gt;: HTML Service operates inside an &lt;code&gt;iframe&lt;/code&gt;, which limits certain low-level browser APIs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Initial Load Latency&lt;/strong&gt;: Initial page loads require 1–2 seconds to establish the Google Workspace authentication wrapper.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Advanced Patterns and Extensions
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Adaptive Task Execution Portals via A2UI&lt;/strong&gt;: AI dynamically generates input forms based on vague user requests, guiding employees step-by-step through complex workflows.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Corporate Policy Q&amp;amp;A Bot&lt;/strong&gt;: An internal portal that parses PDF manuals stored in Google Drive, providing authoritative answers with exact page citations.&lt;/li&gt;
&lt;/ul&gt;

&lt;h5&gt;
  
  
  Related Articles and References
&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://tanaikech.github.io/2026/01/19/bringing-a2ui-to-google-workspace-with-gemini/" rel="noopener noreferrer"&gt;Bringing A2UI to Google Workspace with Gemini&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tanaikech.github.io/2026/02/03/beyond-chatbots-building-task-driven-agentic-interfaces-in-google-workspace-with-a2ui-and-gemini/" rel="noopener noreferrer"&gt;Beyond Chatbots: Building Task-Driven Agentic Interfaces in Google Workspace with A2UI and Gemini&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tanaikech.github.io/2026/01/08/a2ui-for-google-apps-script/" rel="noopener noreferrer"&gt;A2UI for Google Apps Script&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  6. Context-Aware AI Assistant Panels via Sidebars and Dialogs
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fajwuzlir6hwduzuyb6xh.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fajwuzlir6hwduzuyb6xh.jpg" alt="Figure 6: Context-aware AI assistant panel integrated as a Google Docs sidebar" width="800" height="447"&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;Figure 6: Context-aware AI assistant panel integrated as a Google Docs sidebar — Illustrates bidirectional UI workflow capturing partial document selections, querying Gemini, and streaming proofread text directly back into the editor.&lt;/em&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Concrete Example: In-Editor Text Summarization, Proofreading, and Insertion
&lt;/h4&gt;

&lt;p&gt;As shown in Figure 6, the context-aware sidebar workflow executes seamlessly within the document workspace:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;User highlights any text passage in Google Docs.&lt;/li&gt;
&lt;li&gt;User clicks a pre-configured AI action (Honorific Polish, 3-Line Summary, Business English Translation) in the custom sidebar.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DocumentApp.getSelection()&lt;/code&gt; accurately extracts the highlighted text elements, preserving partial selections.&lt;/li&gt;
&lt;li&gt;Backend GAS transmits the payload to Gemini 3.6 Flash.&lt;/li&gt;
&lt;li&gt;The synthesized text is previewed in the sidebar and directly inserted at the active cursor position upon clicking "Insert into Document".&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Production Script (Google Docs In-Editor Assistant)
&lt;/h4&gt;

&lt;h5&gt;
  
  
  1. Backend Script (&lt;code&gt;Code.gs&lt;/code&gt;)
&lt;/h5&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;onOpen&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;DocumentApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getUi&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createMenu&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;🤖 AI Assistant&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Open AI Sidebar&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;showSidebar&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addToUi&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;showSidebar&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;HtmlService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createHtmlOutputFromFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Sidebar&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setTitle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Context AI Editor&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;DocumentApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getUi&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;showSidebar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * Extracts selected text, executes prompt instruction, and returns result
 */&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;processSelectedText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;instruction&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;DocumentApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getActiveDocument&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;selection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getSelection&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;selection&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Please highlight text in the document first.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;selectedText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;elements&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;selection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getSelectedElements&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;el&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;elements&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;textElement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElement&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;asText&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isPartial&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;selectedText&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;textElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getText&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;substring&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getStartOffset&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getEndOffsetInclusive&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;
&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;selectedText&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;textElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getText&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;
&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;apiKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;PropertiesService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getScriptProperties&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;getProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GEMINI_API_KEY&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://generativelanguage.googleapis.com/v1beta/models/gemini-3.6-flash:generateContent?key=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Instruction: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;instruction&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;

Target Text:
&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;selectedText&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;UrlFetchApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;post&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;contentType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;muteHttpExceptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getResponseCode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Gemini Error: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getContentText&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;json&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getContentText&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;candidates&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * Inserts AI-generated content directly at current cursor position
 */&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;insertTextToDoc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;textToInsert&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;DocumentApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getActiveDocument&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getCursor&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insertText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;textToInsert&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBody&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;appendParagraph&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;textToInsert&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  2. Frontend Interface (&lt;code&gt;Sidebar.html&lt;/code&gt;)
&lt;/h5&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;base&lt;/span&gt; &lt;span class="na"&gt;target=&lt;/span&gt;&lt;span class="s"&gt;"_top"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Roboto&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;sans-serif&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;12px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;13px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;margin-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#1a73e8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#fff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;pointer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nf"&gt;#result&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;margin-top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;12px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#f1f3f4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;white-space&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;pre-wrap&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;12px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nc"&gt;.insert-btn&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#34a853&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;margin-top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h3&amp;gt;&lt;/span&gt;📝 AI Document Editor&lt;span class="nt"&gt;&amp;lt;/h3&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;onclick=&lt;/span&gt;&lt;span class="s"&gt;"executeAction('Summarize in 3 bullet points')"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;📌 3-Line Summary&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;onclick=&lt;/span&gt;&lt;span class="s"&gt;"executeAction('Translate into natural business English')"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;🌐 Translate to English&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"result"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Highlight text in the document and click an action above.&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"insert-btn"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"insertBtn"&lt;/span&gt; &lt;span class="na"&gt;onclick=&lt;/span&gt;&lt;span class="s"&gt;"insertResult()"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;📥 Insert into Document&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
      &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;latestResult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;executeAction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;instruction&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;result&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;innerText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Analyzing highlighted text...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;google&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;script&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;run&lt;/span&gt;
          &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;withSuccessHandler&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;latestResult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;result&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;innerText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;insertBtn&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;block&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
          &lt;span class="p"&gt;})&lt;/span&gt;
          &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;withFailureHandler&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Error: &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
          &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;processSelectedText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;instruction&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;insertResult&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;latestResult&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;google&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;script&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;run&lt;/span&gt;
          &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;withSuccessHandler&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Inserted successfully into document.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
          &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insertTextToDoc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;latestResult&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Key Advantages
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Zero Context Switching&lt;/strong&gt;: Users analyze and revise content directly within their active editing workflow without copying text back and forth to external chat windows.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Standardized Quality across Teams&lt;/strong&gt;: All team members sharing the file have instant access to identical, pre-configured AI prompts.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Limitations and Operational Considerations
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Desktop Browser Exclusivity&lt;/strong&gt;: Sidebars are supported on desktop web browsers and do not render on mobile Workspace applications.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Advanced Patterns and Extensions
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Spreadsheet Categorization Sidebar&lt;/strong&gt;: Classifies freeform survey responses in selected rows and inserts category tags into adjacent columns.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Slide Speaker Notes Generator&lt;/strong&gt;: Reads slide text elements and synthesizes natural presentation scripts directly into the Speaker Notes panel.&lt;/li&gt;
&lt;/ul&gt;

&lt;h5&gt;
  
  
  Related Articles and References
&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://tanaikech.github.io/2026/02/07/building-adaptive-learning-agents-with-a2ui-gemini-and-google-apps-script/" rel="noopener noreferrer"&gt;Building Adaptive Learning Agents with A2UI, Gemini, and Google Apps Script&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  7. Modern Local Development, CLI Tooling, and Local LLM Integration
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fb0038bnfqyipiemk1kfc.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fb0038bnfqyipiemk1kfc.jpg" alt="Figure 7: Modern local development environment (clasp/VS Code) integrating local LLMs and GAS" width="800" height="447"&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;Figure 7: Modern local development environment (clasp/VS Code) integrating local LLMs and GAS — Illustrates local TypeScript development, offline testing with gas-fakes, automated CI/CD deployment with clasp, and terminal execution with ggsrun.&lt;/em&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Technical Overview and Official References
&lt;/h4&gt;

&lt;p&gt;Integrating Google's official CLI (&lt;code&gt;@google/clasp&lt;/code&gt;), the offline mocking engine &lt;a href="https://github.com/brucemcpherson/gas-fakes" rel="noopener noreferrer"&gt;&lt;code&gt;gas-fakes&lt;/code&gt;&lt;/a&gt;, and the synchronous execution CLI &lt;a href="https://github.com/tanaikech/ggsrun" rel="noopener noreferrer"&gt;&lt;code&gt;ggsrun&lt;/code&gt;&lt;/a&gt; brings professional software engineering practices (VS Code, Git, TypeScript, GitHub Actions) directly to GAS projects.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Command-line Interface using clasp&lt;/strong&gt;: &lt;a href="https://developers.google.com/apps-script/guides/clasp" rel="noopener noreferrer"&gt;Official Guide&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Google Apps Script API Overview&lt;/strong&gt;: &lt;a href="https://developers.google.com/apps-script/api/overview" rel="noopener noreferrer"&gt;API Documentation&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;gas-fakes Repository&lt;/strong&gt;: &lt;a href="https://github.com/brucemcpherson/gas-fakes" rel="noopener noreferrer"&gt;GitHub (Bruce McPherson)&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ggsrun Repository&lt;/strong&gt;: &lt;a href="https://github.com/tanaikech/ggsrun" rel="noopener noreferrer"&gt;GitHub (Kanshi Tanaike)&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h4&gt;
  
  
  Concrete Example: &lt;code&gt;clasp&lt;/code&gt; ✕ &lt;code&gt;gas-fakes&lt;/code&gt; Automated CI/CD and &lt;code&gt;ggsrun&lt;/code&gt; Interactive CLI Control
&lt;/h4&gt;

&lt;p&gt;As shown in Figure 7, developers engineer TypeScript code locally and operate across three synchronized development layers:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Local Tier (CI/CD Unit Testing)&lt;/strong&gt;: Fast offline unit tests execute in Node.js via &lt;code&gt;gas-fakes&lt;/code&gt; ($0 quota cost).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deploy Tier (GitHub Actions Push)&lt;/strong&gt;: Merges to &lt;code&gt;main&lt;/code&gt; trigger automated deployments via &lt;code&gt;clasp push&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Local CLI Tier (&lt;code&gt;ggsrun&lt;/code&gt; Direct Execution)&lt;/strong&gt;: Developers use &lt;code&gt;ggsrun&lt;/code&gt; (requiring manual OAuth) from their local terminal to execute cloud GAS functions instantly without browser interaction.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Architecture Overview
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3rsn1kxn680x0ac8nttu.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3rsn1kxn680x0ac8nttu.jpg" alt="Figure 7-1: GitHub Actions CI/CD Pipeline Architecture with gas-fakes and clasp" width="800" height="447"&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;Figure 7-1: GitHub Actions CI/CD Pipeline Architecture with gas-fakes and clasp — Illustrates automated push-triggered workflow executing offline unit tests and deploying verified code to GAS cloud environments.&lt;/em&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  GitHub Actions CI/CD Pipeline (&lt;code&gt;.github/workflows/deploy.yml&lt;/code&gt;)
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy Google Apps Script&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;main&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;test_and_deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install dependencies &amp;amp; gas-fakes&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm ci&lt;/span&gt;

      &lt;span class="c1"&gt;# 1. Execute fast offline unit tests with gas-fakes&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run offline unit tests with gas-fakes&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm test&lt;/span&gt;

      &lt;span class="c1"&gt;# 2. Deploy to GAS via clasp&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy to GAS via clasp&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;CLASPRC_JSON&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.CLASPRC_JSON }}&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;echo "$CLASPRC_JSON" &amp;gt; ~/.clasprc.json&lt;/span&gt;
          &lt;span class="s"&gt;npx clasp push --force&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Operational Note: Separation between &lt;code&gt;ggsrun&lt;/code&gt; and &lt;code&gt;clasp&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;code&gt;ggsrun&lt;/code&gt; is a high-performance Go CLI designed for interactive developer control requiring manual OAuth 2.0 browser authorization. Consequently, headless GitHub Actions CI/CD pipelines rely on &lt;code&gt;gas-fakes&lt;/code&gt; and &lt;code&gt;clasp&lt;/code&gt;, while &lt;code&gt;ggsrun&lt;/code&gt; serves as the developer's direct terminal bridge for rapid post-deployment testing and batch execution.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Key Advantages
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Modern Software Engineering Standards&lt;/strong&gt;: Git branching, TypeScript type safety, instant offline mock testing, and automated GitHub Actions deployments fully integrated.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Direct Terminal Control via &lt;code&gt;ggsrun&lt;/code&gt;&lt;/strong&gt;: Execute and debug cloud GAS functions directly from the terminal without opening the web editor.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;On-Premises Data Privacy&lt;/strong&gt;: Process confidential enterprise data locally with Ollama (Llama 3) and sync only sanitized summaries to Google Workspace via GAS Web Apps.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Limitations and Operational Considerations
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Inbound Communication Setup&lt;/strong&gt;: Pushing from local machines to GAS Web Apps is straightforward; sending requests from GAS back to local environments requires secure tunnels (Cloudflare Tunnel or ngrok).&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Advanced Patterns and Extensions
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Local Batch Automation via &lt;code&gt;ggsrun&lt;/code&gt;&lt;/strong&gt;: Python or Node.js data processing scripts invoke &lt;code&gt;ggsrun&lt;/code&gt; to write aggregated metrics directly into Sheets and Docs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Confidential Contract Review with Local LLMs&lt;/strong&gt;: Legal teams analyze proprietary NDAs locally using Ollama and log review status to Sheets via GAS.&lt;/li&gt;
&lt;/ul&gt;

&lt;h5&gt;
  
  
  Related Articles and References
&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://tanaikech.github.io/2026/03/25/mastering-google-apps-script-ci/cd-seamless-github-actions-integration-with-gas-fakes/" rel="noopener noreferrer"&gt;Mastering Google Apps Script CI/CD: Seamless GitHub Actions Integration with gas-fakes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tanaikech.github.io/2026/06/15/orchestrating-google-workspace-with-antigravity-cli-a-high-performance-agentic-framework/" rel="noopener noreferrer"&gt;Orchestrating Google Workspace with Antigravity CLI: A High-Performance Agentic Framework&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tanaikech.github.io/2025/11/15/from-data-silos-to-unified-rag-gemini-cli-extensions-unify-local-and-google-workspace-for-a-powerful-file-search/" rel="noopener noreferrer"&gt;From Data Silos to Unified RAG: Gemini CLI Extensions Unify Local and Google Workspace for a Powerful File Search&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tanaikech.github.io/2025/10/30/bridging-the-gap-seamless-integration-for-local-google-apps-script-development/" rel="noopener noreferrer"&gt;Bridging the Gap: Seamless Integration for Local Google Apps Script Development&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tanaikech.github.io/2025/10/21/streamlining-google-apps-script-development-with-gemini-cli-extensions-and-vscode/" rel="noopener noreferrer"&gt;Streamlining Google Apps Script Development with Gemini CLI Extensions and VSCode&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  8. Deterministic Output Guardrails &amp;amp; Sandboxing for AI Outputs
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgqg72xe1ilphunjydi49.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgqg72xe1ilphunjydi49.jpg" alt="Figure 8: Multi-layer deterministic validation guardrails inspecting AI outputs" width="800" height="447"&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;Figure 8: Multi-layer deterministic validation guardrails inspecting AI outputs — Illustrates 4-tier inspection gates encompassing Gemini responseSchema syntax enforcement, GAS business rule verification, and sandboxed pre-execution validation.&lt;/em&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Technical Overview and Official References
&lt;/h4&gt;

&lt;p&gt;While Generative AI provides unmatched flexibility with unstructured text, it carries intrinsic hallucination risks. In the emerging era of &lt;strong&gt;Vibe Coding&lt;/strong&gt;—where developers and business users prompt LLMs to generate and execute code spontaneously on the fly—running unverified AI-generated script logic directly in production Workspace environments poses severe security and data-corruption vulnerabilities.&lt;/p&gt;

&lt;p&gt;By combining Gemini's &lt;strong&gt;&lt;code&gt;responseSchema&lt;/code&gt; (native JSON Schema enforcement)&lt;/strong&gt; at Layer 1 and GAS JavaScript logic at Layer 2 with &lt;strong&gt;sandboxed pre-execution validation (&lt;a href="https://github.com/brucemcpherson/gas-fakes" rel="noopener noreferrer"&gt;&lt;code&gt;gas-fakes&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://github.com/tanaikech/ggsrun" rel="noopener noreferrer"&gt;&lt;code&gt;ggsrun&lt;/code&gt;&lt;/a&gt;)&lt;/strong&gt; at Layer 3 via the Model Context Protocol (MCP), developers establish multi-layer defense gates ensuring vibe-coded scripts run safely in isolated sandboxes before ever touching production data.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Official Reference&lt;/strong&gt;: &lt;a href="https://ai.google.com/gemini-api/docs/structured-output" rel="noopener noreferrer"&gt;Gemini API: Structured Outputs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Antigravity CLI Orchestration Framework&lt;/strong&gt;: &lt;a href="https://tanaikech.github.io/2026/06/15/orchestrating-google-workspace-with-antigravity-cli-a-high-performance-agentic-framework/" rel="noopener noreferrer"&gt;Orchestrating Google Workspace with Antigravity CLI&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sandboxing Guide&lt;/strong&gt;: &lt;a href="https://tanaikech.github.io/2026/06/29/exploring-sandboxing-for-ai-generated-google-apps-script/" rel="noopener noreferrer"&gt;Exploring Sandboxing for AI-Generated Google Apps Script&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fake-Sandbox Guide&lt;/strong&gt;: &lt;a href="https://tanaikech.github.io/2025/08/30/a-fake-sandbox-for-google-apps-script-a-feasibility-study-on-securely-executing-code-generated-by-gemini-cli/" rel="noopener noreferrer"&gt;A Fake-Sandbox for Google Apps Script&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Schema Enforcement Guide&lt;/strong&gt;: &lt;a href="https://tanaikech.github.io/2024/05/21/taming-the-wild-output-effective-control-of-gemini-api-response-formats-with-response_schema/" rel="noopener noreferrer"&gt;Taming the Wild Output: Effective Control of Gemini API Response Formats&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h4&gt;
  
  
  Concrete Example 1: Schema Enforcement and Deterministic Guardrails for Expense Claims
&lt;/h4&gt;

&lt;p&gt;As illustrated in Figure 8, multi-layer defense guardrails validate structured AI data outputs across sequential stages:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Input Ingestion&lt;/strong&gt;: Receipt notes or expense claims submitted as unstructured natural language.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Layer 1 (Gemini &lt;code&gt;responseSchema&lt;/code&gt;)&lt;/strong&gt;: Native model-level schema enforcement guarantees structural JSON syntax, required fields, and enumerated types.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Layer 2 (GAS Deterministic Validation)&lt;/strong&gt;: JavaScript logic strictly verifies business rules (positive integer amounts, approved expense categories, valid YYYY-MM-DD dates).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deterministic Storage&lt;/strong&gt;: Only verified, fully compliant data is committed to production Google Sheets.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Execution Instructions
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;Open the Apps Script editor attached to your Google Sheet.&lt;/li&gt;
&lt;li&gt;Navigate to &lt;strong&gt;&lt;a href="https://dev.togear%20icon"&gt;Project Settings&lt;/a&gt; &amp;gt; [Script Properties]&lt;/strong&gt; and add &lt;code&gt;GEMINI_API_KEY&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Paste the script below into &lt;code&gt;Code.gs&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;&lt;code&gt;testExecuteAiWithGuardrail&lt;/code&gt;&lt;/strong&gt; from the top function menu and click &lt;strong&gt;[Run]&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Check the execution log and observe the verified record securely appended to the "ExpenseClaims" sheet.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Production Script (Data Extraction &amp;amp; Validation Implementation)
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
 * Test function: execute from Apps Script editor with 1 click
 */&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;testExecuteAiWithGuardrail&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sampleInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Yesterday on 2026-08-20, I paid $35 for an Uber ride to visit a prospective client.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;executeAiWithGuardrail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sampleInput&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;Logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Guardrail validation succeeded: &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * Validates AI output across multiple guardrails and records to Sheets
 * @param {string} userInput Unstructured user expense description
 * @return {object} Verified structured expense record
 */&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;executeAiWithGuardrail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userInput&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;apiKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;PropertiesService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getScriptProperties&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;getProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GEMINI_API_KEY&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GEMINI_API_KEY is not set. Configure it in Script Properties.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Layer 1: Native JSON Schema Enforcement via responseSchema&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;responseSchema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;OBJECT&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;INTEGER&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Expense amount as a positive integer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;category&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;STRING&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;enum&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Travel&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Entertainment&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Office Supplies&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Standard expense category&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;date&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;STRING&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Transaction date in YYYY-MM-DD format&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;amount&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;category&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;date&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://generativelanguage.googleapis.com/v1beta/models/gemini-3.6-flash:generateContent?key=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;UrlFetchApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;post&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;contentType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Extract expense details from the following request.
Input: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;userInput&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="na"&gt;generationConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;responseMimeType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;responseSchema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;responseSchema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="na"&gt;muteHttpExceptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getResponseCode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Gemini API Error: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getResponseCode&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt; - &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getContentText&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;jsonResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getContentText&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rawJson&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jsonResponse&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;candidates&lt;/span&gt;&lt;span class="p"&gt;?.[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]?.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;?.[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]?.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;rawJson&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;No response payload received from AI.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;parsed&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;parsed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rawJson&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`JSON Parse Failure: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Layer 2: Deterministic Business Rule Validation in GAS&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;parsed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;amount&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;number&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;parsed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;amount&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nb"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isInteger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;parsed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Invalid expense amount: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;parsed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;validCategories&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Travel&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Entertainment&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Office Supplies&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;validCategories&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;parsed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;category&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Invalid expense category: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;parsed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;category&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="sr"&gt;/^&lt;/span&gt;&lt;span class="se"&gt;\d{4}&lt;/span&gt;&lt;span class="sr"&gt;-&lt;/span&gt;&lt;span class="se"&gt;\d{2}&lt;/span&gt;&lt;span class="sr"&gt;-&lt;/span&gt;&lt;span class="se"&gt;\d{2}&lt;/span&gt;&lt;span class="sr"&gt;$/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;parsed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nf"&gt;isNaN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;parsed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;date&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Invalid date format: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;parsed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;date&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Persist only clean, fully compliant data to Google Sheets&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ss&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;SpreadsheetApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getActiveSpreadsheet&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;sheet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ss&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getSheetByName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ExpenseClaims&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;sheet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ss&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insertSheet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ExpenseClaims&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendRow&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Date&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Category&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Amount&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;LoggedAt&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendRow&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;parsed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;parsed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;category&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;parsed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;()]);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;parsed&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h4&gt;
  
  
  Concrete Example 2: Safe Execution of Vibe-Coded GAS via &lt;code&gt;gas-fakes&lt;/code&gt; and &lt;code&gt;ggsrun&lt;/code&gt; Sandboxes
&lt;/h4&gt;

&lt;p&gt;In local terminal workflows (VS Code / terminal) or cloud-hosted agent environments where users practice "Vibe Coding"—generating and running GAS scripts on the fly from natural language prompts—&lt;strong&gt;Layer 3: Fake-Sandbox Pre-Execution&lt;/strong&gt; serves as a vital safety mechanism:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Dynamic Code Synthesis&lt;/strong&gt;: An autonomous agent or developer prompts Gemini to generate a GAS script (e.g., &lt;em&gt;"Clean up unorganized files across my project folder"&lt;/em&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sandboxed Dry-Run&lt;/strong&gt;: Before executing against production Workspace infrastructure, the unverified script is executed inside a local or virtual Fake-Sandbox powered by &lt;a href="https://github.com/brucemcpherson/gas-fakes" rel="noopener noreferrer"&gt;&lt;code&gt;gas-fakes&lt;/code&gt;&lt;/a&gt; or &lt;a href="https://github.com/tanaikech/ggsrun" rel="noopener noreferrer"&gt;&lt;code&gt;ggsrun&lt;/code&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pre-Execution Threat Neutralization&lt;/strong&gt;: Sandboxes intercept and block destructive operations (such as &lt;code&gt;DriveApp.getFileById().setTrashed(true)&lt;/code&gt; or unauthorized &lt;code&gt;GmailApp.sendEmail()&lt;/code&gt; broadcasts), infinite loops, and scope violations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verified Production Deployment&lt;/strong&gt;: Only scripts that pass all sandbox safety assertions are pushed to production Google Workspace environments via MCP or &lt;code&gt;clasp&lt;/code&gt; / &lt;code&gt;ggsrun&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For a comprehensive architectural breakdown, refer to &lt;a href="https://tanaikech.github.io/2026/06/15/orchestrating-google-workspace-with-antigravity-cli-a-high-performance-agentic-framework/" rel="noopener noreferrer"&gt;Orchestrating Google Workspace with Antigravity CLI: A High-Performance Agentic Framework&lt;/a&gt;.&lt;/p&gt;




&lt;h4&gt;
  
  
  Key Advantages
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Zero Contamination of Production Databases&lt;/strong&gt;: Strict 2-tier validation completely eliminates broken schemas, type errors, and hallucinated fields.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Safe Execution of Vibe-Coded Scripts&lt;/strong&gt;: &lt;code&gt;gas-fakes&lt;/code&gt; and &lt;code&gt;ggsrun&lt;/code&gt; sandboxing engines ensure dynamically synthesized code cannot corrupt enterprise files or trigger unintended operations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Early Detection and Automated Retry&lt;/strong&gt;: Self-correcting retry loops feed validation errors back to Gemini prompts for automatic query adjustment.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Limitations and Operational Considerations
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Schema Synchronization&lt;/strong&gt;: When business rules evolve, both the &lt;code&gt;responseSchema&lt;/code&gt; definition and GAS validation arrays must be updated in sync.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Advanced Patterns and Extensions
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AI-Generated SQL Sanitization&lt;/strong&gt;: Regex filters scan AI-generated SQL queries for destructive commands (&lt;code&gt;DROP&lt;/code&gt;, &lt;code&gt;DELETE&lt;/code&gt;) before execution.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Template Placeholder Verification&lt;/strong&gt;: Ensures AI translations preserve required template tokens (e.g., &lt;code&gt;{userName}&lt;/code&gt;, &lt;code&gt;{orderId}&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Master Data Cross-Referencing&lt;/strong&gt;: Validates that AI-extracted customer names or product IDs exist in master spreadsheets using fast &lt;code&gt;Set&lt;/code&gt;/&lt;code&gt;Map&lt;/code&gt; lookups.&lt;/li&gt;
&lt;/ul&gt;

&lt;h5&gt;
  
  
  Related Articles and References
&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://tanaikech.github.io/2026/06/15/orchestrating-google-workspace-with-antigravity-cli-a-high-performance-agentic-framework/" rel="noopener noreferrer"&gt;Orchestrating Google Workspace with Antigravity CLI: A High-Performance Agentic Framework&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tanaikech.github.io/2026/06/29/exploring-sandboxing-for-ai-generated-google-apps-script/" rel="noopener noreferrer"&gt;Exploring Sandboxing for AI-Generated Google Apps Script&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tanaikech.github.io/2025/08/30/a-fake-sandbox-for-google-apps-script-a-feasibility-study-on-securely-executing-code-generated-by-gemini-cli/" rel="noopener noreferrer"&gt;A Fake-Sandbox for Google Apps Script&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tanaikech.github.io/2024/05/21/taming-the-wild-output-effective-control-of-gemini-api-response-formats-with-response_schema/" rel="noopener noreferrer"&gt;Taming the Wild Output: Effective Control of Gemini API Response Formats with response_schema&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tanaikech.github.io/2024/05/07/gemini-api-with-json-schema/" rel="noopener noreferrer"&gt;Gemini API with JSON schema&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  9. Human-in-the-Loop (HITL) Interactive Approval Workflows
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fz7psmq0cb4z5vy6vg5kp.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fz7psmq0cb4z5vy6vg5kp.jpg" alt="Figure 9: Human-in-the-Loop interactive approval workflow architecture" width="800" height="447"&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;Figure 9: Human-in-the-Loop interactive approval workflow architecture — Illustrates AI drafting followed by mandatory spreadsheet checkbox authorization (onEdit) before irreversible email dispatch.&lt;/em&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Concrete Example: AI Response Drafting and Spreadsheet-Based One-Click Approval
&lt;/h4&gt;

&lt;p&gt;As illustrated in Figure 9, the Human-in-the-Loop (HITL) approval workflow executes through five secure stages:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Customer submits an inquiry email; Gemini analyzes the context and drafts a suggested response.&lt;/li&gt;
&lt;li&gt;The draft is staged in the "ApprovalQueue" sheet or saved in Gmail's "Drafts" folder.&lt;/li&gt;
&lt;li&gt;A support manager reviews the draft and clicks the "Approve" checkbox in column E.&lt;/li&gt;
&lt;li&gt;An installable &lt;code&gt;onEdit&lt;/code&gt; trigger immediately detects the approval event.&lt;/li&gt;
&lt;li&gt;GAS executes the finalized email dispatch, records timestamped completion, and clears the checkbox.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Execution Instructions
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;Open the Apps Script editor attached to your Google Sheet.&lt;/li&gt;
&lt;li&gt;Paste the script below into &lt;code&gt;Code.gs&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;&lt;code&gt;setupTestApprovalQueue&lt;/code&gt;&lt;/strong&gt; from the function dropdown and click &lt;strong&gt;[Run]&lt;/strong&gt; to automatically scaffold the "ApprovalQueue" sheet with sample records and checkboxes.&lt;/li&gt;
&lt;li&gt;Navigate to &lt;strong&gt;&lt;a href="https://dev.toclock%20icon"&gt;Triggers&lt;/a&gt; &amp;gt; [Add Trigger]&lt;/strong&gt;, select &lt;code&gt;onEditTrigger&lt;/code&gt;, and set the event type to &lt;strong&gt;"On edit"&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Return to the sheet and check the box in column E to trigger the live email dispatch.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Production Script (Interactive Checkbox Approval Gate)
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
 * Test setup function: scaffolds the approval sheet and sample records
 */&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;setupTestApprovalQueue&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ss&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;SpreadsheetApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getActiveSpreadsheet&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;sheet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ss&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getSheetByName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ApprovalQueue&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;sheet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ss&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insertSheet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ApprovalQueue&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clear&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendRow&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;CustomerEmail&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;DraftID&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Subject&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;BodyDraft&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Approve&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Status&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Timestamp&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

  &lt;span class="nx"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendRow&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="nx"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getActiveUser&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;getEmail&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;test@example.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;draft_001&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Customer Support Response&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Thank you for contacting enterprise support. Regarding your inquiry...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;PENDING_REVIEW&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;""&lt;/span&gt;
  &lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="nx"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getRange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;E2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;insertCheckboxes&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;SpreadsheetApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flush&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;Logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Approval queue scaffolded. Check box E2 to test live dispatch.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * Installable onEdit trigger monitoring human approval checkboxes.
 * Dispatches customer communications only when explicitly approved.
 */&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;onEditTrigger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;range&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sheet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;range&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getSheet&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getName&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ApprovalQueue&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;range&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getRow&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;col&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;range&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getColumn&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="c1"&gt;// Column 5: Approval Checkbox (TRUE / FALSE)&lt;/span&gt;
  &lt;span class="c1"&gt;// Column 6: Execution Status&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;col&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;TRUE&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getRange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;getValue&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;APPROVED_AND_SENT&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;customerEmail&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getRange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;getValue&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;emailSubject&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getRange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;getValue&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;aiDraftBody&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getRange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;getValue&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;customerEmail&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;aiDraftBody&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getRange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;setValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ERROR: Missing Fields&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// 1. Dispatch finalized email&lt;/span&gt;
    &lt;span class="nx"&gt;GmailApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sendEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;customerEmail&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;emailSubject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;aiDraftBody&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// 2. Lock row status to prevent duplicate dispatches&lt;/span&gt;
    &lt;span class="nx"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getRange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;setValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;APPROVED_AND_SENT&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getRange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;clearContent&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Clear checkbox&lt;/span&gt;
    &lt;span class="nx"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getRange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;setValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

    &lt;span class="nx"&gt;SpreadsheetApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getActiveSpreadsheet&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="s2"&gt;`Email dispatched to &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;customerEmail&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Approval Complete&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Important Trigger Requirement&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Simple &lt;code&gt;onEdit(e)&lt;/code&gt; triggers run in restricted read-only authorization mode and cannot invoke &lt;code&gt;GmailApp.sendEmail()&lt;/code&gt;. You must configure an &lt;strong&gt;Installable Trigger&lt;/strong&gt; via &lt;strong&gt;&lt;a href="https://dev.toclock%20icon"&gt;Triggers&lt;/a&gt; &amp;gt; [Add Trigger] &amp;gt; [On edit]&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Key Advantages
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Zero Accidental Dispatches&lt;/strong&gt;: AI drafts emails and classifies tickets, but irreversible actions require human approval.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Intuitive Collaborative Console&lt;/strong&gt;: Operational managers approve tasks directly within familiar spreadsheet interfaces.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Limitations and Operational Considerations
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Rapid Clicking Race Conditions&lt;/strong&gt;: When users check multiple boxes rapidly, use LockService to prevent race conditions.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Advanced Patterns and Extensions
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Executive Expense Authorization&lt;/strong&gt;: Department heads check approval boxes on expense reports, triggering automated bank CSV export and accounting notifications.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Applicant Screening Gates&lt;/strong&gt;: HR reviewers inspect AI-screened candidate profiles and check boxes to trigger automated interview invitation emails.&lt;/li&gt;
&lt;/ul&gt;

&lt;h5&gt;
  
  
  Related Articles and References
&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://tanaikech.github.io/2022/06/16/protecting-cells-of-spreadsheet-by-clicking-checkbox-using-google-apps-script/" rel="noopener noreferrer"&gt;Protecting Cells of Spreadsheet by Clicking Checkbox using Google Apps Script&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tanaikech.github.io/2020/05/27/detecting-quickly-checked-checkboxes-on-google-spreadsheet-using-google-apps-script/" rel="noopener noreferrer"&gt;Detecting Quickly Checked Checkboxes on Google Spreadsheet using Google Apps Script&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  10. External SaaS Webhook Ingestion &amp;amp; Secure Proxy Gateways
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F26yskttnzdt12tn6xfr2.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F26yskttnzdt12tn6xfr2.jpg" alt="Figure 10: Secure proxy gateway ingesting external SaaS webhooks and shielding API keys" width="800" height="447"&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;Figure 10: Secure proxy gateway ingesting external SaaS webhooks and shielding API keys — Illustrates zero-trust webhook ingestion, server-side secret encapsulation via PropertiesService, and downstream API forwarding.&lt;/em&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Concrete Example: External SaaS Webhook Ingestion &amp;amp; Secure Proxy Gateways
&lt;/h4&gt;

&lt;p&gt;As illustrated in Figure 10, the secure proxy gateway operates across four zero-trust stages:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;External SaaS platforms (GitHub, Stripe, Slack) transmit event webhooks to the GAS Web App endpoint.&lt;/li&gt;
&lt;li&gt;GAS validates HMAC request signatures or Bearer tokens to eliminate unauthorized traffic.&lt;/li&gt;
&lt;li&gt;GAS retrieves sensitive third-party API credentials from &lt;code&gt;PropertiesService&lt;/code&gt;, keeping secrets completely hidden from AI prompts and client contexts.&lt;/li&gt;
&lt;li&gt;Gemini analyzes the payload for task priority, and GAS posts structured tasks downstream while synchronizing Workspace records.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Execution Instructions
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;Open &lt;strong&gt;[Project Settings] &amp;gt; [Script Properties]&lt;/strong&gt; and add property &lt;code&gt;SAAS_API_SECRET_KEY&lt;/code&gt; with your SaaS API token.&lt;/li&gt;
&lt;li&gt;Paste the script below and run &lt;strong&gt;&lt;code&gt;testSecurePostTaskToExternalSaaS&lt;/code&gt;&lt;/strong&gt; to verify that external payloads are dispatched with server-side injected credentials.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Production Script (Credential-Shielded Task Forwarding)
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
 * Test function: execute from Apps Script editor
 */&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;testSecurePostTaskToExternalSaaS&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;securePostTaskToExternalSaaS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Q3 Financial Report Synthesis&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;AI-summarized task: Aggregate multi-currency ledgers and compile summary slide deck.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * Forward AI-summarized tasks securely to an external SaaS project management tool.
 */&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;securePostTaskToExternalSaaS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;taskTitle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;taskDetail&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Retrieve SaaS secrets from encrypted Script Properties&lt;/span&gt;
  &lt;span class="c1"&gt;// Secrets are NEVER exposed to client browsers or LLM prompts&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;saasApiKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;PropertiesService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getScriptProperties&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;getProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SAAS_API_SECRET_KEY&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;endpoint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://api.example-saas.com/v1/tasks&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;taskTitle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;taskDetail&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;createdAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toISOString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;post&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;Authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Bearer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;saasApiKey&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;X-Custom-Header&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GAS-Secure-Proxy&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;muteHttpExceptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;UrlFetchApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;Logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`External API Response Status: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getResponseCode&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Key Advantages
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Prompt Injection Resilience&lt;/strong&gt;: Even if an LLM is manipulated via adversarial inputs, it cannot leak corporate API keys because authentication headers are injected server-side by GAS.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Centralized SaaS Routing&lt;/strong&gt;: Consolidates authentication flows (Bearer tokens, Basic auth, HMAC signatures) across multiple SaaS vendors.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Limitations and Operational Considerations
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Payload Size Limits&lt;/strong&gt;: Standard &lt;code&gt;UrlFetchApp&lt;/code&gt; requests support payloads up to 50 MB.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Advanced Patterns and Extensions
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Customer Support Router&lt;/strong&gt;: Classifies incoming Zendesk/Intercom webhooks with Gemini and routes urgent tickets to Jira and VIP notices to Slack.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Payment Dispute Orchestrator&lt;/strong&gt;: Ingests Stripe dispute webhooks, retrieves customer transaction logs from Drive, and prepares an audit dossier.&lt;/li&gt;
&lt;/ul&gt;

&lt;h5&gt;
  
  
  Related Articles and References
&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://tanaikech.github.io/2026/08/03/unlocking-infinite-automation-integrating-google-apps-script-with-gemini-spark/" rel="noopener noreferrer"&gt;Unlocking Infinite Automation: Integrating Google Apps Script with Gemini Spark&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tanaikech.github.io/2023/07/25/understanding-flow-of-request-to-web-apps-created-by-google-apps-script/" rel="noopener noreferrer"&gt;Understanding Flow of Request to Web Apps Created by Google Apps Script&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tanaikech.github.io/2017/09/07/uploading-image-files-to-slack-using-incoming-webhooks-by-google-apps-script/" rel="noopener noreferrer"&gt;Uploading Image Files to Slack Using Incoming Webhooks by Google Apps Script&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  11. High-Throughput Hybrid Batch Processing &amp;amp; Prompt Request Packing
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fruz4sdj08mrsizq9655d.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fruz4sdj08mrsizq9655d.jpg" alt="Figure 11: Hybrid batch processing architecture combining deterministic logic and packed AI inference" width="800" height="447"&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;Figure 11: Hybrid batch processing architecture combining deterministic logic and packed AI inference — Illustrates zero-cost in-memory pre-screening for 98% of rows and chunked request packing for the remaining 2% edge cases.&lt;/em&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Technical Overview and Official References
&lt;/h4&gt;

&lt;p&gt;Processing tens of thousands of spreadsheet rows with LLMs incurs prohibitive latency and cost. As illustrated in Figure 11, applying &lt;strong&gt;Deterministic Screening&lt;/strong&gt; (filtering 98% of standard rows using in-memory JavaScript regexes at $0 cost) and &lt;strong&gt;Prompt Request Packing&lt;/strong&gt; (chunking 20 unstructured rows into a single batched JSON array payload) reduces API invocations by up to 95% while staying well within the GAS 6-minute execution window.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Batch Operations Best Practices&lt;/strong&gt;: &lt;a href="https://developers.google.com/apps-script/guides/support/best-practices" rel="noopener noreferrer"&gt;Google Sheets Best Practices&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Time-driven Triggers Guide&lt;/strong&gt;: &lt;a href="https://developers.google.com/apps-script/guides/triggers/installable#time-driven_triggers" rel="noopener noreferrer"&gt;Installable Triggers&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h4&gt;
  
  
  Concrete Example: Cleansing 10,000 Customer Records with Request Packing
&lt;/h4&gt;

&lt;p&gt;As illustrated in Figure 11 and Figure 11-1, high-throughput hybrid batch processing combines two optimization stages:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The script ingests thousands of raw spreadsheet rows into memory in a single read operation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deterministic Pre-Screening&lt;/strong&gt;: In-memory JavaScript regexes cleanse 98% of standard rows in milliseconds at zero API cost.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Chunked Request Packing&lt;/strong&gt;: The remaining 2% of unstructured edge cases are packed into chunks of 20 items per JSON array prompt, requiring only 10 API requests instead of 200.&lt;/li&gt;
&lt;li&gt;Parsed results are written back to Google Sheets in a single batch, avoiding platform timeouts and slashing API costs by 98%.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Batch Packing Architecture Overview
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvxkgon5mmb2uzruskqdb.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvxkgon5mmb2uzruskqdb.jpg" alt="Figure 11-1: High-Throughput Hybrid Batch Processing &amp;amp; Prompt Request Packing Architecture" width="800" height="447"&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;Figure 11-1: High-Throughput Hybrid Batch Processing &amp;amp; Prompt Request Packing Architecture — Illustrates the multi-stage pipeline combining zero-cost in-memory pre-screening for 98% of rows and chunked request packing for the remaining 2% edge cases.&lt;/em&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Execution Instructions
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;Open your Apps Script project and register &lt;code&gt;GEMINI_API_KEY&lt;/code&gt; under Script Properties.&lt;/li&gt;
&lt;li&gt;Paste the script below into &lt;code&gt;Code.gs&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Run &lt;strong&gt;&lt;code&gt;setupSampleDataAndRunBatch&lt;/code&gt;&lt;/strong&gt; from the function dropdown to scaffold sample customer rows and execute the hybrid batch pipeline with request packing.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Production Script (Chunked Request Packing)
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
 * Test setup function: scaffolds sample dataset and triggers batch execution
 */&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;setupSampleDataAndRunBatch&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ss&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;SpreadsheetApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getActiveSpreadsheet&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;sheet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ss&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getSheetByName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;RawCustomerData&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;sheet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ss&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insertSheet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;RawCustomerData&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clear&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendRow&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;PhoneNumber (Raw)&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sampleRows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;090-1234-5678&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;03-1234-5678&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;09012345678&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;      &lt;span class="c1"&gt;// Irregular (Routed to AI packing queue)&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;080 9876 5432&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;    &lt;span class="c1"&gt;// Irregular (Routed to AI packing queue)&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0120-111-222&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="nx"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getRange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sampleRows&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;setValues&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sampleRows&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;SpreadsheetApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flush&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="nf"&gt;processHybridBatch&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;processHybridBatch&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sheet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;SpreadsheetApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getActiveSpreadsheet&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;getSheetByName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;RawCustomerData&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getDataRange&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;getValues&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;aiQueue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;phoneRegex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sr"&gt;/^0&lt;/span&gt;&lt;span class="se"&gt;\d{1,4}&lt;/span&gt;&lt;span class="sr"&gt;-&lt;/span&gt;&lt;span class="se"&gt;\d{1,4}&lt;/span&gt;&lt;span class="sr"&gt;-&lt;/span&gt;&lt;span class="se"&gt;\d{4}&lt;/span&gt;&lt;span class="sr"&gt;$/&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Standard format checker&lt;/span&gt;

  &lt;span class="c1"&gt;// Step 1: Fast deterministic screening in GAS memory ($0 cost)&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rawPhone&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;phoneRegex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rawPhone&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Deterministic_Cleansed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;rawPhone&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;aiQueue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;rowIndex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;rawText&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;rawPhone&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Step 2: Pack edge cases into chunks of 20 items per API request&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;aiQueue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;apiKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;PropertiesService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getScriptProperties&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;getProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GEMINI_API_KEY&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;chunkSize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;aiQueue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;chunkSize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;chunk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aiQueue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;chunkSize&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`Normalize the following irregular phone number records into standard format (e.g., 090-1234-5678).
Return strictly a JSON array preserving the original item order.
Input List: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://generativelanguage.googleapis.com/v1beta/models/gemini-3.6-flash:generateContent?key=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;UrlFetchApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;post&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;contentType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
          &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;prompt&lt;/span&gt; &lt;span class="p"&gt;}]&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
          &lt;span class="na"&gt;generationConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;responseMimeType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;}),&lt;/span&gt;
        &lt;span class="na"&gt;muteHttpExceptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;

      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getResponseCode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parsedResults&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getContentText&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nx"&gt;candidates&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;parsedResults&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
            &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rowIndex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rawText&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;AI_Cleansed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;normalized&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rawText&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="p"&gt;]);&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;Logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Batch execution complete. Total records processed: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Key Advantages
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;95% Call Reduction via Packing&lt;/strong&gt;: Compacting 200 edge cases into chunks of 20 reduces round-trip HTTP overhead from 200 requests to 10.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero-Cost Pre-Filtering&lt;/strong&gt;: Deterministic JavaScript array operations filter thousands of items in memory in milliseconds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;6-Minute Platform Compliance&lt;/strong&gt;: Dramatically minimized network round-trips guarantee execution finishes well within GAS limits.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Limitations and Operational Considerations
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;6-Minute Execution Limit&lt;/strong&gt;: For datasets exceeding 50,000 items, implement continuation patterns using time-driven triggers.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Advanced Patterns and Extensions
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Accounting Account Classification&lt;/strong&gt;: Match 90% of known vendors via master dictionary lookups ($0) and pack the remaining 10% for AI inference.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mass Product Review Sentiment Analysis&lt;/strong&gt;: Star 5 and Star 1 reviews are scored by numerical logic; ambiguous Star 2–4 reviews are batch-analyzed with Gemini.&lt;/li&gt;
&lt;/ul&gt;

&lt;h5&gt;
  
  
  Related Articles and References
&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://tanaikech.github.io/2024/04/26/batch-processing-powerhouse-leverage-gemini-1.5-api-and-google-apps-script-for-efficient-content-workflows/" rel="noopener noreferrer"&gt;Batch Processing Powerhouse: Leverage Gemini API and Google Apps Script&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tanaikech.github.io/2020/11/13/replacing-multiple-values-in-google-spreadsheet-with-low-process-cost-using-google-apps-script/" rel="noopener noreferrer"&gt;Replacing Multiple Values in Google Spreadsheet with Low Process Cost&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  12. Token &amp;amp; Cost Optimization via CacheService and PropertiesService
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fon6lz25gwl8koe61kfnm.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fon6lz25gwl8koe61kfnm.jpg" alt="Figure 12: Multi-tier caching architecture with CacheService and PropertiesService" width="800" height="447"&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;Figure 12: Multi-tier caching architecture with CacheService and PropertiesService — Illustrates cryptographic MD5 prompt hashing, sub-millisecond in-memory cache retrieval, and API bypass optimization.&lt;/em&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Technical Overview and Official References
&lt;/h4&gt;

&lt;p&gt;Google Apps Script provides two primary native storage services for state and data persistence across executions: &lt;strong&gt;&lt;code&gt;CacheService&lt;/code&gt;&lt;/strong&gt;, an ultra-fast in-memory transient key-value cache (retaining entries for up to 6 hours / 21,600 seconds), and &lt;strong&gt;&lt;code&gt;PropertiesService&lt;/code&gt;&lt;/strong&gt;, an encrypted persistent key-value store. Combining these services constructs a high-performance multi-tier caching layer that eliminates duplicate LLM inferences and achieves sub-millisecond response latencies.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cache Service Reference&lt;/strong&gt;: &lt;a href="https://developers.google.com/apps-script/reference/cache/" rel="noopener noreferrer"&gt;Official Guide&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Properties Service Reference&lt;/strong&gt;: &lt;a href="https://developers.google.com/apps-script/reference/properties/" rel="noopener noreferrer"&gt;Official Guide&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h4&gt;
  
  
  Concrete Example: Semantic Response Caching via Prompt Hashing
&lt;/h4&gt;

&lt;p&gt;As illustrated in Figure 12, multi-tier caching minimizes latency and duplicate costs through four sequential checks:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The script computes a unique cryptographic MD5 hash key from the input prompt string.&lt;/li&gt;
&lt;li&gt;GAS queries &lt;code&gt;CacheService&lt;/code&gt; (in-memory cache); on a cache hit, the response returns instantly in sub-milliseconds with zero API cost.&lt;/li&gt;
&lt;li&gt;On a cache miss, GAS dispatches the request to the Gemini API endpoint.&lt;/li&gt;
&lt;li&gt;The generated response is stored in &lt;code&gt;CacheService&lt;/code&gt; (up to 6 hours) and &lt;code&gt;PropertiesService&lt;/code&gt; for future requests.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Execution Instructions
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;Open &lt;strong&gt;[Project Settings] &amp;gt; [Script Properties]&lt;/strong&gt; and configure &lt;code&gt;GEMINI_API_KEY&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Paste the script below into &lt;code&gt;Code.gs&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Run &lt;strong&gt;&lt;code&gt;testCallGeminiWithCache&lt;/code&gt;&lt;/strong&gt; from the function dropdown twice consecutively.&lt;/li&gt;
&lt;li&gt;Observe in the execution log that Run 1 invokes the live Gemini API, while Run 2 hits the sub-millisecond in-memory cache instantly.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Production Script (MD5 Hashed Prompt Caching)
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
 * Test function: execute twice to observe cache hit behavior
 */&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;testCallGeminiWithCache&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;What is the single greatest advantage of Google Apps Script?&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;start1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;getTime&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;callGeminiWithCache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;elapsed1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;getTime&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;start1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;Logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`[Run 1 (API Invocation)] Latency: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;elapsed1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;ms | Response: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;res1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;start2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;getTime&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;callGeminiWithCache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;elapsed2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;getTime&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;start2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;Logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`[Run 2 (Cache Hit)] Latency: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;elapsed2&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;ms | Response: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;res2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * Executes Gemini API requests with automatic multi-tier caching.
 */&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;callGeminiWithCache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;promptText&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// 1. Generate unique MD5 hash key for prompt&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rawHash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Utilities&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;computeDigest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;Utilities&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DigestAlgorithm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MD5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;promptText&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;Utilities&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Charset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;UTF_8&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hashKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rawHash&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;256&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;padStart&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cacheKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`ai_cache_&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;hashKey&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;CacheService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getScriptCache&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ttlSeconds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;21600&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 6 hours&lt;/span&gt;

  &lt;span class="c1"&gt;// 2. Return cached response if available&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cachedResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cacheKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cachedResponse&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Cache Hit: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;cacheKey&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;cachedResponse&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// 3. Dispatch to Gemini API on cache miss&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;apiKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;PropertiesService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getScriptProperties&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;getProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GEMINI_API_KEY&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://generativelanguage.googleapis.com/v1beta/models/gemini-3.6-flash:generateContent?key=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;UrlFetchApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;post&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;contentType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;promptText&lt;/span&gt; &lt;span class="p"&gt;}]&lt;/span&gt; &lt;span class="p"&gt;}]&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="na"&gt;muteHttpExceptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;json&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getContentText&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;generatedText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;candidates&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// 4. Cache response for 6 hours (21,600 seconds)&lt;/span&gt;
  &lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cacheKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;generatedText&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ttlSeconds&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;generatedText&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Key Advantages
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Zero Duplicate Inference Cost&lt;/strong&gt;: Repetitive queries and re-evaluated formulas return instant cached results at $0 cost.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ultra-Low Latency&lt;/strong&gt;: Network round-trips (1–3 seconds) drop to in-memory lookup times (sub-milliseconds).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rate Limit Resilience&lt;/strong&gt;: Shields downstream APIs from 429 Too Many Requests errors during sudden traffic spikes.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Limitations and Operational Considerations
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cache Capacity Boundaries&lt;/strong&gt;: &lt;code&gt;CacheService&lt;/code&gt; limits individual cache entries to 100 KB. For large text corpora, store intermediate blobs in Drive or &lt;code&gt;PropertiesService&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Advanced Patterns and Extensions
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;OAuth Access Token Caching&lt;/strong&gt;: Cache SaaS bearer tokens matching their expiration window (e.g., 3,600s), avoiding redundant token exchanges.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-Turn Session Context&lt;/strong&gt;: Maintain recent conversation state in &lt;code&gt;CacheService&lt;/code&gt; across Web App interactions for fluid multi-turn dialogues.&lt;/li&gt;
&lt;/ul&gt;

&lt;h5&gt;
  
  
  Related Articles and References
&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://tanaikech.github.io/2023/05/22/report-specification-of-properties-service-for-google-apps-script/" rel="noopener noreferrer"&gt;Report: Specification of Properties Service for Google Apps Script&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Conclusion: Architectural Blueprint for Google Workspace Automation in the AI Era
&lt;/h2&gt;

&lt;p&gt;The exponential advancement of Generative AI has fundamentally reshaped the Google Workspace automation landscape. Far from signaling the demise of Google Apps Script, it establishes a crystal-clear &lt;strong&gt;Separation of Concerns: Generative AI serves as the probabilistic reasoning brain, while Google Apps Script acts as the deterministic execution hands, feet, and nervous system.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;By combining generative reasoning with deterministic execution, enterprise teams achieve software quality and governance unreachable by either tool in isolation.&lt;/p&gt;




&lt;h3&gt;
  
  
  1. Separation of Concerns Matrix: Generative AI vs. Google Apps Script
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Architecture Dimension&lt;/th&gt;
&lt;th&gt;Generative AI (Gemini / Workspace Studio / Spark)&lt;/th&gt;
&lt;th&gt;Google Apps Script (GAS)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Optimal Data Types&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Ambiguous natural language, unstructured text, media&lt;/td&gt;
&lt;td&gt;Structured schemas, JSON, tabular numbers, master records&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Execution Paradigm&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Probabilistic &amp;amp; Flexible Reasoning&lt;/strong&gt; (Context, summaries)&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Deterministic &amp;amp; 100% Reproducible Execution&lt;/strong&gt; (Math, validation)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Trigger Mechanisms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Prompt interaction, autonomous schedules, agent goals&lt;/td&gt;
&lt;td&gt;Form submissions, cell edits (&lt;code&gt;onEdit&lt;/code&gt;), cron timers, Webhooks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Security &amp;amp; Auth&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Semantic interpretation (Unsuitable for holding raw secrets)&lt;/td&gt;
&lt;td&gt;Encrypted storage (&lt;code&gt;PropertiesService&lt;/code&gt;), secure proxy dispatch&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cost &amp;amp; Latency&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Per-token pricing, inference latency (seconds)&lt;/td&gt;
&lt;td&gt;Zero-cost serverless execution, in-memory caching (milliseconds)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;UI Integration&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Chat panels, prompt dialogs&lt;/td&gt;
&lt;td&gt;Formula custom functions (spill), sidebars, modal dialogs, menus&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h3&gt;
  
  
  2. Four Practical Principles for Enterprise-Grade Automation
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Principle 1: Separate the Brain from the Nervous System and Fix Scripts for Determinism&lt;/strong&gt;
Delegate fuzzy semantic understanding, creative synthesis, and unstructured extraction to AI models. Delegate mathematical calculations, rigid business rules, state persistence, and OAuth management to GAS. For routine workflows, compile natural-language instructions into fixed GAS scripts rather than repeatedly invoking runtime LLM API reasoning, thereby guaranteeing 100% deterministic reproducibility and sub-second execution latency. Never spend tokens where a deterministic JavaScript regex or array filter can execute for free in milliseconds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Principle 2: Enforce Multi-Layered Guardrails and Sandboxing&lt;/strong&gt;
Never pipe raw LLM text directly into mission-critical systems. Enforce strict JSON Schemas (&lt;code&gt;responseSchema&lt;/code&gt;) at the model layer and validate types, boundaries, and foreign keys in GAS before committing writes. For dynamically generated script code, perform pre-execution dry-runs in sandboxed environments (&lt;code&gt;gas-fakes&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Principle 3: Gate High-Stakes Actions with Human Authorization (HITL)&lt;/strong&gt;
For operations involving external data transmission, destructive modifications, or monetary transactions, design systems that produce drafts or staging rows, requiring explicit human sign-off (via checkboxes or dialogs) before irreversible execution.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Principle 4: Optimize Throughput via Hybrid Batching and Request Packing&lt;/strong&gt;
Maximize throughput and respect platform execution windows (e.g., the GAS 6-minute ceiling) by screening standard cases with deterministic code and packing residual unstructured items into batch prompts. Couple this with multi-tier in-memory caching to minimize operational costs.&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  3. To the Engineers Pioneering the Next Generation of Workspace Automation
&lt;/h3&gt;

&lt;p&gt;Google Apps Script has matured into a fully recognized &lt;strong&gt;Google Workspace Core Service&lt;/strong&gt; &lt;a href="https://developers.google.com/apps-script/release-notes#June_22_2026" rel="noopener noreferrer"&gt;Ref&lt;/a&gt;. With Gemini embedded directly in the script editor &lt;a href="https://developers.google.com/apps-script/guides/gemini" rel="noopener noreferrer"&gt;Ref&lt;/a&gt; and professional CLI tooling (&lt;code&gt;@google/clasp&lt;/code&gt;, &lt;code&gt;ggsrun&lt;/code&gt;, &lt;code&gt;gas-fakes&lt;/code&gt;) &lt;a href="https://developers.google.com/apps-script/guides/clasp" rel="noopener noreferrer"&gt;Ref&lt;/a&gt;, the developer experience has reached unprecedented heights.&lt;/p&gt;

&lt;p&gt;In an era where AI writes code, the supreme value of the software engineer lies not in rote syntax memorization, but in &lt;strong&gt;holistic system architecture design and the elegant orchestration of probabilistic intelligence with deterministic cloud substrates.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;By harmonizing the cognitive agility of Generative AI with the rock-solid execution foundation of Google Apps Script, developers can architect the resilient, intelligent, and scalable enterprise automations of tomorrow.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>gemini</category>
      <category>googleappsscript</category>
      <category>llm</category>
    </item>
    <item>
      <title>Preventing Quota Crashes via Antigravity CLI Agent Hooks</title>
      <dc:creator>Tanaike</dc:creator>
      <pubDate>Wed, 12 Aug 2026 07:15:38 +0000</pubDate>
      <link>https://dev.to/gde/preventing-quota-crashes-via-antigravity-cli-agent-hooks-24hd</link>
      <guid>https://dev.to/gde/preventing-quota-crashes-via-antigravity-cli-agent-hooks-24hd</guid>
      <description>&lt;h2&gt;
  
  
  Solving the LLM quota monitoring paradox with zero-overhead local Connect RPC agent hooks.
&lt;/h2&gt;




&lt;h2&gt;
  
  
  Abstract
&lt;/h2&gt;

&lt;p&gt;Google Antigravity CLI users using Google OAuth face abrupt task failures when API quota hits 0%, while account switching triggers unrecoverable signature errors. Querying quota via LLM tool calls creates a paradox by consuming the very tokens being monitored. We resolve this with &lt;code&gt;antigravity-cli-check-usage-plugin&lt;/code&gt;, a CLI Agent Hook running outside the LLM execution turn. Directly querying local Connect RPC endpoints, it monitors quota with zero token overhead and injects proactive warning banners when threshold limits are reached.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Introduction
&lt;/h2&gt;

&lt;p&gt;Developers relying on &lt;strong&gt;Google Antigravity CLI&lt;/strong&gt; for autonomous pair programming frequently encounter a frustrating barrier: running out of API quota mid-session. When using Google OAuth authentication, your quota can silently hit 0%, causing task execution to halt abruptly with an unrecoverable quota error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;⚠ Individual quota reached. Please upgrade your subscription to increase your limits. Resets in 1h00m00s.
Error ID: 49a81c0f
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To bypass this roadblock, developers often attempt to log out and switch to a paid Google Cloud project billing account. However, in &lt;strong&gt;Antigravity CLI v1.1.12&lt;/strong&gt;, attempting to resume an active agent session after switching accounts triggers a critical signature mismatch failure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;⚠ Invalid thought signature.
Error ID: e2901f4c
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This error prevents the session from continuing, forcing you to wait until the quota resets. While future CLI updates may resolve this session state issue, waiting for a patch is not a viable strategy when shipping code today.&lt;/p&gt;

&lt;p&gt;The architectural divergence between standard tool-based monitoring and our agent hook model is illustrated in Figure 1. While developers can manually run the &lt;code&gt;/usage&lt;/code&gt; slash command to view quota, &lt;strong&gt;AI agents executing multi-step autonomous tasks cannot trigger &lt;code&gt;/usage&lt;/code&gt; programmatically&lt;/strong&gt;. In traditional CLI workflows, invoking quota checks via LLM tool calls requires passing context back and forth through the inference API, depleting active model tokens. Conversely, the zero-overhead agent hook interceptor executes locally prior to prompt dispatch, querying the process socket silently and injecting status alerts only when remaining quota breaches configured safety bounds.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5rot60e9sqw34vxdspd4.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5rot60e9sqw34vxdspd4.jpg" alt="Figure 1: Architectural comparison between traditional CLI agent quota limitations and the zero-overhead agent hook workflow." width="800" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this article, to overcome the limitation of agents being unable to trigger &lt;code&gt;/usage&lt;/code&gt;, we walk through the engineering journey of building &lt;strong&gt;&lt;code&gt;antigravity-cli-check-usage-plugin&lt;/code&gt;&lt;/strong&gt;. By combining local Connect RPC inspection with proactive lifecycle hooks, this plugin automatically performs external quota checks with &lt;strong&gt;Zero Quota Consumption (0 LLM tokens)&lt;/strong&gt;, completely preventing mid-session crashes.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Repository
&lt;/h2&gt;

&lt;p&gt;The plugin developed and discussed in this article is open-sourced and available on GitHub:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub Repository&lt;/strong&gt;: &lt;a href="https://github.com/tanaikech/antigravity-cli-check-usage-plugin" rel="noopener noreferrer"&gt;tanaikech/antigravity-cli-check-usage-plugin&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This repository contains the dual-runner entrypoint (&lt;code&gt;entrypoint.sh&lt;/code&gt;), Python script (&lt;code&gt;check_quota.py&lt;/code&gt;), pure Bash fallback script (&lt;code&gt;check_quota.sh&lt;/code&gt;), lifecycle hook manifest (&lt;code&gt;hooks.json&lt;/code&gt;), and default threshold configuration (&lt;code&gt;config.json&lt;/code&gt;), allowing instant one-command installation as an Antigravity CLI plugin across any developer environment.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Core Motivation
&lt;/h2&gt;

&lt;p&gt;While Antigravity CLI provides the &lt;code&gt;/usage&lt;/code&gt; slash command for developers to manually inspect quota limits, AI agents executing autonomous task loops cannot invoke &lt;code&gt;/usage&lt;/code&gt; programmatically.&lt;/p&gt;

&lt;p&gt;If we attempted to solve this by equipping the AI agent with a custom tool to query the internal RPC endpoint (&lt;code&gt;/exa.language_server_pb.LanguageServerService/GetUserStatus&lt;/code&gt;), the tool invocation and context turns would consume LLM API tokens. This creates a fundamental paradox: &lt;strong&gt;using LLM context tokens to check remaining quota consumes the very quota you are trying to preserve.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In addressing this challenge, the solution built upon our previously published article, &lt;a href="https://medium.com/google-cloud/a-developers-guide-to-agent-hooks-in-antigravity-cli-4c1440febd11" rel="noopener noreferrer"&gt;A Developer’s Guide to Agent Hooks in Antigravity CLI&lt;/a&gt;. Recalling the out-of-band execution mechanics of CLI Agent Hooks explored in that guide, we leveraged lifecycle events (&lt;code&gt;PreInvocation&lt;/code&gt; and &lt;code&gt;PostInvocation&lt;/code&gt;) to run local process checks completely outside the LLM inference turn—guaranteeing zero API token quota overhead.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Zero Token Overhead&lt;/strong&gt;: During normal operation, quota checking runs entirely outside the LLM context (via local Python/Bash scripts) without invoking LLM tool calls.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Local RPC Interception&lt;/strong&gt;: It automatically queries the CLI's internal status endpoint on &lt;code&gt;127.0.0.1&lt;/code&gt; without external network calls.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Proactive Threshold Alerting&lt;/strong&gt;: It notifies both the developer and the AI agent &lt;em&gt;before&lt;/em&gt; quota hits 0%, preventing session corruption and hard crashes.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  4. Connect RPC
&lt;/h2&gt;

&lt;p&gt;Through reverse-engineering the Antigravity CLI local process architecture (originally explored in the &lt;a href="https://github.com/skainguyen1412/antigravity-usage" rel="noopener noreferrer"&gt;antigravity-usage repository by skainguyen1412&lt;/a&gt;), we discovered that the running &lt;code&gt;agy&lt;/code&gt; process hosts a local HTTPS server using the gRPC / Connect Protocol on &lt;code&gt;127.0.0.1&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;By querying the internal endpoint &lt;code&gt;/exa.language_server_pb.LanguageServerService/GetUserStatus&lt;/code&gt;, we can retrieve real-time model quota fractions and reset timestamps directly from the local process.&lt;/p&gt;

&lt;p&gt;Because the &lt;code&gt;agy&lt;/code&gt; process may open multiple listening sockets on &lt;code&gt;127.0.0.1&lt;/code&gt; for IPC and WebSockets, a shell loop that probes each detected port until it receives a valid &lt;code&gt;userStatus&lt;/code&gt; response is required:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Scan listening sockets for the active 'agy' process on loopback (127.0.0.1)&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;PORT &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;ss &lt;span class="nt"&gt;-tulpn&lt;/span&gt; 2&amp;gt;/dev/null | &lt;span class="nb"&gt;grep &lt;/span&gt;agy | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;&lt;span class="s1"&gt;'127.0.0.1:'&lt;/span&gt; &lt;span class="s1"&gt;'{print $2}'&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print $1}'&lt;/span&gt; | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="c"&gt;# Post a Connect Protocol request to the internal GetUserStatus RPC endpoint&lt;/span&gt;
  &lt;span class="nv"&gt;RES&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-k&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://127.0.0.1:&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PORT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/exa.language_server_pb.LanguageServerService/GetUserStatus &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Connect-Protocol-Version: 1"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"metadata":{"ideName":"antigravity","extensionName":"antigravity","locale":"en"}}'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

  &lt;span class="c"&gt;# Verify if the response contains the userStatus JSON key&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$RES&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="s2"&gt;"userStatus"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$RES&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | jq &lt;span class="nb"&gt;.&lt;/span&gt;
    &lt;span class="nb"&gt;break
  &lt;/span&gt;&lt;span class="k"&gt;fi
done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To execute this logic seamlessly and rapidly inside an agent hook outside the LLM invocation turn, we implemented a Python script using standard library components, alongside a pure Bash fallback script (&lt;code&gt;check_quota.sh&lt;/code&gt;) and an entrypoint runner (&lt;code&gt;entrypoint.sh&lt;/code&gt;) that automatically selects Python when available or Bash on systems without Python installed.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;[!IMPORTANT]&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Note on Scope&lt;/strong&gt;: The &lt;code&gt;GetUserStatus&lt;/code&gt; endpoint returns the &lt;strong&gt;Five Hour Limit Remaining&lt;/strong&gt; fraction (&lt;code&gt;remainingFraction&lt;/code&gt;) and ISO reset timestamp (&lt;code&gt;resetTime&lt;/code&gt;) for active model pools. The long-term &lt;strong&gt;Weekly Limit Remaining&lt;/strong&gt; is not exposed through this RPC endpoint.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  5. Complete Agent Hook Workflow
&lt;/h2&gt;

&lt;p&gt;Building upon the lifecycle concepts detailed in &lt;a href="https://medium.com/google-cloud/a-developers-guide-to-agent-hooks-in-antigravity-cli-4c1440febd11" rel="noopener noreferrer"&gt;A Developer’s Guide to Agent Hooks in Antigravity CLI&lt;/a&gt;, the plugin integrates into the Antigravity CLI by registering &lt;code&gt;PreInvocation&lt;/code&gt; and &lt;code&gt;PostInvocation&lt;/code&gt; agent hooks in &lt;code&gt;hooks.json&lt;/code&gt;. Because &lt;code&gt;PreInvocation&lt;/code&gt; fires after the user submits input but &lt;em&gt;before&lt;/em&gt; the prompt payload is dispatched to the LLM backend, it inspects local process state and dynamically injects steps prior to model inference.&lt;/p&gt;

&lt;p&gt;As detailed in Figure 2, the final agent hook operates under two distinct execution patterns based on the configured warning threshold (default: 20%):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fikh914c63vrs98ufm9cr.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fikh914c63vrs98ufm9cr.jpg" alt="Figure 2: Complete agent hook execution workflow diagram detailing Pattern A (silent) and Pattern B (warning state)." width="800" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Pattern A: Normal Operation (Quota &amp;gt; Threshold)
&lt;/h3&gt;

&lt;p&gt;When remaining quota is above the warning threshold, the hook outputs an empty step injection payload:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"injectSteps"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Impact&lt;/strong&gt;: &lt;strong&gt;Zero Quota Consumption (0 Token Overhead)&lt;/strong&gt;. The hook executes silently in less than 50 milliseconds. No messages or extra context are injected into the LLM session, consuming absolutely zero model quota.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Pattern B: Warning State (Quota &amp;lt;= Threshold)
&lt;/h3&gt;

&lt;p&gt;When remaining quota drops to or below the threshold, the hook injects a transient system message with mandatory agent directives:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"injectSteps"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"ephemeralMessage"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"⚠️ [SYSTEM QUOTA WARNING] Model quota is below threshold (20%) (Active: gemini-3.6-flash-medium):&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt; - GEMINI Models [ACTIVE MODEL]: 20.0% remaining (Refreshes in 3h 00m)&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;[MANDATORY INSTRUCTION FOR AGENT]: The model quota has dropped below the threshold. You MUST display a prominent Quota Warning banner at the very top of your response for THIS TURN ONLY! Do NOT display a warning banner on subsequent turns unless another quota warning is explicitly injected. In the warning banner, you MUST also inform the user that they can run the '/usage' command at any time to inspect detailed quota status."&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Impact&lt;/strong&gt;: The AI agent immediately prepends a prominent Quota Warning banner to its response, advising the developer to run &lt;code&gt;/usage&lt;/code&gt; or pause heavy multi-step automation before encountering a hard crash.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  6. Installation &amp;amp; Dual Runtime
&lt;/h2&gt;

&lt;p&gt;The complete implementation is published as an open-source Antigravity CLI plugin: &lt;code&gt;antigravity-cli-check-usage-plugin&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Installation
&lt;/h3&gt;

&lt;p&gt;Install the plugin directly via the Antigravity CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;agy plugin &lt;span class="nb"&gt;install &lt;/span&gt;https://github.com/tanaikech/antigravity-cli-check-usage-plugin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Dual Runtime Architecture: Python Primary + Pure Bash Fallback
&lt;/h3&gt;

&lt;p&gt;The plugin features a multi-environment entrypoint (&lt;code&gt;entrypoint.sh&lt;/code&gt;) producing 100% identical JSON outputs across both runtimes. The engineering rationale behind this dual design includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Python (Primary Runner)&lt;/strong&gt;: Requires zero external dependencies like &lt;code&gt;jq&lt;/code&gt;, absorbs OS-specific syntax differences across Linux, macOS, and Windows, and guarantees type-safe date math.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pure Bash (Fallback Safety Net)&lt;/strong&gt;: Ensures instant execution in minimal or containerized environments where Python is not pre-installed.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Configuration and Disabling
&lt;/h3&gt;

&lt;p&gt;You can customize or completely disable the warning threshold (default: &lt;strong&gt;20.0%&lt;/strong&gt;) using environment variables, configuration files, or hook arguments.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Set Custom Threshold (e.g., 25%):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;QUOTA_THRESHOLD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;25.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Disable Quota Check Completely:&lt;/strong&gt;&lt;br&gt;
Setting &lt;code&gt;QUOTA_THRESHOLD&lt;/code&gt; to &lt;code&gt;-1&lt;/code&gt; instructs the hook to skip all RPC queries immediately:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;QUOTA_THRESHOLD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nt"&gt;-1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  7. Real-World Testing &amp;amp; Verification
&lt;/h2&gt;

&lt;p&gt;After installing the plugin, setting &lt;code&gt;export QUOTA_THRESHOLD=80.0&lt;/code&gt; and executing a live session test in Antigravity CLI v1.1.12 demonstrates the hook in action, as captured in Figure 3:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fywasyvlq3u48ootk7lnt.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fywasyvlq3u48ootk7lnt.jpg" alt="Figure 3: Live terminal demonstration of real-time Quota Warning banner injection in Antigravity CLI 1.1.12." width="799" height="236"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When the user enters a simple greeting (&lt;code&gt;hello&lt;/code&gt;), the agent hook instantly detects that the active model's remaining quota (71.0%) has dropped below the configured threshold (80.0%). A prominent yellow &lt;strong&gt;Warning banner&lt;/strong&gt; (&lt;code&gt;Quota Warning: GEMINI Models quota is at 71.0% remaining...&lt;/code&gt;) is dynamically prepended at the top of the AI's response, alerting the developer and providing a reminder to inspect detailed limits via &lt;code&gt;/usage&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  8. Updating &amp;amp; Uninstalling
&lt;/h2&gt;

&lt;p&gt;To update the plugin to the latest version or remove it from your environment:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Check installed plugins&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  agy plugin list
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Uninstall the plugin&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  agy plugin uninstall antigravity-cli-check-usage-plugin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reinstall the updated version&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  agy plugin &lt;span class="nb"&gt;install &lt;/span&gt;https://github.com/tanaikech/antigravity-cli-check-usage-plugin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;In this article, we presented a zero-overhead solution to eliminate mid-session quota crashes and account-switching signature errors in Google Antigravity CLI. Drawing upon foundational concepts from &lt;a href="https://medium.com/google-cloud/a-developers-guide-to-agent-hooks-in-antigravity-cli-4c1440febd11" rel="noopener noreferrer"&gt;A Developer’s Guide to Agent Hooks in Antigravity CLI&lt;/a&gt; and resolving the paradox where using LLM tool calls to query internal RPC endpoints consumes quota, we built native CLI Agent Hooks (&lt;code&gt;PreInvocation&lt;/code&gt; / &lt;code&gt;PostInvocation&lt;/code&gt;) running completely outside the LLM execution turn. Featuring a dual Python primary and pure Bash fallback architecture, the hook probes internal local Connect RPC endpoints with absolute zero token consumption during normal operation. By proactively injecting warning banners and &lt;code&gt;/usage&lt;/code&gt; reminders when quota drops below threshold, it guarantees universal environment compatibility and eliminates task interruptions cleanly at the root.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>gemini</category>
      <category>antigravity</category>
      <category>devops</category>
    </item>
    <item>
      <title>Before the Quake: How Antigravity CLI's AI Agents &amp; IoT Data Predict Earthquakes</title>
      <dc:creator>Tanaike</dc:creator>
      <pubDate>Fri, 07 Aug 2026 02:55:21 +0000</pubDate>
      <link>https://dev.to/gde/before-the-quake-how-antigravity-clis-ai-agents-iot-data-predict-earthquakes-34if</link>
      <guid>https://dev.to/gde/before-the-quake-how-antigravity-clis-ai-agents-iot-data-predict-earthquakes-34if</guid>
      <description>&lt;p&gt;&lt;strong&gt;Published Paper&lt;/strong&gt;: &lt;a href="https://essopenarchive.org/doi/full/10.22541/essoar.15007109/v2" rel="noopener noreferrer"&gt;Unification Theory of Lithosphere-Atmosphere-Ionosphere Coupling via Acoustic-Gravity Waves (LAIC-AGW) and Quantitative Pre- and Post-Seismic Anomaly Verification Using Ultra-Dense IoT Weather Sensor Networks (ESS Open Archive)&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Abstract
&lt;/h3&gt;

&lt;p&gt;We announce the publication of our updated manuscript on ESS Open Archive, establishing the Unified LAIC-AGW Theory using ultra-dense IoT weather data. Executed on &lt;strong&gt;Antigravity CLI&lt;/strong&gt; with &lt;strong&gt;Gemini 3.6 Flash&lt;/strong&gt; using the autonomous R&amp;amp;D framework &lt;strong&gt;&lt;code&gt;tanaike-lab&lt;/code&gt;&lt;/strong&gt;, this project integrates 71,107 authentic Netatmo observation records with seismic moment tensors ($M_{ij}$). By capturing pre-seismic thermodynamic enthalpy anomalies ($\delta \theta_e = 5.96\text{--}15.47\,\text{K}$) and acoustic-gravity waves through a deterministic signal processing pipeline, Haversine focal attenuation, and a Sigmoidal Bayesian probability model ($P_{\text{eq}} = 0.0\%\text{--}99.8\%$), this framework delivers &lt;strong&gt;2 to 6 hours of advance lead time&lt;/strong&gt; with &lt;strong&gt;100% false alert elimination&lt;/strong&gt; in non-epicentral regions. This milestone demonstrates how human researchers and autonomous AI multi-agent matrices co-create rigorous, empirical scientific breakthroughs.&lt;/p&gt;




&lt;h3&gt;
  
  
  1. Introduction
&lt;/h3&gt;

&lt;p&gt;Today, on August 13, 2026, our updated geophysics research paper titled &lt;a href="https://essopenarchive.org/doi/abs/10.22541/essoar.15007109/v1" rel="noopener noreferrer"&gt;&lt;em&gt;Unification Theory of Lithosphere-Atmosphere-Ionosphere Coupling via Acoustic-Gravity Waves (LAIC-AGW) and Quantitative Pre- and Post-Seismic Anomaly Verification Using Ultra-Dense IoT Weather Sensor Networks&lt;/em&gt;&lt;/a&gt; has been officially updated and released on the international open-access preprint server &lt;strong&gt;ESS Open Archive&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Conventional earthquake early warning systems (such as P-wave alerts) operate reactively by detecting seismic waves &lt;em&gt;after&lt;/em&gt; fault rupture has already occurred, offering at most a few seconds to tens of seconds of warning time. In contrast, this study leverages continuous 20-minute weather big data collected across thousands of crowdsourced IoT weather stations (Netatmo) nationwide in Japan via the Netatmo API (totaling &lt;strong&gt;71,107 authentic observation records&lt;/strong&gt;). By coupling these atmospheric observations directly with seismic moment tensors ($M_{ij}$) via partial differential equations, we quantitatively demonstrate a &lt;strong&gt;"Proactive Earthquake Early Warning System"&lt;/strong&gt; providing &lt;strong&gt;2 to 6 hours of advance lead time&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The execution of this complex project—including partial differential equation solver integration, big data signal extraction, Popperian falsifiability verification, and manuscript preparation—was driven by the AI Co-Researcher framework &lt;strong&gt;&lt;code&gt;tanaike-lab&lt;/code&gt;&lt;/strong&gt; operating on &lt;strong&gt;Antigravity CLI&lt;/strong&gt; powered by &lt;strong&gt;Gemini 3.6 Flash&lt;/strong&gt; (for background on &lt;code&gt;tanaike-lab&lt;/code&gt;, see our technical articles on &lt;a href="https://medium.com/@tanaike/inside-the-virtual-r-d-lab-how-human-imagination-and-ai-multi-agents-shape-the-future-of-science-d4e27a33b1af" rel="noopener noreferrer"&gt;Medium&lt;/a&gt; and &lt;a href="https://dev.to/gde/inside-the-virtual-rd-lab-how-human-imagination-and-ai-multi-agents-shape-the-future-of-science-3c6j"&gt;DEV.to&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;【Academic Note】&lt;/strong&gt;: It should be emphasized that the physical models, data interpretations, inferences, and precursor warning conclusions presented in this paper represent &lt;strong&gt;one of many academic perspectives and methodologies&lt;/strong&gt; regarding earthquake precursors and lithosphere-atmosphere coupling within the broad geophysics community. Given the inherent complexity of fault dynamics, these findings contribute a novel framework to the ongoing scientific discourse, inviting further empirical validation and community dialogue.&lt;/p&gt;

&lt;p&gt;In this article, written specifically for AI practitioners, software architects, and technology leaders, we explore how human-AI collaboration on Antigravity CLI turned ultra-dense IoT sensor data into a proactive planetary defense platform.&lt;/p&gt;




&lt;h3&gt;
  
  
  2. Proactive vs. Reactive Warning: Turning Cities into Sensitive Skin
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5hyoh69cbkez1uyhndpu.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5hyoh69cbkez1uyhndpu.jpg" alt="Unified LAIC-AGW Early Warning Framework" width="800" height="447"&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;Figure 1: Executive infographic of the LAIC-AGW Unified Theory illustrating lithospheric seismic displacement, pre- and post-seismic acoustic-gravity wave (AGW) atmospheric excitation, Bolton (1980) exact equivalent potential temperature ($\theta_{e, \text{Bolton}}$) calculations, Haversine spherical distance mechanics, regional baseline extraction, and station-level focal attenuation decay.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Imagine living in a city where early warning alerts don't chime &lt;em&gt;after&lt;/em&gt; the ground starts violently shaking, but hours &lt;em&gt;before&lt;/em&gt;. &lt;/p&gt;

&lt;p&gt;Existing early warning infrastructure relies on P-wave detectors placed near fault lines. When an earthquake strikes, P-waves travel at roughly $6\,\text{km/s}$, granting cities tens of kilometers away a brief 5 to 30 second window before destructive S-waves arrive. However, near the epicenter—where damage is most severe—people reside inside an inevitable "blind zone" where shaking hits before any alert can arrive.&lt;/p&gt;

&lt;p&gt;Our &lt;strong&gt;Unified LAIC-AGW Theory&lt;/strong&gt; shifts the paradigm from reactive seconds to &lt;strong&gt;proactive hours&lt;/strong&gt;. Instead of waiting for subterranean rocks to snap, our system treats thousands of ultra-dense crowdsourced IoT weather stations (Netatmo) placed across metropolitan areas like a sensitive array of skin sensors. Hours prior to fault rupture, subtle physical anomalies—micro-barometric strain and thermal energy releases—are injected into the atmospheric boundary layer. Capturing these pre-seismic signatures unlocks a &lt;strong&gt;2 to 6 hour advance lead time window&lt;/strong&gt;, allowing smart cities to automatically decelerate high-speed trains, halt semiconductor lithography tools, isolate chemical pipelines, and safely evacuate citizens.&lt;/p&gt;




&lt;h3&gt;
  
  
  3. Accessible Atmospheric Physics: "Cold Sweat" and "The Drum Skin Analogy"
&lt;/h3&gt;

&lt;p&gt;How does a solid Earth fault rupture manifest in surface weather sensors hours before shaking? The underlying physics can be understood through intuitive real-world analogies:&lt;/p&gt;

&lt;h4&gt;
  
  
  1. Pre-Seismic "Geophysical Cold Sweat" and Micro-Barometric "Creaking"
&lt;/h4&gt;

&lt;p&gt;Hours before a major earthquake, tectonic stress accumulation along a fault plane creates microscopic fractures within crustal rock. This micro-fracturing releases radioactive radon gas, which ionizes air molecules in the boundary layer. These ionized air molecules act as condensation nuclei, causing ambient water vapor to condense and release latent heat energy into the atmosphere.&lt;/p&gt;

&lt;p&gt;To detect this subtle thermal release, our system continuously calculates &lt;strong&gt;Equivalent Potential Temperature ($\theta_e$)&lt;/strong&gt;—representing total atmospheric enthalpy (sensible plus latent heat)—from basic weather parameters: Barometric Pressure ($P$), Air Temperature ($T$), and Relative Humidity ($RH$). Hours prior to rupture, a prominent &lt;strong&gt;pre-seismic thermal spike ($\delta \theta_e = 5.96\text{--}15.47\,\text{K}$)&lt;/strong&gt; emerges directly above the impending epicenter, acting like a &lt;strong&gt;"geophysical cold sweat"&lt;/strong&gt; on the Earth's surface.&lt;/p&gt;

&lt;p&gt;Simultaneously, localized crustal compression drives short-period residual pressure perturbations in the boundary layer, creating atmospheric &lt;strong&gt;"creaking sounds"&lt;/strong&gt; prior to major shaking.&lt;/p&gt;

&lt;h4&gt;
  
  
  2. Atmospheric Wave Responses: "The Drum Skin Analogy"
&lt;/h4&gt;

&lt;p&gt;The atmospheric excitation signature varies dramatically based on fault dislocation geometry, which can be visualized using a simple &lt;strong&gt;"drum skin"&lt;/strong&gt; analogy:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Normal Faulting (Pulling the drum skin downward)&lt;/strong&gt;: Seafloor subsidence ($M_{zz} &amp;lt; 0$) excites sustained, long-period acoustic-gravity waves ($\Delta P_{\text{post}} = 255.60\,\text{hPa}$) that propagate outward in harmony with ocean tsunamis (2016 Fukushima M7.4).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Strike-Slip Faulting (Rubbing the drum surface horizontally)&lt;/strong&gt;: Although vertical displacement is minimal, intense shear friction generates powerful pre-seismic latent heat spikes ($\delta \theta_e = 14.36\,\text{K}$) directly above the epicenter, followed by directional post-rupture wave propagation (2018 Osaka M6.1, 2026 Kumamoto M7.1).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reverse Faulting (Unclamping the drum skin)&lt;/strong&gt;: Rapid atmospheric pressure drops ($102.10\,\text{hPa/h}$) caused by passing typhoons reduce fault normal stress, unclamping fault friction and dynamically triggering rupture via positive Coulomb stress shifts (2018 Hokkaido M6.7 during Typhoon Jebi).&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  4. Data Processing Pipeline &amp;amp; False Alarm Suppression
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftcayj46b6qj1qeel9a8x.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftcayj46b6qj1qeel9a8x.jpg" alt="Unified LAIC-AGW Mathematical and Physical Derivation Pipeline" width="800" height="447"&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;Figure 2: Unified LAIC-AGW Mathematical and Physical Derivation Pipeline illustrating the 7-step progression from raw weather measurements ($P, T, RH$) to exact thermodynamic equivalent potential temperature $\theta_{e, \text{Bolton}}$, regional baseline residual extraction, Haversine focal weighting $W_{\text{focal}}$, API station density factor $\Phi_{\text{density}}$, Sigmoidal Bayesian probability derivation $P_{\text{eq}}^{\text{real}}$, post-seismic 3D AGW wave excitation, and thermal relaxation decay.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;To transform raw, noisy crowdsourced IoT weather data into high-confidence alerts without triggering false alarms, our pipeline executes a 7-step signal processing flow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Thermodynamic Conversion&lt;/strong&gt;: Continuous streaming weather parameters $(P, T, RH)$ are converted via Bolton's (1980) exact formula into Equivalent Potential Temperature $\theta_{e, \text{Bolton}}$.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Regional Baseline Subtraction&lt;/strong&gt;: Spatially averaging regional temperature trends ($\bar{\theta}_{e, \text{regional}}$) filters out synoptic meteorological fronts (e.g., passing cold fronts or typhoons).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Haversine Focal Decay&lt;/strong&gt;: Spherical Haversine distance ($d_i$) to candidate epicenters is calculated, applying exponential attenuation $W_{\text{focal}, i} = \exp(-d_i / 250\,\text{km})$ to isolate localized anomalies.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;API Station Density Completeness Factor&lt;/strong&gt;: A density scaling factor $\Phi_{\text{density}}(N) = 1 - \exp(-N / 30)$ automatically scales down probabilities in sparse station zones to prevent over-confidence.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sigmoidal Bayesian Forecasting Model&lt;/strong&gt;: Log-odds metrics $L(\mathbf{X})$ compute exact prediction probabilities $P_{\text{eq}}^{\text{real}}(\%)$ ($0.0\%\text{--}99.8\%$).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Folp06rn7s1ij3o1sncfe.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Folp06rn7s1ij3o1sncfe.jpg" alt="Spatial Relationship Between Epicenter Location and Data Acquisition Bounding Box Area" width="800" height="447"&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;Figure 3: Executive Infographic explaining the Spatial Relationship Between Epicenter Location and Sensor Array Area. Panel A shows Intra-Area Containment where the epicenter is located inside the sensor array, yielding high prediction probability $P_{\text{eq}} &amp;gt; 90\%$. Panel B shows Off-Epicentral Distance Scaling where the epicenter is situated outside the sensor array, causing exponential signal attenuation and scaling $P_{\text{eq}} &amp;lt; 5\%$.&lt;/em&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Why Distant Cities Don't Suffer False Panic Alerts
&lt;/h4&gt;

&lt;p&gt;A critical challenge in disaster prediction is eliminating false positives. As illustrated in Figure 3:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Panel A (Intra-Area Containment)&lt;/strong&gt;: When an epicenter lies directly inside the monitored sensor array ($d_{\text{min}} \to 0$), prediction probability peaks at maximum confidence (&lt;strong&gt;$P_{\text{eq}} = 89.4\%\text{--}99.8\%$&lt;/strong&gt;, reaching &lt;strong&gt;$92.7\%\text{--}99.8\%$&lt;/strong&gt; in dense urban arrays).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Panel B (Off-Epicentral Scaling)&lt;/strong&gt;: When the epicenter lies outside the sensor array ($d_{\text{min}} &amp;gt; 150\,\text{km}$), exponential distance attenuation $\exp(-d_{\text{min}} / 250\,\text{km})$ smoothly scales prediction probabilities down to background levels (&lt;strong&gt;$P_{\text{eq}} \le 4.8\%$&lt;/strong&gt;), mathematically guaranteeing &lt;strong&gt;100% false alarm elimination in non-epicentral cities&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  5. Empirical Results Across 71,107 Authentic Netatmo Records
&lt;/h3&gt;

&lt;p&gt;Our study validated the LAIC-AGW Theory using &lt;strong&gt;71,107 authentic observation records&lt;/strong&gt; across four major Japanese earthquakes and one severe weather control event:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Earthquake Event&lt;/th&gt;
&lt;th&gt;Evaluated Area&lt;/th&gt;
&lt;th&gt;Pre-Seismic Enthalpy $\delta \theta_e$&lt;/th&gt;
&lt;th&gt;Prediction Probability $P_{\text{eq}}$ (%)&lt;/th&gt;
&lt;th&gt;Alert Level&lt;/th&gt;
&lt;th&gt;Post-Seismic AGW Wave $\Delta P_{\text{post}}$&lt;/th&gt;
&lt;th&gt;Spatial Isolation Performance&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;① 2016 Fukushima M7.4&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Tokyo / Kanto [Target]&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;15.47 K&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;99.8%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;CRITICAL&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;255.60 hPa&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Epicenter Hit ($P = 99.8\%$)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Osaka / Kansai&lt;/td&gt;
&lt;td&gt;4.19 K&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;4.2%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;LOW&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;12.40 hPa&lt;/td&gt;
&lt;td&gt;False Positive Eliminated ($P = 4.2\%$)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;② 2018 Osaka M6.1&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Osaka / Kansai [Target]&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;6.79 K&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;92.7%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;HIGH&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;37.10 hPa&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Epicenter Hit ($P = 92.7\%$)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Tokyo / Kanto&lt;/td&gt;
&lt;td&gt;1.77 K&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;2.4%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;LOW&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;4.20 hPa&lt;/td&gt;
&lt;td&gt;False Positive Eliminated ($P = 2.4\%$)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;③ 2018 Hokkaido M6.7&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Hokkaido Area [Target]&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;5.96 K&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;89.4%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;HIGH&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;256.10 hPa&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Sparse Network Scaled ($P = 89.4\%$)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Tokyo / Kanto&lt;/td&gt;
&lt;td&gt;0.35 K&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0.5%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;LOW&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;8.40 hPa&lt;/td&gt;
&lt;td&gt;False Positive Eliminated ($P = 0.5\%$)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;④ 2026 Kumamoto M7.1&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Fukuoka / Kyushu [Target]&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;14.36 K&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;99.6%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;CRITICAL&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;257.20 hPa&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Epicenter Hit ($P = 99.6\%$)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Tokyo / Kanto&lt;/td&gt;
&lt;td&gt;1.73 K&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;2.3%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;LOW&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;9.10 hPa&lt;/td&gt;
&lt;td&gt;False Positive Eliminated ($P = 2.3\%$)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;⑤ 2018 Typhoon 18 (Control)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;All Regions (Kanto / Kansai / Kyushu)&lt;/td&gt;
&lt;td&gt;0.85 K&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1.2%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;LOW&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;18.40 hPa&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0.0% False Positive Rate ($P = 1.2\%$)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6i5i4adjnv1wr7f4pah7.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6i5i4adjnv1wr7f4pah7.jpg" alt="Empirical Performance Comparison Chart Across 4 Major Earthquakes and 1 Control Event" width="800" height="447"&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;Figure 4: Empirical Performance Comparison Chart Across 4 Major Earthquakes and 1 Control Event. Displays side-by-side comparative bar charts comparing Old Model v3.2 and Updated Model v7.0 across Sensitivity (%), False Positive Count (100% elimination), Overall Accuracy (%), and Signal-to-Noise Ratio (SNR).&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;As shown in Figure 4, our updated model achieved major empirical breakthroughs across 71,107 records:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Sensitivity&lt;/strong&gt;: Boosted from $64\%\text{--}73\%$ to &lt;strong&gt;$77\%\text{--}89\%$&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Overall Accuracy&lt;/strong&gt;: Improved from $74\%\text{--}86\%$ to &lt;strong&gt;$94\%\text{--}99\%$&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Signal-to-Noise Ratio (SNR)&lt;/strong&gt;: Jumped from $&amp;lt;8\,\text{dB}$ to **$&amp;gt;21\,\text{dB}$** (an 8-fold signal boost).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;False Positives&lt;/strong&gt;: Reduced from 8–18 false alerts down to &lt;strong&gt;0 false positives (100% false alarm elimination)&lt;/strong&gt; across all non-target cities and control weather scenarios.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  6. Actionable Infrastructure Mitigation
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpuw5v9les3d175knh8ie.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpuw5v9les3d175knh8ie.jpg" alt="Smart City and Critical Infrastructure Early Mitigation Network" width="800" height="447"&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;Figure 5: Application 1: Smart City and Critical Infrastructure Early Mitigation Network illustrating automated bullet train pre-deceleration, semiconductor EUV lithography stepper suspension, toxic gas valve shutoff, and smart elevator emergency parking triggered 2 to 6 hours before rupture.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A 2 to 6 hour pre-seismic lead-time window transforms municipal disaster management:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Shinkansen Bullet Trains&lt;/strong&gt;: High-speed trains automatically reduce speeds from $300\,\text{km/h}$ to crawling speeds prior to shaking, preventing catastrophic derailments.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Semiconductor EUV Fabrication&lt;/strong&gt;: EUV lithography steppers park in safe rest modes, protecting multi-billion-dollar optical mirror alignments.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Toxic Industrial Gas Lines&lt;/strong&gt;: Automated shut-off valves close chemical pipelines, preventing toxic leaks and urban fires.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Smart Elevators&lt;/strong&gt;: High-rise elevators descend to the nearest floor and park open-doored, eliminating passenger entrapment.&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  7. Inside the AI Co-Researcher Framework (&lt;code&gt;tanaike-lab&lt;/code&gt;) on Antigravity CLI
&lt;/h3&gt;

&lt;p&gt;Executing complex partial differential equations, ingesting 71,107 IoT records, and running multi-axis peer reviews was accomplished using &lt;strong&gt;Antigravity CLI&lt;/strong&gt; powered by &lt;strong&gt;Gemini 3.6 Flash&lt;/strong&gt; and the auxiliary R&amp;amp;D framework &lt;strong&gt;&lt;code&gt;tanaike-lab&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwn53p5xp96qaib48csca.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwn53p5xp96qaib48csca.jpg" alt="tanaike-lab Autonomous R&amp;amp;D Workflow on Antigravity CLI" width="800" height="447"&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;Figure 6: Autonomous R&amp;amp;D workflow of &lt;code&gt;tanaike-lab&lt;/code&gt; running on Antigravity CLI powered by Gemini 3.6 Flash.&lt;/em&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  1. The Human-AI Collaborative Co-Creation Lifecycle &amp;amp; Specialized Subagents Matrix
&lt;/h4&gt;

&lt;p&gt;As shown in Figure 6, &lt;code&gt;tanaike-lab&lt;/code&gt; operates through a tightly coupled human-AI loop driven by a matrix of specialized subagents:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Human Strategic Vision&lt;/strong&gt;: The human Principal Investigator (PI) sets the research hypothesis: coupling Netatmo IoT weather archives with seismic moment tensors ($M_{ij}$) to build a unified LAIC-AGW theory.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI Plan Audit &amp;amp; Mathematical Physics Derivation&lt;/strong&gt;: Specialized subagents (&lt;code&gt;plan_audit_dryrun_agent&lt;/code&gt; and &lt;code&gt;theoretical_refinement_physicist&lt;/code&gt;) inspect the plan, adding cubic spline interpolation, Morlet wavelets, spatial array beamforming (+18 dB SNR boost), Dobrovolsky strain radii ($R_{\text{prep}} = 10^{0.43M}$), Haversine focal attenuation ($W_{\text{focal}}$), and station density completeness ($\Phi_{\text{density}}$).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automated Code Development &amp;amp; Popperian Self-Healing&lt;/strong&gt;: The code execution agent (&lt;code&gt;experiment_code_developer&lt;/code&gt;) writes Python data processing scripts inside an isolated sandbox, injecting automated assertion hooks (&lt;code&gt;assert&lt;/code&gt;) to repair debug loops autonomously upon encountering runtime errors.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Big Data Analysis &amp;amp; Agent-to-Agent (A2A) Peer Reviews&lt;/strong&gt;: Specialized subagents (&lt;code&gt;experimental_results_auditor&lt;/code&gt;, &lt;code&gt;blind_calibration_auditor&lt;/code&gt;, &lt;code&gt;theory_discussion_reviewer&lt;/code&gt;) run A2A peer discussions to verify pre-seismic enthalpy spikes ($\delta \theta_e = 15.47\,\text{K}$), execute zero-hindsight blind evaluations, and confirm Typhoon 18 control noise suppression (0.0% false alert rate).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Manuscript Peer Review &amp;amp; Article Refinement&lt;/strong&gt;: A 5-axis simulated peer review panel and &lt;code&gt;article_editorial_architect&lt;/code&gt; audit citation integrity (100% 1-to-1 matching), LaTeX compilation, and developer-focused article readability before final PI review.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  2. Real-Time Self-Crystallization &amp;amp; CLI Auto-Reinstallation Engine
&lt;/h4&gt;

&lt;p&gt;Unlike static agent scripts, &lt;code&gt;tanaike-lab&lt;/code&gt; features an active &lt;strong&gt;Real-Time Self-Crystallization &amp;amp; CLI Auto-Reinstallation Engine&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Real-Time Directive Capture&lt;/strong&gt;: Human steering directives and verified execution lessons are captured on the fly during project runs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SKILL.md Auto-Crystallization&lt;/strong&gt;: Lessons are written immediately into &lt;code&gt;SKILL.md&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CLI Plugin Auto-Reinstallation&lt;/strong&gt;: An automated export engine (&lt;code&gt;scripts/auto_crystallize_and_export.py&lt;/code&gt;) exports and reinstalls the updated skill matrix directly into the local Antigravity CLI plugin directory on the fly, performing instant Git remote sync to keep the framework continuously evolving.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  3. Academic and Systemic Positioning &amp;amp; Triple-Domain Architecture
&lt;/h4&gt;

&lt;p&gt;Within modern technology R&amp;amp;D, &lt;code&gt;tanaike-lab&lt;/code&gt; is positioned as a &lt;strong&gt;"Human-Centric Dynamic Virtual R&amp;amp;D Laboratory OS"&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Positioning Against "AI Slop" vs. Human-AI Synergy&lt;/strong&gt;: Unsupervised AI generation frequently suffers from hallucinations and lack of domain rigor ("AI slop"). &lt;code&gt;tanaike-lab&lt;/code&gt; enforces a &lt;strong&gt;Human-AI Synergy Model&lt;/strong&gt;, where the human PI retains strategic direction while AI subagent matrices accelerate logical formulation, code execution, empirical auditing, and multi-axis peer reviews.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Positioning as a Cognitive Friction Eliminator&lt;/strong&gt;: Traditional scientific workflows consume massive cognitive bandwidth on operational friction—debugging scripts, interpolating non-uniform temporal grids, adjusting graphics for color universal design, and fixing LaTeX compilation errors. &lt;code&gt;tanaike-lab&lt;/code&gt; acts as a &lt;strong&gt;cognitive accelerator&lt;/strong&gt;, eliminating operational friction so human researchers can focus on high-level strategic reasoning.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Triple-Domain Architecture&lt;/strong&gt;: &lt;code&gt;tanaike-lab&lt;/code&gt; operates across three interconnected domain pillars:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Academic &amp;amp; Natural Sciences&lt;/strong&gt;: Geophysical modeling, computational fluid dynamics (CFD), quantum chemistry, and material science.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Generative AI &amp;amp; LLM Engineering&lt;/strong&gt;: Prompt architecture, RAG vector retrieval pipelines, Multi-Agent orchestration, token budget compression, and LLM benchmarks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Google Ecosystem&lt;/strong&gt;: Google Apps Script (GAS) libraries, Google Workspace automation (Drive, Sheets, Docs, Gmail, Forms), and Google Cloud/Workspace APIs with quota limit management.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  8. Summary
&lt;/h3&gt;

&lt;p&gt;The release of our updated manuscript &lt;a href="https://essopenarchive.org/doi/abs/10.22541/essoar.15007109/v1" rel="noopener noreferrer"&gt;&lt;em&gt;Unification Theory of Lithosphere-Atmosphere-Ionosphere Coupling via Acoustic-Gravity Waves (LAIC-AGW)...&lt;/em&gt;&lt;/a&gt; on ESS Open Archive marks a major leap in earthquake science. By proving that pre-seismic enthalpy anomalies ($\delta \theta_e = 5.96\text{--}15.47\,\text{K}$) can be captured hours prior to shaking using crowdsourced IoT weather networks with 100% false alarm elimination ($P_{\text{eq}} \le 4.8\%$), this research shifts earthquake warning from reactive seconds to proactive hours.&lt;/p&gt;

&lt;p&gt;It should be recognized that the analytical results, physical interpretations, and conclusions presented in this study represent &lt;strong&gt;one of many diverse scientific perspectives and theoretical approaches&lt;/strong&gt; within the evolving domain of earthquake physics. Continuous empirical validation and open community dialogue remain essential to building upon these findings.&lt;/p&gt;

&lt;p&gt;Beyond geophysics, the real-time self-evolving Human-AI synergy framework embodied by &lt;code&gt;tanaike-lab&lt;/code&gt; on Antigravity CLI provides a scalable blueprint for AI engineers and researchers across climate adaptation, LLM multi-agent engineering, and enterprise Google API automation. We invite the global geophysics, meteorology, generative AI, and smart-city engineering communities to read the full open-access paper on ESS Open Archive.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>gemini</category>
      <category>antigravity</category>
      <category>datascience</category>
    </item>
  </channel>
</rss>
