---
id: w1/m24/t003
title: Deploy verb + app-apply accept the stack form — DBs first, idempotent
worker: worker1
status: todo
estimate: 35m
depends_on: [w1/m24/t001, w1/m24/t002]
---

## Objective

One call applies the stack: `apps.Service.Deploy` and `scripts/app-apply.sh` accept the multi-resource form, creating/updating M Database CRs then N App CRs in dependency order, through the same single-writer path as single-service deploys (store rows when `BEX_CP_DB_URI`). Re-applying the same file is a per-resource idempotent no-op (existing upsert semantics — no spurious restarts or new deploy records for unchanged entries). `DRY_RUN=1` previews the whole set.

## Context

- `apps.Service.Create` upsert + the existing `Deploy` mapper (w2/m2); Database creation path from the postgres feature (w1/m17 verbs) — delegate, don't re-implement.
- Removed-entry handling: Render Blueprints optionally sync-delete; bex v1 does **not** delete resources absent from the file (document the divergence for t006).

## Files

- `lego/backend/internal/apps/{deploy,service}.go`
- `scripts/app-apply.sh`

## Acceptance criteria

- [ ] One call yields all CRs in order; unchanged re-apply produces zero spec diffs and zero new deploy records; `DRY_RUN=1` renders the full set without writing.

## Out of scope

- Sync-delete of removed entries (documented divergence); progress streaming.
