firebase / firebase/extensions

Kits parity issues

Open
#2,974 20 comments 0 reactions 3 assignees Claimed by @IzaakGough View on GitHub
kits
Dominant language
TypeScript
Stars
979
Forks
433
Avg merge
1d 13h
Merged PRs (30d)
125

Description

Tracking board: https://github.com/orgs/firebase/projects/38/views/1

Below are comments for each extension to kit migration. Each comment displays the differences between the extension and the corresponding kit.

## Valid differences

Reviewed against `kits` at 9cc453c5 on 2026-09-15.
- Some changes are due to migrations from gen1 functions to gen2. These specific changes have been excluded from the comments below.
- Extension selects that stored `yes` / `no` keep `defineString` with those values and the extension's `=== "yes"` coercion (#3145 for `firestore-bigquery-export`, #3148 for `delete-user-data` and `firestore-genai-chatbot`); an extension `.env` is reusable as-is. Selects that stored `true` / `false` keep `defineBoolean`, which parses them like the extension's `=== "true"`, with the extension's option labels restored via `select` (#3148). Exception: `WILDCARD_IDS` in `firestore-bigquery-export` is a bare `defineBoolean` without the extension's No/Yes labels (`kits/firestore-bigquery-export/src/config.ts:394-400`); #3148 did not touch that kit.
- Extension `required: true` on params that carry a default (`ENABLE_AUTO_DISCOVERY`, `ENABLE_DISCUSSION_OPTION_OVERRIDES`, `ENABLE_GENKIT_MONITORING`) is not reproduced as a blocking prompt; kit params with a default are never prompted as required. Platform difference, no value change.
- `storage-resize-images` `MAKE_PUBLIC` is `defineString` with `"true"` / `"false"` values rather than `defineBoolean`: firebase-tools' select prompt ignores a non-string `default` and preselects the first option (firebase/firebase-tools#11053), which left `Yes` highlighted where the extension preselected `No`. Stored values unchanged (#3148).
- Kits grant IAM through `requiresRole` per role (for example `kits/speech-to-text/src/index.ts:57-59`), so the extension platform's per-instance service account and its implicit grants are replaced by explicit role declarations. Roles the platform granted implicitly are now declared: `roles/eventarc.publisher` in `speech-to-text`, `roles/eventarc.eventReceiver` and `roles/run.invoker` in `rtdb-limit-child-nodes`. No extension ever declared a service-account param, so nothing was removed.
- The extension's function location (a declared `LOCATION` param in the GCP-hosted extensions, the platform system param in this repo's extensions) is not a kit param; the Firebase CLI places functions. `firebase ext:export` writes it to `.env` as `FUNCTION_DEFAULT_REGION` (`src/extensions/export.ts:208-209`, `:221-222`), which no kit reads; the CLI's `firebase init functions` kit templates do read it into `setGlobalOptions({ region })` (`templates/init/functions/typescript/index-kit.ts:14`, `index-kit-migration.ts:20`). Exceptions: `firestore-incremental-capture` keeps `LOCATION` and pins all five of its functions to it; `firestore-bundle-builder` hardcodes `us-central1` as the extension did.
- Separate from that, some kits keep a *resource*-location param (`DATABASE_REGION`, `BUCKET_REGION`) saying where the triggered database or bucket lives, which is not the same setting. It is there only where the CLI cannot place the function itself. Shipped: `firestore-send-email` and `firestore-bigquery-export`, whose triggers pass `database` as a param expression, so the CLI's region lookup sees the literal `{{ params.DATABASE }}` and falls back to `us-central1` (firebase/firebase-tools#11020, #3101, #3102). Proposed in #3173: `storage-resize-images`, `speech-to-text` and `rtdb-limit-child-nodes` for the same reason, and `firestore-counter` and `firestore-vector-search` because their scheduled, task and callable functions get no inference at all and otherwise split away from their trigger. Kits whose trigger the CLI can resolve (`firestore-translate-text`, `firestore-genai-chatbot`) have no such param. For `firestore-send-email` and `firestore-bigquery-export` the extension yaml already declared `DATABASE_REGION`, so the kit keeps an existing param rather than adding one. #3173 is still open, so the proposed kits have no such param yet.
- Some changes may be due to dependency upgrades, or more recent Node versions.
- Extensions had no number type, so numeric params were `type: string` with a regex (e.g. `validation: ^[0-9]`). Some kit params moved to `defineInt` and keep the extension regex for range checks: `MAX_DISPATCHES_PER_SECOND`, `MAX_ENQUEUE_ATTEMPTS`, `TTL_EXPIRE_VALUE`, `DEFAULT_QUERY_LIMIT`, `MAX_COUNT`, `AUTO_DISCOVERY_SEARCH_DEPTH`, `OAUTH_PORT`, `FUNCTION_MEMORY`. Others stay `defineString` with the regex: `SCHEDULE_FREQUENCY`, `TOP_K`, `CANDIDATE_COUNT`, `MAX_OUTPUT_TOKENS`, `REFRESH_INTERVAL_MINUTES`. `IntParam.value()` returns `0` for an unset variable and ignores the declared default, so kits re-read `process.env` to apply defaults (`kits/firestore-bigquery-export/src/config.ts:657-660`, `kits/delete-user-data/src/config.ts:173-178`).
- `required:` defaults to `true` when omitted in `extension.yaml`, and several extensions omit it on params their own code and descriptions treat as optional (`BACKUP_COLLECTION`, `DEFAULT_REPLY_TO`, `USERS_COLLECTION`, `TEMPLATES_COLLECTION`). Kits match the code behaviour (optional, empty → `undefined`), not the yaml.
- `defineSecret` cannot be optional - `SecretParamOptions` only takes `label`/`description`, so any bound secret must exist at deploy. Extension params declared `type: secret, required: false` (`API_KEY`, `GOOGLE_AI_API_KEY`, `GEMINI_API_KEY`, `OPENAI_API_KEY`, and the send-email SMTP/OAuth secrets) therefore become mandatory-at-deploy in kits. Platform constraint, not a kit choice. `firestore-send-email` limits the impact by binding only the secrets for the chosen `AUTH_TYPE` (`secretParamsForAuthType`, `kits/firestore-send-email/src/config.ts:313-332`).
- Where an extension's yaml default disagreed with its own code fallback (`firestore-genai-chatbot` / `firestore-vector-search` `COLLECTION_NAME`, `delete-user-data` `AUTO_DISCOVERY_SEARCH_FIELDS`), kits standardise on the yaml default.
- No `firebase-functions/params` equivalent of the `${DATABASE_INSTANCE}` system param. In the extension, the built-in `FIREBASE_CONFIG.databaseURL` resolves without prompting. This is something kit has to handle itself. Only applicable in `rtdb-limit-child-nodes` extension/kit.
- `IMG_BUCKET` / `EXTENSION_BUCKET` use the params `BUCKET_PICKER` (a `ResourceInput`), which cannot also carry a `validationRegex` or `example`, so those two params keep the picker instead of the extensions' regex and example. The picker constrains input to real buckets, which is stronger, and this will stay as is.
- Function resource properties (`timeout`, `memory`, `maxInstances`) are declared per function in kit code, not left to firebase-tools. Spot checks against each extension's `resources[].properties` match (`firestore-counter`, `firestore-vector-search`, `bigquery-firestore-export`, `firestore-genai-chatbot`, `firestore-incremental-capture`, `storage-resize-images`); no full sweep has been done. Known addition: `speech-to-text` sets a 540s timeout (`kits/speech-to-text/src/export-config.ts:115`) where the extension declared only `availableMemoryMb: 1024`.
- Kits have no deploy-time status surface. Extensions reported progress and failures through `getExtensions().runtime().setProcessingState(...)`; nothing equivalent exists for kits, so deploy-time outcomes are only visible in function logs. Affects `bigquery-firestore-export` (10 call sites), `firestore-bigquery-export` (lifecycle hooks), `firestore-translate-text` (backfill), `firestore-vector-search` (backfill gating) and `firestore-incremental-capture` (setup and backup-init tasks).
- `billingRequired: true` has no kit equivalent, so kits cannot declare the billing requirement that every extension except `speech-to-text` declared in its yaml.
- `rtdb-limit-child-nodes` renamed `NODE_PATH` to `RTDB_NODE_PATH` and will keep it: Node.js reserves `NODE_PATH` for module resolution, so the original name is overwritten at runtime.
- Event payload shapes match the extensions: `{ change, context }` for `firestore-counter` and `firestore-translate-text`, and the raw `{ error }` for `speech-to-text`. The gen2 shapes were ruled not a valid difference in issue #3027 and restored in #3098 (merged 2026-09-11).
- `firestore-vector-search` declares four event types in its `extension.yaml` but never publishes them. The kit publishes none either: the kit-only events were removed in #3094 for parity.
- Kits read `FIREBASE_KIT_INSTANCE_ID` (firebase-tools >= 15.27.0) and name their resources `kit--...` where the extension used `ext--...`. `bigquery-firestore-export` adds a `PUB_SUB_TOPIC` param so a migrated instance can keep the extension's topic.
- Some kits add params the extension never had, to name resources the platform used to name for it: `DISCOVERY_TOPIC_NAME` and `DELETION_TOPIC_NAME` in `delete-user-data`, and the four `*_QUEUE_NAME` params in `firestore-vector-search`.
- Optional params whose extension regex rejected the empty string gain an empty branch (`|^$`) so an unset value passes: `CLUSTERING`, `KMS_KEY_NAME`, `REFRESH_INTERVAL_MINUTES` (`firestore-bigquery-export`), `SELECTED_DATABASE_INSTANCE` (`delete-user-data`), `COLLECTION_PATH` (`speech-to-text`), `OUTPUT_OPTIONS`, `SHARP_OPTIONS`, `PLACEHOLDER_IMAGE_PATH` (`storage-resize-images`), and the positive-int params in `firestore-genai-chatbot`.
- `extension.yaml` used to have a `reason` for roles and APIs. Now with kits, only `requiresAPI` accepts a reason param. `requiresRole` doesn't accept a `reason`.

## Remaining parity work

### A. PR open, ready for review

- [ ] `rtdb-limit-child-nodes`: README documents `RTDB_NODE_PATH` and `MAX_COUNT` defaults that c4735460 removed for parity - issue #3194
- **What:** README says the kit falls back to `messages` and `MAX_COUNT` defaults to 100; neither default exists, the CLI prompts for both and the runtime rejects an empty path or non-positive count.
- **Decision:** docs only, code already matches the extension.
- **Status:** https://github.com/firebase/extensions/pull/3196 open, needs review.
- [x] `firestore-bigquery-export`: `WILDCARD_IDS` is a bare `defineBoolean`, the extension's No/Yes select labels are gone - issue #3195
- **What:** #3148 restored labels on the other seven `true`/`false` selects but skipped this kit. Stored values unchanged, prompt only.
- **Decision:** parity, add `input: select({ No: false, Yes: true })`.
- **Status:** https://github.com/firebase/extensions/pull/3197 open, needs review.
### B. PR open, in progress

### C. Decision needed

### D. Fix needed, decision made

- [ ] Cross-kit: write the Notes entries for accepted divergences — issue #3035
- **What:** one PR documenting the differences we decided to keep: no deploy-time status surface, no `billingRequired`, the `NODE_PATH` rename, the `ext-` to `kit-` topic rename, startup validation moved to CLI deploy time, plus anything the decision issues ruled as accepted (see Valid differences above and the DECISION lines under Completed).
- **Decision:** made, this is docs only.
- **Status:** not started, no PR yet

## Completed

### Correctness / production-firing

- [x] Cross-kit: no kit that publishes events declares `roles/eventarc.publisher` (`firestore-counter`, `firestore-translate-text`, `firestore-bigquery-export`, `speech-to-text`); with `EVENTARC_CHANNEL` set the awaited publish 403s and the function fails before doing any work - issue #3125 (closed) - https://github.com/firebase/extensions/pull/3098 (merged 2026-09-11; 8 publishing kits, not 4; not deploy-verified) @CorieW
- [x] `bigquery-firestore-export`: soft failures → hard failures — cases the extension reported `PROCESSING_COMPLETE` now throw under `retry: true` → retry storms (§7a–7c, §8) - https://github.com/firebase/extensions/pull/2983 — #2983 @IzaakGough look into this, what *should* the kit behaviour be — https://github.com/firebase/extensions/pull/3086 (merged) stops retrying the two terminal failures; config-validation residual tracked in issue #3008
- [x] `firestore-send-email`: SendGrid + `AUTH_TYPE=OAuth2` sends fail — secret gating forces `apiKey: undefined`, `setApiKey` never called — issue #3009 (closed) — https://github.com/firebase/extensions/pull/3089 (merged 2026-09-07)
- [x] `storage-resize-images`: partial-env crashes — issue #3138 (closed) - https://github.com/firebase/extensions/pull/3002 (merged 2026-09-08)
- [x] `storage-resize-images`: deletion risk — empty `imageTypes` → zero outputs counts as success → original deleted under `on_success` (§6) - https://github.com/firebase/extensions/pull/3037 (merged)
- [x] `firestore-counter`: `deep-equal` called without `{ strict: true }` in the worker (§1) - https://github.com/firebase/extensions/pull/3072 (merged)
- [x] `storage-resize-images`: content-filter region reads `FUNCTION_REGION` and throws when absent; the extension fell back to `us-central1` (§5) - https://github.com/firebase/extensions/pull/3090 (merged)
- [x] `firestore-vector-search`: `queryOnWrite` `prefilters` cast unvalidated — the extension zod-parses them (§7b) - https://github.com/firebase/extensions/pull/3065 (merged)
- [x] `firestore-vector-search`: `queryOnWrite` self-retrigger loop (§7) - https://github.com/firebase/extensions/pull/3038 (merged; stored-request guard, self-heals the stale-overwrite race — supersedes https://github.com/firebase/extensions/pull/3095, closed 2026-09-08 as superseded)
- [x] `bigquery-firestore-export`: Pub/Sub topic renamed `ext-` → `kit-`, update path rewrote `notification_pubsub_topic` (§4) - https://github.com/firebase/extensions/pull/3088 (merged; topic configurable via `PUB_SUB_TOPIC`, set it to the old `ext-` topic to preserve notifications across migration)
- [x] `firestore-bigquery-export`: `DATABASE_REGION` used as function region — `eur3`/`nam5`/`nam7` aren't Cloud Run regions → deploy hard-fails (§2) - https://github.com/firebase/extensions/pull/3066 (merged, live-verified on a nam5 database; the same conflation is still live in firestore-send-email: #3069, closed 2026-09-08 by https://github.com/firebase/extensions/pull/3102, see the firestore-send-email entry below. NOTE: #3066's param removal partially reverted by https://github.com/firebase/extensions/pull/3101, which restores DATABASE_REGION as a placement param with the dual-region mapping, per the firebase-tools team's minimal-divergence decision and upstream bug firebase/firebase-tools#11020)
- [x] Cross-kit: `PROJECT_ID` resolution — the extension reads `process.env.PROJECT_ID` (`functions/src/config.ts:166`), injected by the Extensions runtime, and defaults `BIGQUERY_PROJECT_ID` to the `${PROJECT_ID}` system param; kits use `firebase-functions/params` `projectID`, which resolves from `FIREBASE_CONFIG.projectId`. Surfaced by #3120 (and the older #2778). Only the shared change tracker was affected: its update-view path dropped `bqProjectId` and fell back to `process.env.PROJECT_ID` - fixed in https://github.com/firebase/extensions/pull/3137 (merged, tracker 2.2.1, supersedes #3121). The kit picks it up via `@firebaseextensions/firestore-bigquery-change-tracker@^2.2.1` (#3127, shipped in 0.0.2-rc.7) and the fix was verified live on dev-extensions-testing. Tracker follow-ups: #3143 (project override clobbers ADC), #3144 (shared view schema mutated on update)
- [x] `storage-resize-images`: `CONTENT_FILTER_LEVEL` "Off" option stored the string `False` instead of `OFF`, so every resize with the filter off threw at runtime — issue #3047 (closed) — https://github.com/firebase/extensions/pull/3064 (merged 2026-09-08; follow-ups #3123 label, #3124 stale `IMAGE_TYPE` validation)
- [x] `firestore-send-email`: `DATABASE_REGION` used as function region — same conflation as firestore-bigquery-export #3066/#3101, `eur3`/`nam5`/`nam7` selectable so deploy hard-fails on a multi-region database — issue #3069 — https://github.com/firebase/extensions/pull/3102 (merged 2026-09-08)

### Migration / consumer breaks

- [x] `firestore-vector-search`: status field shape change — `status..state` consumers break (§6) - issue #3139 (decision; #3092 leaves the shape out of scope) — https://github.com/firebase/extensions/pull/3180 (open)
- [x] `firestore-bigquery-export`: legacy back-compat event types dropped — the extension published both `firebase.extensions.firestore-counter.v1.*` (legacy) and `…firestore-bigquery-export.v1.*`; the kit publishes only the new ones — issue #3108 (closed) — https://github.com/firebase/extensions/pull/3111 (merged 2026-09-11) @cabljac @CorieW
- [x] `speech-to-text`: transcoded `.wav` lands in a different Storage location (§1, including the `.txt` path's retained `replace("tmp/", …)`) - `.txt` remnant fixed in https://github.com/firebase/extensions/pull/3073 (merged 2026-09-02); the `.wav` location - issue #3140 (closed) - **DECISION: parity, reproduce the extension's `tmp/`-prefixed paths (double slash included)** - https://github.com/firebase/extensions/pull/3157 (merged 2026-09-10) @cabljac
- [x] Cross-kit: event payload shapes — `{change, context}` → `{data, params}` (`firestore-counter` §5, `firestore-translate-text`), `speech-to-text` error `{}` → `{message, stack}` — decision recorded in issue #3027: NOT intentional gen2 design, fix — https://github.com/firebase/extensions/pull/3098 (merged 2026-09-11; #3027 still open, close once deploy-verified) @CorieW
- [x] Cross-kit: `yes`/`no` `.env` break for `delete-user-data` `ENABLE_AUTO_DISCOVERY` and `firestore-genai-chatbot` `ENABLE_DISCUSSION_OPTION_OVERRIDES` / `ENABLE_GENKIT_MONITORING` (same defect as #3126): now `defineString` + `=== "yes"`, so an extension `.env` reads correctly. The same PR restores the extension's option labels on the 7 `true`/`false` selects (`OAUTH_SECURE`, `DO_BACKFILL`, `UPDATE_ON_CONFIGURE`, `ENABLE_AUTOMATIC_PUNCTUATION`, `MAKE_PUBLIC`, `IS_ANIMATED`, `REGENERATE_TOKEN`) and fixes the `MAKE_PUBLIC` prompt preselecting `Yes` (firebase/firebase-tools#11053) — https://github.com/firebase/extensions/pull/3148 (merged 2026-09-08)
- [x] `firestore-vector-search`: OpenAI model changed — ada-002 → 3-small@512, incompatible with existing indexes (§2) — decision recorded in issue #3029 (revert to the extension's defaults) — https://github.com/firebase/extensions/pull/3096 (merged)
- [x] `firestore-vector-search`: gemini/vertex embedding dimensions changed — extension hardcoded `outputDimensionality: 768` with no truncation; kit passes `config.dimension` and truncates the result (§5) — https://github.com/firebase/extensions/pull/3096 (merged)
- [x] `firestore-vector-search`: OpenAI index dimension does not match the vectors written, the index is declared 512 while ada-002 writes 1536, so `findNearest` cannot use it, **DECISION: not a parity item, the extension declares 512 against the same 1536-dim vectors (`functions/src/config.ts` `getDimension()`), so the kit reproduces it exactly after #3096 restored the model**. Refiled as an inherited bug against both codebases: issue #3166, which carries the delete-the-old-index migration constraint (`createIndex` never compares dimension, `src/queries/setup.ts:49-56`) and the extension's `_/index` re-backfill gating. Issue #3105 closed not planned, https://github.com/firebase/extensions/pull/3163 closed unmerged as out of parity

### Events gaps

- [x] `firestore-translate-text`: early-returns on `!event.data` without emitting start/completion events; the extension always emitted both — issue #3020 @cabljac - https://github.com/firebase/extensions/pull/3149 (merged 2026-09-08)
- [x] `firestore-bigquery-export`: `onSuccess` never published, and the kit README claims it is — doc bug, small standalone fix - https://github.com/firebase/extensions/pull/3071 (merged; doc-only, no code change needed)
- [x] `firestore-vector-search`: events published only from `handleEmbedOnWrite` — nothing from the query/backfill/update/init paths — https://github.com/firebase/extensions/pull/3094 (merged) resolves issue #3016 by REMOVING the kit-only events instead (the extension never published any; parity) @CorieW

### Behavior divergence (fix or decide)

- [x] `firestore-vector-search`: `multimodal` embedding provider is non-functional in both codebases (kit throws not implemented, extension targets an invalid region) — **deferred to kits-follow-ups: no parity gap, and removing the value is a breaking config change** — issue #3135 (open, kits-follow-ups)
- [x] `bigquery-firestore-export`: clearing `PARTITIONING_FIELD` is blocked by the kit's guard, but the DTS API allows it — **DECISION: parity, guard stays; fix deferred to kits-follow-ups, to land on kit and extension together after testing the fixed-table and non-`us` cases** — issue #2985 (open, kits-follow-ups)
- [x] `firestore-translate-text`: `null` input routing — **DECISION: parity**, guard dropped so `null` fails from `Object.entries(null)` like the extension — issue #3142 (closed) — https://github.com/firebase/extensions/pull/3183 (merged 2026-09-11). Inherited gap (`null` neither clears nor skips, both codebases) split out as #3182 (kits-follow-ups)
- [x] `firestore-genai-chatbot`: startup fail-fast removed (`validateRequiredEnvVars()`) — **DECISION: deferred to kits-follow-ups, out of scope for kits-stable** — issue #3022 (open) @cabljac
- [x] `firestore-bigquery-export`: `DATASET_LOCATION` is `immutable: true` in the extension but the kit accepts a new value on any redeploy and silently ignores it (`firebase-functions/params` has no immutability) — decide — issue #3152 - DECISION: just document this clearly — https://github.com/firebase/extensions/pull/3181 (open)
- [x] Cross-kit: AI-provider location fallback — `firestore-genai-chatbot` `VERTEX_AI_MODEL_LOCATION=null` no longer means "function region" (§3); `firestore-translate-text` vertex region from `FUNCTION_REGION`/genkit default (§2); `firestore-vector-search` same class (§5) — knock-on of the `LOCATION` removal, one decision — issue #3028 (closed 2026-09-11, fixed by https://github.com/firebase/extensions/pull/3162, merged 2026-09-11: Vertex AI is called in the function's own region) - as an example fbe had DATABASE_REGION (or similar) that is removed in the kit now, on kits branch.
- **Related:** [PR #2943](https://github.com/firebase/extensions/pull/2943) changes chatbot and translation to use the `global` Vertex AI endpoint. Partial coverage: vector search is untouched, and #3028’s explicit-region decision is not fully implemented. Closed unmerged 2026-09-11, superseded by #3162.
- [x] `firestore-vector-search`: re-embed-on-update flipped (the extension never re-embeds terminal docs) + ERROR-retry flipped (§6a/6b) — issue #3011 (closed) — **DECISION**: parity where possible with extension — https://github.com/firebase/extensions/pull/3092 (merged 2026-09-11) @CorieW
- [x] `bigquery-firestore-export`: `DISPLAY_NAME` edits silently no-op — never added to the DTS update mask — issue #3141 (closed) - https://github.com/firebase/extensions/pull/2980 (merged 2026-09-09)
- [x] `bigquery-firestore-export`: BigQuery job/billing project now `config.projectId` — the extension used the ADC default (§10) — **DECISION: no change, no Notes entry** — on a deployed function `projectID` (`FIREBASE_CONFIG.projectId`) and ADC (`GCLOUD_PROJECT`) resolve to the same project, and the client project is only used for the job, never a resource reference (the single `createQueryJob` query is fully qualified). Diverges only for local runs or a foreign `GOOGLE_APPLICATION_CREDENTIALS` key. Same idiom as `delete-user-data` Pub/Sub and the `firestore-incremental-capture` extension @IzaakGough
- [x] `firestore-bigquery-export`: no `onConfigure` lifecycle equivalent (§3a) — **DECISION: investigate** — investigated: no gap, `afterRedeploy` covers it. firebase-tools hashes each endpoint as source+env+secrets, so an `.env`-only change marks the endpoint for update, which counts as a resource modification and fires `afterRedeploy` (`release/lifecycle.js` skips the hook only when nothing changed). The extension wired `onUpdate` and `onConfigure` to the same handler, and the kit wires `afterRedeploy` to the same idempotent `setupBigQuerySync` reconcile task, so behaviour is equivalent
- [x] `firestore-genai-chatbot`: generation options now reach the model — **DECISION: forward** — README note in https://github.com/firebase/extensions/pull/3087 (merged)
- [x] `firestore-translate-text`: new `TranslationService` per invocation — fresh client per event, performance (§5f) - https://github.com/firebase/extensions/pull/3075 (merged)
- [x] `firestore-vector-search`: vector-store error codes collapsed to `unknown` — the extension mapped 15 Firestore codes to `HttpsError` (§8) — **DECISION: fix, parity** — issue #3015 — https://github.com/firebase/extensions/pull/3093 (merged)
- [x] `firestore-send-email`: `TESTING` env path gone — the kit cannot enter testing mode via env — issue #3107 — https://github.com/firebase/extensions/pull/3110 (merged) @CorieW
- [x] `firestore-translate-text`: non-string input coerced — `42` → `"42"` reaches the API (§4b) — minor, decide — **DECISION: fix, aim for parity** — issue #3106 — https://github.com/firebase/extensions/pull/3109 (merged) @CorieW
- [x] `bigquery-firestore-export`: a `TIME` column is stored as the raw string BigQuery returned. The extension passed it to `Timestamp.fromDate(new Date("10:30:00"))`, which throws and loses the whole run (no rows, no run document, no `latest`), so no installed instance can have stored a `TIME` value. Issue #3068. **DECISION: store the string, extension unchanged**: https://github.com/firebase/extensions/pull/3146 (merged 2026-09-08)
- [x] `firestore-bigquery-export`: `EXCLUDE_OLD_DATA` / `USE_NEW_SNAPSHOT_QUERY_SYNTAX` - extension accepted `yes`/`no`, kit `defineBoolean` accepts `true` only, so a migrated `.env` with `yes` silently reads as false - issue #3126 - **DECISION: accept `yes`/`no` alongside booleans** - https://github.com/firebase/extensions/pull/3145 (merged 2026-09-08, shipped in 0.0.2-rc.7)
- [x] `firestore-vector-search`: multimodal unimplemented yet still an offered `EMBEDDING_PROVIDER` value (§3) — **DECISION: leave the value, removing it is a breaking config change and the extension has the same gap** — https://github.com/firebase/extensions/pull/3074 closed unmerged 2026-09-07, issue #3014 closed; the non-functional provider itself is tracked in #3135 (kits-stable) @CorieW
- [x] `bigquery-firestore-export`: `TRANSFER_CONFIG_NAME` let the kit adopt an existing scheduled query; the extension has the same code but no param, so it never ran - **DECISION: parity, remove the param and the branch** - issue #3165 - https://github.com/firebase/extensions/pull/3186 (merged 2026-09-15). Related: #2970 (defect in the branch) closed not planned, #2960 (its fix) closed unmerged; re-adding adoption later tracked in #3185 (kits-follow-ups)
- [x] `storage-resize-images`: `deleteOriginal` default - with it omitted, the extension deleted the original after a successful resize (`onSuccess`), the kit kept it (`never`) - **DECISION: `deleteOriginal` back to `onSuccess` for parity; `isAnimated` stays `true`, the extension's `false` was a shipped bug, README explains** - issue #3024 - https://github.com/firebase/extensions/pull/3100 (merged 2026-09-14) @cabljac

### Feature-scale (design first)

- [x] `firestore-counter`: no client libraries (Android/iOS/Dart/node/web), shard format undocumented — closed as not planned (#3033): migration only; the existing extension clients keep working against the unchanged shard format
- [x] `firestore-translate-text`: backfill removed, along with the "only fill missing languages" semantics — **DECISION: no fix — the extension's backfill never deploys (resource and `DO_BACKFILL` commented out in its yaml), so no-backfill is parity** — issue #3032
- [x] `firestore-bigquery-export`: user-facing tooling absent — import, gen-schema-view, cross-project scripts, guides — **DECISION: reference existing extension scripts/tooling** — issue #3034
- [x] `firestore-bigquery-export`: write-path buffering — the extension buffers in Cloud Tasks; the kit rethrows and can hot-loop (§1) — issue #3031 — **DECISION: reinstate the Cloud Tasks buffer (option D, migration safety)** — https://github.com/firebase/extensions/pull/3103 closed 2026-09-07 in favour of the stack (bottom to top): #3127 (firebase-admin ^14.2.0), #3128 (enqueue module), #3129 (queue params), #3130 (the behaviour change), #3131 (docs), all merged into kits 2026-09-08 @cabljac
- [x] `firestore-vector-search`: restore batched backfill - the extension backfills in chunks of 50 documents per task, embeds in batches, and skips the pass when the metadata doc shows nothing changed; the kit enqueued one task per document, one embedding call per document, loaded the whole collection into memory, and re-embedded everything on every redeploy - **DECISION: fix, aim for parity** - issue #3012 - https://github.com/firebase/extensions/pull/3097 (merged 2026-09-15; deliberate deviations from the extension's backfill are listed in the kit README) @CorieW

### Notes candidates (won't fix — differences to justify in Notes instead)

- [x] `rtdb-limit-child-nodes`: `NODE_PATH` rename (Node.js reserves the name)
- [x] No deploy-time status surface in kits (no `setProcessingState` equivalent) — `firestore-bigquery-export` §3d, `firestore-translate-text`, `firestore-vector-search` §9d
- [x] `billingRequired: true` has no kit equivalent (`firestore-translate-text`, `rtdb-limit-child-nodes`)
- [x] `firestore-send-email` `OAUTH_SECURE` and `speech-to-text` `ENABLE_AUTOMATIC_PUNCTUATION` advertise a default of `true`, but an unset variable reads `false` in both the extension (`=== "true"`) and the kit (`BooleanParam`). Inherited, reproduced for parity, pinned by tests in #3148

### Minor / cosmetic (batchable)

- [x] `firestore-send-email`: `SMTP_CONNECTION_URI` validation regex is unanchored — inherited, kit and extension identical — **deferred to kits-follow-ups, fix both together; anchoring rejects some URIs with unusual passwords that pass today, so the legacy change needs a changelog note** — issue #3067 (open, kits-follow-ups)
- [x] `storage-resize-images`: `FUNCTION_MEMORY` prompt preselects `512 MB` where the extension preselected `1 GB`: `defineInt` select with `default: 1024` whose first option is `512`, and the CLI ignores non-string select defaults (firebase/firebase-tools#11053). Same class as `MAKE_PUBLIC` in #3148; the only other non-string select across kits with a non-first default — issue #3156 — https://github.com/firebase/extensions/pull/3164 (merged 2026-09-10)
- [x] `storage-resize-images`: unknown `IMAGE_TYPE` values not rejected at config resolve time — issue #3124 (closed as not planned). **DECISION: not a parity difference.** The extension has the identical gap (`functions/src/config.ts:85`, and no `validationRegex` on the `IMAGE_TYPE` param), so adding an allowlist kit-side alone would be stricter than the extension. The `False` that prompted this was a kit-only typo from #2984 (`original: "False"`), already fixed at source in #3064. Inherited bug filed against both codebases as #3168.
- [x] Cross-kit: `firestore-incremental-capture` (`7.3.2`), `firestore-send-email` and `firestore-vector-search` (`^7.3.2`) are the only kits not on `firebase-functions@^7.3.3-rc.0`; caret excludes prereleases so they run stable 7.3.2 - issue #3154 - https://github.com/firebase/extensions/pull/3155 (merged 2026-09-08)
- [x] `storage-resize-images`: `IS_ANIMATED` select label reads `True` where the extension says `Yes` (`kits/storage-resize-images/src/config.ts:189` vs `extension.yaml:289-292`) — issue #3123 — https://github.com/firebase/extensions/pull/3148 (merged 2026-09-08)
- [x] `firestore-bigquery-export`: `logs.start()` called with no argument → logs `undefined` (§4) - https://github.com/firebase/extensions/pull/3039 (merged)
- [x] `firestore-vector-search`: custom-endpoint content-type check dropped (§4c) - https://github.com/firebase/extensions/pull/3042 (merged)
- [x] `firestore-send-email`: `formatZodError` `invalid_string` branch dropped - https://github.com/firebase/extensions/pull/3040 (merged)
- [x] `storage-resize-images`: `BLOCK_NONE` accepted where the extension threw (§4); `cacheControlHeader: ""` set literally (§7) - https://github.com/firebase/extensions/pull/3044 (merged)
- [x] `bigquery-firestore-export`: dropped log helpers; `MAX_STALENESS` doc example 8h → 4h - https://github.com/firebase/extensions/pull/3045 (merged)
- [x] `delete-user-data`: `node-fetch@2` where the extension used global `fetch` - https://github.com/firebase/extensions/pull/3041 (merged)
- [x] `firestore-translate-text`: dead backfill remnants (`filterLanguagesFn`, unused log helpers) - https://github.com/firebase/extensions/pull/3043 (merged)
- [x] `firestore-genai-chatbot`: local `SafetySetting` type (no enum check); `PROJECT_ID` error text - https://github.com/firebase/extensions/pull/3046 (merged; whole minor-cleanups batch issue #3036 now complete)

### Coverage

- [x] `firestore-bigquery-change-tracker`: jest suites run in no CI workflow — **deferred to kits-follow-ups, out of scope for kits-stable** — issue #3053 (open) — https://github.com/firebase/extensions/pull/3171 (open) @cabljac
- [x] `bigquery-firestore-export`: 1330 unit + 3170 E2E lines → 550 unit; `ensureNotificationTopic` untested - https://github.com/firebase/extensions/pull/3056 (merged; 19 → 65 tests)
- [x] `firestore-genai-chatbot`: emulator E2E + logger/overrides/config suites gone - https://github.com/firebase/extensions/pull/3058 (merged; incl. emulator flow test, now run in PR CI via #3070)
- [x] `firestore-counter`: no emulator tests - https://github.com/firebase/extensions/pull/3054 (merged; run in PR CI via #3070)
- [x] `delete-user-data`: lazy `getContext()` path untested - closed by #2955 and https://github.com/firebase/extensions/pull/3055 (both merged)
- [x] `firestore-send-email`: 2540 → 483 lines - https://github.com/firebase/extensions/pull/3057 (merged; 121 tests, coverage audit found no decrease vs the legacy suite)

### Look into

- [x] Could kits use `FIREBASE_KIT_INSTANCE_ID`? Yes - firebase-tools >= 15.27.0 injects it for kit instances (discovery, deploy, emulator, serve). It must be read via `process.env`, never declared as a param (the params machinery only consults `.env`, and the `FIREBASE_` prefix is reserved there). Implemented for delete-user-data in #3060; remaining kits migrated in https://github.com/firebase/extensions/pull/3122 (merged 2026-09-08, closes #3063).

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.