f rules are live and logs are clean, here’s a super-quick validation pack so you (or Replit) can prove end-to-end without touching more rules.

60-second smoke tests

List API returns the new template

curl -s https://<your-domain>/api/bp-templates-firebase/list | jq


✅ Expect an array with business-plan-general-blank-template and isMaster: true.

Public preview is readable (no auth)

curl -I https://firebasestorage.googleapis.com/v0/b/<bucket>/o/templates%2Fbusiness-plan%2Fpreviews%2Fbusiness-plan-general-blank-template%2Fv1-preview.webp?alt=media


✅ Expect 200 OK and an image/webp content-type.

DOCX is readable if you chose public reads (your rules allow read: true)

curl -I "https://firebasestorage.googleapis.com/v0/b/<bucket>/o/templates%2Fbusiness-plan%2Fdocs%2Fbusiness-plan-general-blank-template%2Fv1.docx?alt=media"


✅ 200 OK and application/vnd.openxmlformats-officedocument.wordprocessingml.document.

Non-admin write is blocked
Try uploading to /templates/business-plan/docs/... while not signed in or signed in as a non-admin.
✅ Expect permission denied in response and Storage logs.

Admin write works
Signed in as admin, upload a tiny dummy file to /templates/business-plan/previews/.../ping.txt.
✅ 200 OK. Delete it afterwards.

Uniqueness of master (one per category)

In Firestore, run a quick query on templates where category == "General" and isMaster == true.
✅ Exactly one result (your General Blank).

API contract (what the list endpoint should return)

Minimal contract

[
  {
    "title": "Business Plan General Blank Template",
    "slug": "business-plan-general-blank-template",
    "category": "General",
    "industries": ["general", "business", "startup"],
    "currentVersion": "v1",
    "isActive": true,
    "isMaster": true,
    "storagePaths": {
      "docx": "templates/business-plan/docs/business-plan-general-blank-template/v1.docx",
      "preview": "templates/business-plan/previews/business-plan-general-blank-template/v1-preview.webp",
      "thumb": "templates/business-plan/previews/business-plan-general-blank-template/v1-thumb.webp"
    },
    "sections": [
      { "id": "cover", "label": "Cover" },
      { "id": "executive-summary", "label": "Executive Summary" },
      { "id": "company-overview", "label": "Company Overview" },
      { "id": "market-analysis", "label": "Market Analysis" },
      { "id": "products-services", "label": "Products & Services" },
      { "id": "go-to-market", "label": "Go-To-Market" },
      { "id": "operations", "label": "Operations" },
      { "id": "team", "label": "Management & Team" },
      { "id": "financials", "label": "Financials" },
      { "id": "appendix", "label": "Appendix" }
    ],
    "createdAt": 1727660000000,
    "updatedAt": 1727660000000
  }
]


Notes:

Timestamps can be millis or ISO; just be consistent.

sections is enough for the UI preview renderer; full blocks can live in a manifest file if you prefer to keep the list payload light.

Quick UI checks

/admin/upload-business-plan-firebase

Shows “Master Template” chip after publish.

Preview card uses the preview.webp; if missing, it falls back to rendering from sections.

Public gallery

New item appears immediately (Cloud CDN may lag a few seconds; versioned paths avoid cache issues).

Fast rollback / rotate master

If you ever need to flip the master:

Set current isMaster: false, set the replacement’s isMaster: true, update currentVersion, and (optionally) a pointer doc like settings/templates.business-plan with:

{ "masterSlug": "business-plan-general-blank-template", "masterVersion": "v1" }


Do it in a Firestore transaction so the flags can’t drift.