●CHAT — Ask Gemini in Chat goes live today, August 26, turning Google Chat into a single command line for search, drafting, catching up, and task management●LIMITS — Ask Gemini in Chat comes with promotional higher limits through October 1, after which standard usage limits apply●SHEETS — Sheets canvas turns a spreadsheet into an interactive, read-write application from a plain-language prompt●MEET — You can now start a Gemini note-taking session straight from the Google Meet home screen, including for in-person meetings, with the summary, action items, and full transcript saved to a Google Doc●MODELS — Gemini 3.7 Flash reached general availability on August 13, with introductory pricing running through December 31, 2026●DEPRECATION — gemini-robotics-er-1.6-preview shuts down on August 31, five days from now. The ER 2 endpoints have been in public preview since July 30●CHAT — Ask Gemini in Chat goes live today, August 26, turning Google Chat into a single command line for search, drafting, catching up, and task management●LIMITS — Ask Gemini in Chat comes with promotional higher limits through October 1, after which standard usage limits apply●SHEETS — Sheets canvas turns a spreadsheet into an interactive, read-write application from a plain-language prompt●MEET — You can now start a Gemini note-taking session straight from the Google Meet home screen, including for in-person meetings, with the summary, action items, and full transcript saved to a Google Doc●MODELS — Gemini 3.7 Flash reached general availability on August 13, with introductory pricing running through December 31, 2026●DEPRECATION — gemini-robotics-er-1.6-preview shuts down on August 31, five days from now. The ER 2 endpoints have been in public preview since July 30
Sheets canvas Takes the Entry Point, Not the Execution Boundary
With Sheets canvas available, how much of your hand-built Apps Script can you actually retire? Sorting the answer by execution boundary, with the decision rules and a classifier script that does the inventory for you.
As an indie developer I have been pulling store-related work into spreadsheets for a while now. Localized descriptions, review submission history, staged rollout progress. A table is the fastest container for all of it. What actually took time, though, was never building the table.
The friction always sat somewhere else: whose permissions it runs under, what starts it, and how many times it is allowed to run.
A sheet only you open needs one function. But once you want it to fire on a schedule, and the external API you call has a daily cap, and a mid-run failure must not double-process anything, the spreadsheet stops being a sufficient container. Most of the logic I pushed into Apps Script went there not because I wanted a table, but because there was nowhere else to write those execution conditions down.
On August 26, Sheets canvas gained the ability to turn a natural-language prompt into an interactive, readable and writable application. Handing out a spreadsheet as a small business app with an input form is a familiar need, and Gemini now reaches into it directly.
My first question was whether I could retire the Apps Script I had written. The short answer: you can retire the entry point, and only the entry point. The line is drawn by execution boundary, not by feature count.
What August 26 Actually Added
What canvas gives you is a way to convert sheet contents into an interactive read-write surface. Creation and editing are subject to a per-user usage limit.
The easy thing to miss is that the new capability covers how you build, not how the thing runs.
Dimension
Sheets canvas
Apps Script
Build effort
Assembled from a prompt
You write the code
What starts it
A person opening it
Triggers and external calls too
State across runs
Not assumed
Held in PropertiesService and friends
Permission surface
The sheet's own context
Whatever the manifest declares
Volume constraints
Per-user usage limit
Per-script quotas
Everything below the first row is what I have started calling the execution boundary. canvas shortens the first row only.
The Execution Boundary as a Yardstick
The execution boundary is the part of a piece of logic that fixes four things: whose permissions it runs under, what triggers it, how many times it may run, and what happens when it dies partway through. It is my own shorthand, but lining up those four separates movable work from immovable work cleanly.
A function that reads numbers from a sheet, formats them, and writes them back to another column has almost no execution boundary. It runs when someone presses a button, a failure just means pressing again, and it consumes no shared external quota. That kind of work can move to canvas.
Compare that with a job that wakes at 1 a.m., walks unprocessed rows, posts each to an external endpoint, and resumes from where it stopped last time. That is thick with boundary: a time-driven trigger owns the start, the daily UrlFetch quota is shared, and the resume cursor survives across executions. None of those three have anywhere to live inside an interactive app.
The automations that gave me trouble in the past were always on that side of the line. Burning the daily UrlFetch quota before month-end is covered in budgeting against the UrlFetch daily quota, and time-driven triggers quietly running out is covered in consolidating triggers into one dispatcher. Neither has anything to do with how the table is built.
✦
Thank you for reading this far.
Continue Reading
What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.
WHAT YOU'LL LEARN
✦You can sort your existing Apps Script assets into move, split, and keep using a single yardstick instead of case-by-case guesswork
✦You can catch the permission and trigger surprises before migrating, rather than after someone else opens the sheet
✦You can hand the migration decision to a classifier script and keep the result, so the same question never costs you a second afternoon
Secure payment via Stripe · Cancel anytime
✦
Unlock This Article
Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.
Before drawing the line, list how many scripts run under which trigger. You will think you remember. On a project that has been running for a few years, you almost certainly do not.
/** * Lists the triggers registered on this project. * The goal is to surface one part of the execution boundary: what starts the work. * The permission surface is not retrievable from the API, so cross-check * oauthScopes in appsscript.json by hand. */function auditTriggers() { const triggers = ScriptApp.getProjectTriggers(); if (triggers.length === 0) { Logger.log('No triggers: this project is manual-start only'); return; } triggers.forEach(function (t) { const handler = t.getHandlerFunction(); const source = String(t.getTriggerSource()); // CLOCK / SPREADSHEETS / FORMS const event = String(t.getEventType()); // CLOCK / ON_EDIT / ON_OPEN ... const userDriven = (event === 'ON_EDIT' || event === 'ON_OPEN'); Logger.log( [handler, source, event, userDriven ? 'user-started' : 'not-user-started'].join(' | ') ); }); Logger.log('Registered: ' + triggers.length + ' (limits vary by trigger type)');}
getTriggerSource() and getEventType() return enums rather than plain strings, which is why both are wrapped in String() before comparison. Skip that and every comparison silently evaluates false, so the whole project reports as user-started. I trusted exactly that output once and over-estimated how much I could migrate.
Every row that comes back as not-user-started is work that will not move to canvas. That alone narrows the candidate list considerably.
Move, Split, Keep
An inventory settles into one of three buckets.
Verdict
Condition
What to do
Move
No boundary signals; finishes with sheet reads and writes alone
Move it into canvas and delete the script
Split
Entry point is a sheet action, but the body calls out or holds state
Entry in canvas, body stays in Apps Script, wired together
Keep
Something other than a person starts it, or it needs mutual exclusion
Leave it alone
The surprise was that split was the largest bucket. Migration conversations tend to be framed as move-or-keep, but real code usually has the entry point and the body living in the same function. Framed as a binary, you end up keeping things whose entry point could have moved.
So when something lands in split, break the function into entry and body first, then judge migratability. Deciding that order up front means you only make the call once.
Letting a Script Decide
A handful of scripts is fine to eyeball. Past ten or so, you start missing things. Here is a small classifier that reads the source text and picks out boundary signals.
// classify.js — detects execution boundary signals in Apps Script sourceconst BOUNDARY_SIGNALS = [ { key: 'time_trigger', re: /ScriptApp\.newTrigger|getProjectTriggers/, why: 'something other than a person starts it' }, { key: 'external_call', re: /UrlFetchApp\./, why: 'shares the daily UrlFetch quota' }, { key: 'exclusive_lock', re: /LockService\./, why: 'needs execution serialized to one' }, { key: 'durable_state', re: /PropertiesService\.|CacheService\./, why: 'carries state across executions' }, { key: 'wider_scope', re: /DriveApp\.|GmailApp\.|CalendarApp\./, why: 'asks for permissions beyond the sheet' }, { key: 'long_running', re: /Utilities\.sleep|while\s*\(true\)/, why: 'brushes the six-minute execution ceiling' },];// Entry signal: work that finishes with sheet reads, writes and formattingconst ENTRY_ONLY = /SpreadsheetApp\.|getRange\(|setValues\(|getValues\(/;function classifyScript(name, source) { const hits = BOUNDARY_SIGNALS.filter(function (s) { return s.re.test(source); }); const hasEntry = ENTRY_ONLY.test(source); let verdict; if (hits.length === 0 && hasEntry) verdict = 'move'; // entry point and all else if (hits.length > 0 && hasEntry) verdict = 'split'; // separate entry from body else verdict = 'keep'; // stays in Apps Script return { name: name, verdict: verdict, reasons: hits.map(function (h) { return h.key + ': ' + h.why; }), };}module.exports = { classifyScript };
Running three of my own files through it:
FormatReport.gs -> moveTranslateRows.gs -> split external_call: shares the daily UrlFetch quota durable_state: carries state across executionsNightlyDispatcher.gs -> keep time_trigger: something other than a person starts it external_call: shares the daily UrlFetch quota exclusive_lock: needs execution serialized to one
TranslateRows.gs coming back as split is the entire reason the classifier exists. Eyeballing it, I had filed it under keep because it calls an external endpoint. But the part that reads the sheet and writes results back can move. Seeing exactly two reasons listed made it obvious that pushing those two concerns into a separate function frees the entry point.
Do not throw the verdict away. Writing verdict and reasons back to a sheet as CSV means the next person asking the same question, including future you, does not redo the reasoning.
The Seam You Keep
Anything classified as split needs a decision about where entry and body connect. Reaching for the sheet edit event here is tempting and it will hurt later.
/** * Accepts input from the canvas side and only enqueues the heavy work. * The actual external calls happen in drain(), run by a time-driven trigger. * Why the body does not live in onEdit is explained below. */function enqueueFromSheet() { const sheet = SpreadsheetApp.getActive().getSheetByName('queue'); const rows = sheet.getRange(2, 1, Math.max(sheet.getLastRow() - 1, 0), 2).getValues(); const pending = rows .filter(function (r) { return r[0] && !r[1]; }) // unprocessed only .map(function (r) { return String(r[0]); }); if (pending.length === 0) return 'Nothing to enqueue'; const props = PropertiesService.getScriptProperties(); const queue = JSON.parse(props.getProperty('queue') || '[]'); props.setProperty('queue', JSON.stringify(queue.concat(pending))); return 'Accepted ' + pending.length + ' item(s)';}
The reason the body does not belong in onEdit is not execution time. onEdit runs under the permissions of whoever edited the sheet, so the moment a collaborator types something, the external call is attempted as them. Logic that worked fine on your own copy fails on theirs. canvas makes entry points easier to hand out, which makes this path easier to step on.
Where the acting identity sits is the same problem discussed in keeping Apps Script automations on least privilege. canvas added a way to distribute entry points; it did not change how permissions work.
Three Things to Check Before You Move
The failures worth preventing show up after migration, not during the decision.
1. Did the acting identity change?
Logic you ran alone becomes logic other people run once it is an interactive entry point. Their account may not reach the destination sheet or folder. Open it once from a second account before handing it out.
2. Did the quota you are spending change?
Creating and editing in canvas draws on a per-user usage limit. That is counted separately from per-script quotas, so headroom in one is not evidence of headroom in the other.
3. Does anything break the sheet's shape?
Adding or reordering columns interactively is genuinely useful, and it silently breaks every script holding a fixed column reference. A range like getRange('C2:C500') is affected whether or not that script is part of the migration. Resolve columns by header name before you move anything.
That third point bites hardest on position-dependent processing of the kind described in chunking and idempotency under the six-minute limit. If the entry point is going to be interactive, switch to header-name lookup first.
Where to Start
Run auditTriggers() once and count the not-user-started rows. That count is the floor on how much stays in Apps Script.
When a new entry point appears, the first decision worth making is not what to move but what you are declaring immovable. Once the immovable set is fixed, the rest sorts itself out.
Share
Thank You for Reading
Gemini Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.