From 03a16c842662ba9cd44aba0dd409e8fd402d79f2 Mon Sep 17 00:00:00 2001 From: Fluory Date: Mon, 28 Sep 2026 22:24:23 +0200 Subject: [PATCH 1/4] fix(deploy): build the AI service on Vercel only when services/ai changed (#98) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Every push – main and every PR – built and pushed a new AI image, also for web-only changes, until the Hobby container registry hit its image limit and the production build of #95 failed. An ignoreCommand now skips the build unless services/ai changed since the last successful deployment (else the parent commit; if git cannot compare, it builds). The runbook names the registry limit and how to prune it. Co-Authored-By: Claude Opus 5.5 (1M context) --- CHANGELOG.md | 2 ++ docs/technical/deployment-vercel.md | 8 ++++++++ services/ai/vercel.json | 3 ++- 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b871ada..9bb4a11 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ This file records what changes **in the product** – process and session state ## [Unreleased] ### Changed +- The AI service's Vercel project builds only when something under `services/ai/` changed: fewer images in its + container registry and fewer deployments per push (#98). - A unit correction is stored like the extraction stores units: „Stk.“, „Stück“ or „Meter“ become the ERP units `pcs` and `m`; other units stay as typed. The correction field shows „Stk.“, and the audit event keeps what was typed (#96). diff --git a/docs/technical/deployment-vercel.md b/docs/technical/deployment-vercel.md index 764d012..8928eea 100644 --- a/docs/technical/deployment-vercel.md +++ b/docs/technical/deployment-vercel.md @@ -73,6 +73,14 @@ creation). The script is all-or-nothing; running it twice fails on "role already 5. `GET /healthz` → 200. The production URL → `AI_SERVICE_URL` of the web app (§4). Instances scale to zero after 5 minutes without traffic (vercel.com/docs/functions/container-images, verified 2026-09-27); the first call after that pays a cold start (measured about 5.6 s). +6. **Builds only when the service changed (#98):** `services/ai/vercel.json` sets an `ignoreCommand` that + skips the build unless something under `services/ai/` changed since the last successful deployment + (`VERCEL_GIT_PREVIOUS_SHA`, else the parent commit; if git cannot compare, it builds). Every build pushes + an image to the project's container registry, which has a maximum number of images on the Hobby plan – + when a build fails with `repository has reached the maximum allowed number of images`, prune old images + (dashboard: project `requestflow-ai` → Sandboxes → Container Registry, or `vercel vcr image ls dockerfile` + and `vercel vcr image rm dockerfile `), keeping the image of the current production deployment + and one rollback candidate. Deleting an image is permanent. ## 4. Vercel project diff --git a/services/ai/vercel.json b/services/ai/vercel.json index 2c12ed8..0f04c79 100644 --- a/services/ai/vercel.json +++ b/services/ai/vercel.json @@ -1,4 +1,5 @@ { "$schema": "https://openapi.vercel.sh/vercel.json", - "regions": ["fra1"] + "regions": ["fra1"], + "ignoreCommand": "git diff --quiet \"${VERCEL_GIT_PREVIOUS_SHA:-HEAD^}\" HEAD -- ./" } From 22b3adaf0419d7bf700ed5011d7ac37274e01cf3 Mon Sep 17 00:00:00 2001 From: Fluory Date: Mon, 28 Sep 2026 22:42:14 +0200 Subject: [PATCH 2/4] =?UTF-8?q?docs(deploy):=20address=20the=20#102=20revi?= =?UTF-8?q?ew=20=E2=80=93=20redeploy=20must=20reach=20Ready,=20honest=20sc?= =?UTF-8?q?ope?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - a redeploy runs the Ignored Build Step too: after rotating a secret or the Gemini switch, redeploy without it and check Ready (CANCELED = not active) - skipped builds may still count against deployment limits: the CHANGELOG no longer promises fewer deployments - first build of a new branch compares only its last commit (AI preview may be missing); vcr commands name --project Co-Authored-By: Claude Opus 5.5 (1M context) --- CHANGELOG.md | 2 +- docs/technical/deployment-vercel.md | 24 ++++++++++++++++-------- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9bb4a11..043f339 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ This file records what changes **in the product** – process and session state ### Changed - The AI service's Vercel project builds only when something under `services/ai/` changed: fewer images in its - container registry and fewer deployments per push (#98). + container registry and fewer builds. A redeploy after changing its variables must not be skipped (runbook §3.6) (#98). - A unit correction is stored like the extraction stores units: „Stk.“, „Stück“ or „Meter“ become the ERP units `pcs` and `m`; other units stay as typed. The correction field shows „Stk.“, and the audit event keeps what was typed (#96). diff --git a/docs/technical/deployment-vercel.md b/docs/technical/deployment-vercel.md index 8928eea..17dce06 100644 --- a/docs/technical/deployment-vercel.md +++ b/docs/technical/deployment-vercel.md @@ -75,12 +75,18 @@ creation). The script is all-or-nothing; running it twice fails on "role already verified 2026-09-27); the first call after that pays a cold start (measured about 5.6 s). 6. **Builds only when the service changed (#98):** `services/ai/vercel.json` sets an `ignoreCommand` that skips the build unless something under `services/ai/` changed since the last successful deployment - (`VERCEL_GIT_PREVIOUS_SHA`, else the parent commit; if git cannot compare, it builds). Every build pushes - an image to the project's container registry, which has a maximum number of images on the Hobby plan – - when a build fails with `repository has reached the maximum allowed number of images`, prune old images - (dashboard: project `requestflow-ai` → Sandboxes → Container Registry, or `vercel vcr image ls dockerfile` - and `vercel vcr image rm dockerfile `), keeping the image of the current production deployment - and one rollback candidate. Deleting an image is permanent. + (`VERCEL_GIT_PREVIOUS_SHA`, else the parent commit; if git cannot compare, it builds). A skipped build + ends as `CANCELED`; skipped builds may still count against the plan's deployment limits. A new branch + without a successful AI deployment compares only its last commit, so an AI preview may be missing there + (production is not affected). **A redeploy runs the same step:** after changing a variable of + `requestflow-ai` (§9), redeploy without the Ignored Build Step (untick it in the Redeploy dialog if offered) + and check that the deployment reaches **Ready** – `CANCELED` means the new value is not active. + Every build pushes an image to the project's container registry, which has a maximum number of images on + the Hobby plan – when a build fails with `repository has reached the maximum allowed number of images`, + prune old images (dashboard: project `requestflow-ai` → Sandboxes → Container Registry, or + `vercel vcr image ls dockerfile --project requestflow-ai` and + `vercel vcr image rm dockerfile --project requestflow-ai`), keeping the image of the current + production deployment and one rollback candidate. Deleting an image is permanent. ## 4. Vercel project @@ -173,12 +179,14 @@ run started meanwhile waits for the first and then finds the samples in place; a pause the Supabase project. - Code: Vercel "Instant Rollback" to the previous deployment. Migrations are forward-only (`docs/technical/operations.md` → Rollback); roll back code only to a version that knows the schema. -- Rotate a leaked secret in its settings page, then redeploy (Vercel reads variables at deploy time). +- Rotate a leaked secret in its settings page, then redeploy (Vercel reads variables at deploy time). For + `requestflow-ai` the redeploy must not be skipped by the Ignored Build Step (§3 step 6): check it is **Ready**. - Pause the AI service: pause the Vercel project `requestflow-ai`; uploads then end in a visible processing error with retries instead of reaching the model. - When the Gemini free-tier exception expires (ADR-0001 D11 amendment 2026-09-26, at the latest 2026-10-31): delete the key in Google AI Studio, remove `GEMINI_API_KEY` and `AI_ALLOW_GEMINI_API_DEV` - from `requestflow-ai`, then switch to the paid tier or Vertex `eu` (§3 step 4) and redeploy. + from `requestflow-ai`, then switch to the paid tier or Vertex `eu` (§3 step 4) and redeploy – **Ready**, not + skipped (§3 step 6). - Remove the showcase completely: delete both Vercel projects (`requestflow`, `requestflow-ai`) and the Supabase project – all data is synthetic, nothing has to be kept. From 60dc134c2eb954d75020765be50613b6d0e3cd56 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 2 Oct 2026 08:22:31 +0000 Subject: [PATCH 3/4] chore(deploy): the AI service project deploys only from main (#104) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit git.deploymentEnabled { "**": false, "main": true } – PR branches create no requestflow-ai deployment, so they stop counting against the Hobby deployment limits. CI keeps building and smoke-testing the image (#79). Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01DJ5vaKvTYiMvdngT4d3xo1 --- CHANGELOG.md | 2 ++ docs/technical/deployment-vercel.md | 10 ++++++++-- services/ai/vercel.json | 8 +++++++- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 043f339..317eaff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ This file records what changes **in the product** – process and session state ### Changed - The AI service's Vercel project builds only when something under `services/ai/` changed: fewer images in its container registry and fewer builds. A redeploy after changing its variables must not be skipped (runbook §3.6) (#98). +- The AI service project deploys only from `main`: pull-request branches create no AI deployment at all, so + they no longer use up the Vercel plan's deployment limits (runbook §3.7) (#104). - A unit correction is stored like the extraction stores units: „Stk.“, „Stück“ or „Meter“ become the ERP units `pcs` and `m`; other units stay as typed. The correction field shows „Stk.“, and the audit event keeps what was typed (#96). diff --git a/docs/technical/deployment-vercel.md b/docs/technical/deployment-vercel.md index 17dce06..bc06cb5 100644 --- a/docs/technical/deployment-vercel.md +++ b/docs/technical/deployment-vercel.md @@ -77,8 +77,7 @@ creation). The script is all-or-nothing; running it twice fails on "role already skips the build unless something under `services/ai/` changed since the last successful deployment (`VERCEL_GIT_PREVIOUS_SHA`, else the parent commit; if git cannot compare, it builds). A skipped build ends as `CANCELED`; skipped builds may still count against the plan's deployment limits. A new branch - without a successful AI deployment compares only its last commit, so an AI preview may be missing there - (production is not affected). **A redeploy runs the same step:** after changing a variable of + without a successful AI deployment compares only its last commit (since #104 only `main` deploys, see 7). **A redeploy runs the same step:** after changing a variable of `requestflow-ai` (§9), redeploy without the Ignored Build Step (untick it in the Redeploy dialog if offered) and check that the deployment reaches **Ready** – `CANCELED` means the new value is not active. Every build pushes an image to the project's container registry, which has a maximum number of images on @@ -87,6 +86,13 @@ creation). The script is all-or-nothing; running it twice fails on "role already `vercel vcr image ls dockerfile --project requestflow-ai` and `vercel vcr image rm dockerfile --project requestflow-ai`), keeping the image of the current production deployment and one rollback candidate. Deleting an image is permanent. +7. **Only `main` deploys (#104):** `services/ai/vercel.json` sets `git.deploymentEnabled` to + `{ "**": false, "main": true }` – a push to any other branch (every PR) creates **no** `requestflow-ai` + deployment at all, not even a skipped one, so PRs no longer use up the plan's deployment limits. When + several patterns match, `true` wins; `**` also covers branch names with `/` (minimatch). A merge to `main` + that touches `services/ai/` still deploys production (step 6 decides whether it builds). CI keeps + building and smoke-testing the AI image on every Dockerfile or lock change (#79), so no AI preview is + needed; the web app's previews keep calling the production AI URL. ## 4. Vercel project diff --git a/services/ai/vercel.json b/services/ai/vercel.json index 0f04c79..9047d94 100644 --- a/services/ai/vercel.json +++ b/services/ai/vercel.json @@ -1,5 +1,11 @@ { "$schema": "https://openapi.vercel.sh/vercel.json", "regions": ["fra1"], - "ignoreCommand": "git diff --quiet \"${VERCEL_GIT_PREVIOUS_SHA:-HEAD^}\" HEAD -- ./" + "ignoreCommand": "git diff --quiet \"${VERCEL_GIT_PREVIOUS_SHA:-HEAD^}\" HEAD -- ./", + "git": { + "deploymentEnabled": { + "**": false, + "main": true + } + } } From 1cc5f6878c2dfdea2b326dba167bd1d6dc836606 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 2 Oct 2026 08:23:48 +0000 Subject: [PATCH 4/4] docs(deploy): precise preview and CI statements in runbook step 7 (#106 review) Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01DJ5vaKvTYiMvdngT4d3xo1 --- docs/technical/deployment-vercel.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/docs/technical/deployment-vercel.md b/docs/technical/deployment-vercel.md index bc06cb5..c72f169 100644 --- a/docs/technical/deployment-vercel.md +++ b/docs/technical/deployment-vercel.md @@ -77,7 +77,8 @@ creation). The script is all-or-nothing; running it twice fails on "role already skips the build unless something under `services/ai/` changed since the last successful deployment (`VERCEL_GIT_PREVIOUS_SHA`, else the parent commit; if git cannot compare, it builds). A skipped build ends as `CANCELED`; skipped builds may still count against the plan's deployment limits. A new branch - without a successful AI deployment compares only its last commit (since #104 only `main` deploys, see 7). **A redeploy runs the same step:** after changing a variable of + without a successful AI deployment compares only its last commit (since #104 only `main` deploys, see + step 7). **A redeploy runs the same step:** after changing a variable of `requestflow-ai` (§9), redeploy without the Ignored Build Step (untick it in the Redeploy dialog if offered) and check that the deployment reaches **Ready** – `CANCELED` means the new value is not active. Every build pushes an image to the project's container registry, which has a maximum number of images on @@ -90,9 +91,11 @@ creation). The script is all-or-nothing; running it twice fails on "role already `{ "**": false, "main": true }` – a push to any other branch (every PR) creates **no** `requestflow-ai` deployment at all, not even a skipped one, so PRs no longer use up the plan's deployment limits. When several patterns match, `true` wins; `**` also covers branch names with `/` (minimatch). A merge to `main` - that touches `services/ai/` still deploys production (step 6 decides whether it builds). CI keeps - building and smoke-testing the AI image on every Dockerfile or lock change (#79), so no AI preview is - needed; the web app's previews keep calling the production AI URL. + that touches `services/ai/` still deploys production (step 6 decides whether it builds). The setting gates + only deployments triggered by a Git push – a redeploy from the dashboard (§9) still works. No AI preview is + needed: CI builds the image and checks `/healthz` on AI-image changes (#79), and web previews do not call + the AI service (§4: Preview variables stay empty). The preview protection of step 2 only matters if Git + deployments of other branches are ever switched back on. ## 4. Vercel project