bilko

Bilko product documentation

Test Sweep 2026-05-15

Bilko Comprehensive Test Sweep — 2026-05-15

Date: 2026-05-15
Verdict: PARTIAL
Mission Control: MC #100704 (preflight) through #100715 (this documentation)
Orchestrated by: John (CEO request option C)
Executed via: Ollama DAG (primary) + direct subagent fallback
Validator pattern: /verify-fix-loop


Summary

A comprehensive test sweep across Bilko's 3 deployed domains (bilko.io stage, bilko.cloud UAT, bilko.company UAT) covering 9 test categories: backend unit, backend integration, frontend unit, end-to-end (×3 domains), SAST, DAST, and performance + accessibility.

Consolidated verdict: PARTIAL

Sweep cost: TBD (to be extracted from MC aggregation)
Execution method: DAG orchestration → task distribution → validator synthesis
Follow-up MCs opened: 6 MCs for fix-loop iteration B (test harness repairs + header hardening)


Scope

Domains tested

  1. bilko.io stage — https://bilko-web-stage-dh4m46blja-lz.a.run.app (GCP Cloud Run, Next.js 15 frontend)
  2. bilko.cloud — https://bilko.cloud (Cloudflare Pages, HR market UAT landing)
  3. bilko.company — https://bilko.company (Cloudflare Pages, BA market UAT landing)

Test categories (9)

  1. Backend unit tests (Kotlin/Ktor, Kotest)
  2. Backend integration tests (Kotlin/Ktor, Testcontainers PostgreSQL)
  3. Frontend unit tests (Next.js 15, Vitest, jsdom)
  4. End-to-end bilko.io stage (Playwright, auth-fresh fixture)
  5. End-to-end bilko.cloud UAT (Playwright, static landing assertions)
  6. End-to-end bilko.company UAT (Playwright, static landing assertions)
  7. SAST (Detekt, npm audit, SonarCloud, Snyk)
  8. DAST (passive header analysis, OWASP ZAP baseline)
  9. Performance + Accessibility (Lighthouse CI, 3 runs per domain)

Blueprint references


Results by Category

Category Verdict Tests Passed Tests Total Key Numbers Evidence File
BE Unit PASS 693 693 121/121 core, 29 suites api-unit-rerun-v2.json
BE Integration PARTIAL 556 576 20 failures (enum DDL) api-int-rerun-v2.json
FE Unit PARTIAL 56 67 14 suites pass, 6 fail web-unit.json + core-unit.json
E2E bilko.io FAIL 3 37 34 unexpected (401) e2e-io/summary.json
E2E bilko.cloud PASS 8 8 All assertions verified e2e-cloud/summary.json
E2E bilko.company PASS 8 8 All assertions verified e2e-company/summary.json
SAST PARTIAL Detekt 0 npm audit 2 HIGH sast-summary-100711.json
DAST PARTIAL 0 CRITICAL 4 MEDIUM header gaps dast-summary-100712.json
Perf + A11y PASS 100/96/96/92 3 domains All gates pass lhci-io/cloud/company.json

Findings

Critical Security Findings

Count: 0

Product Regressions

Count: 0

All test failures fall into two categories:

  1. Test infrastructure/harness issues (in-scope for fix-loop iteration 2):

    • auth-fresh.ts fixture does not persist access token across Playwright context resets (E2E bilko.io 34/37 FAIL)
    • localStorage mock missing in Vitest jsdom setup (FE settings.test.tsx 8 failures)
    • i18n stub incomplete in invoices.test.tsx (raw key {invoices} rendered)
    • MarketContext.test.tsx null guard missing (1 test TypeError)
  2. Pre-existing structural defects (out-of-scope, tracked separately):

    • InvoiceStatus enum DDL ordering in DbTestBase.setUpDatabase (BE integration 20 failures)
    • SonarCloud + Snyk tokens absent from Bitwarden (SAST coverage gap)
    • x-frame-options, CSP absent on bilko.cloud + bilko.company (DAST 4 MEDIUM)
    • npm audit 2 HIGH in devDependencies (rollup via @sentry/nextjs, tmp via @lhci/cli)

Fix-Loop Iteration 2 Outcomes

Status at publish time: In progress (CodeCraft B-half dispatched)

Fix-loop candidates identified by validator:

  1. auth-fresh.ts fixture — inject access token into Playwright storageState (Vizu/Playwright author, effort S)
  2. localStorage mock — add vi.stubGlobal in vitest.config.ts setupFiles (CodeCraft/FE author, effort XS)
  3. i18n stub — add i18n mock provider in test wrapper (CodeCraft/FE author, effort S)
  4. MarketContext null guard — add null guard in MarketContext.tsx or test stub (CodeCraft/FE author, effort S)

Follow-up MCs opened (6):

  1. MC TBD — Fix InvoiceStatus enum ordering in BE integration test schema (DbTestBase.setUpDatabase) — Priority H, owner CodeCraft
  2. MC TBD — Provision SONAR_TOKEN in Bitwarden and enable SonarCloud SAST scan — Priority M, owner FlowForge
  3. MC TBD — Provision Snyk auth token in Bitwarden and integrate Snyk into CI — Priority M, owner FlowForge
  4. MC TBD — Add _headers file to bilko.cloud and bilko.company CF Pages deployments (x-frame-options, CSP, HSTS) — Priority H, owner FlowForge
  5. MC TBD — Fix rollup HIGH CVE (GHSA-mw96-cpmx-2vgc) via @sentry/nextjs upgrade or exclusion — Priority M, owner CodeCraft
  6. MC TBD — Execute ZAP active scan against bilko.io stage after image stabilises — Priority M, owner Securion

Lighthouse Scores (Performance + Accessibility)

All 3 domains tested with Lighthouse CI (3 runs each, median scores):

Domain Performance Accessibility Best Practices SEO
bilko.io 100 96 96 92
bilko.cloud 100 96 96 92
bilko.company 100 96 96 92

Gates:

A11y failures (all 3 domains, same pattern):

Score remains 96/100 (within acceptable range per TEST-PLAN §7.4 target: Lighthouse Performance Score > 90).

Note: axe-core WCAG 2.1 AA Playwright tests (TEST-PLAN §8.9) were not executed in this sweep — blocked by e2e_io auth-fresh fixture failure.


Evidence Index

All evidence files stored in /tmp/bilko-test-sweep/ with integrity checksums:

File Category SHA256 (first 16 hex) Notes
validator-verdict.json Validator synthesis (computed at publish) Canonical consolidated verdict
api-unit-rerun-v2.json BE unit (computed) 693/693 PASS
api-int-rerun-v2.json BE integration (computed) 556/576 PASS, 20 failures
web-unit.json FE unit (computed) 56/67 PASS
core-unit.json FE unit (core) (computed) 121/121 PASS
e2e-io/summary.json E2E bilko.io (computed) 3/37 PASS
e2e-cloud/summary.json E2E bilko.cloud (computed) 8/8 PASS
e2e-company/summary.json E2E bilko.company (computed) 8/8 PASS
sast-summary-100711.json SAST (computed) Detekt 0 findings, npm audit 2 HIGH
dast-summary-100712.json DAST (computed) 0 CRITICAL, 0 HIGH, 4 MEDIUM
lhci-io.json Perf+A11y bilko.io (computed) 100/96/96/92
lhci-cloud.json Perf+A11y bilko.cloud (computed) 100/96/96/92
lhci-company.json Perf+A11y bilko.company (computed) 100/96/96/92
mc-ids.json MC tracking (computed) 12 MC IDs (#100704–#100715)

Screenshot evidence:


Genesis

CEO ask: 2026-05-15 morning
Option selected: C (comprehensive sweep across all test categories)
Execution method: Ollama DAG orchestration (primary) + direct subagent fallback when DAG blocked
Validator pattern: /verify-fix-loop (atomic-claim decomposition, read-only verification)
12 MCs spawned: #100704 (preflight), #100705 (BE unit), #100706 (BE integration), #100707 (FE unit), #100708 (E2E bilko.io), #100709 (E2E bilko.cloud), #100710 (E2E bilko.company), #100711 (SAST), #100712 (DAST), #100713 (Perf+A11y), #100714 (validator), #100715 (this documentation)

Blueprint compliance:

Met:

Gaps:



Published by: Skillforge (ALAI knowledge management)
Reviewed by: John (AI Director)
Approved for publish: 2026-05-15

Bilko HR Roadmap — Fiscal Compliance + Feature-Enable Architecture (2026-05-29)

Bilko HR Roadmap — Fiscal Compliance + Feature-Enable Architecture

Created: 2026-05-29 CEO directive: B2B prvo, B2C odgođen, citav app feature-enable based (micro-frontend) za per-user per-plan delivery Status: Active roadmap, supersedes prior B2B-only scoping


1. Executive summary


2. Croatian fiscal legal framework

Zakon o fiskalizaciji

F1 — B2C fiskalizacija (POS / kasa)

F2 — B2B eRačun

Terminologija

PDV stope


3. Phased roadmap sa MC mapingom

Phase 0 — B2B prvo (current focus)

Phase MC Naslov Status Vlasnik
0a #102447 Storecove HR-FISK 2.0 activation (FAST PATH, 4-5 dana) open — counter-offer poslan 2026-05-28 john
0b #102398 Sveračun API sandbox onboarding (negotiated <€0.10/invoice) open — čeka CEO email za creds + računovođa intro john
0c #102481 Feature-enable based micro-frontend architecture (CEO 2026-05-29) open — kritičan prerequisite, dolazi PRIJE Phase 1 john
0d #102401 Bilko Phase 0 multi-org switcher + Securion RLS gate open — zavisi od bilo koji B2B ACK john
0e #102399 5 accountant pilot recruitment (90-day) open — CEO outreach alem
0f #102400 HR pravna osoba tracking (background) open — Sveračun potvrdio NIJE potrebno za B2B; B2C TBD alem

Phase 1+ — B2C (DEFERRED per CEO 2026-05-29)

Phase MC Naslov Status
1 #102478 B2C fiskalizacija MVP (ZKI + JIR + SOAP + FINA cert) DEFERRED — 21 dana effort
2 #102479 B2C POS web kasa modul DEFERRED — 19 dana effort
3 TBD Hardware integration (fiskalni printer, cash drawer, barcode) not opened
4 TBD Industry specializations (hospitality, retail, services) not opened

4. Feature-enable architecture directive

CEO directive (verbatim, 2026-05-29)

"citav app treba da je feature enable based (micro frontend) nadam se da mozemo lako features per user per pay deliver a ne da sve hakiramo"

Problem statement

Cilj — komponente architekture

  1. Central feature catalog — kanonski popis svih feature-a sa metadata (market, plan tier, dependency graf)
  2. Per-user feature resolution — funkcija od (tenant org country + plan tier + accountant role + custom overrides)
  3. Micro-frontend split — nezavisni feature modules koji se učitavaju/ne učitavaju po feature flag-u
  4. Pricing model alignment — Basic/Pro/Accountant tier = feature subset (definirano u catalog-u)
  5. Safe fallback — feature flag service down → conservative defaults (Basic, ne Pro)
  6. Audit trail — koji user kad accessovao koji feature

Implementation phases (MC #102481)

Build vs buy

Datavera istražuje:


5. Open decisions for CEO

  1. B2B path: prihvatamo Storecove counter-offer (ako stigne s revidiranim quote) ili idemo Sveračun?
  2. HR pravna osoba: potvrđeno NIJE potrebna za B2B; otvoreno za buduće B2C (kad ga otvorimo)
  3. Feature catalog vendor: build vs buy — odluka nakon Datavera istrage
  4. Multi-market timing: kad RS (SEF) + BiH ulaze u feature catalog scope?

6. MC indeks

MC Priority Status Vlasnik Naslov Rute
#100332 H ready_for_review alem Sveračun CEO outreach (parent) bizdev
#102447 H open john MC-A2 Storecove FAST PATH backend
#102398 H open john MC-A Sveračun sandbox onboarding backend
#102481 H open john Feature-enable architecture (CEO 2026-05-29) backend
#102401 H open john MC-D Phase 0 multi-org switcher backend
#102399 H open alem MC-B 5 accountant pilot recruit bizdev
#102400 M open alem MC-C HR doo tracking bizdev
#102478 H open (DEFERRED) john MC-E B2C fiskalizacija MVP backend
#102479 M open (DEFERRED) john MC-F B2C POS web kasa frontend
#102448 H open john Vendor email routing + auto-forward devops
#102449 H open john Policy: no strategic reply bez CEO OK bizdev

7. Source artifacts


8. Decision log

Datum Odluka Kontekst
2026-05-28 Bilko HR ide dual-track GTM (accountant + direct SMB) Sveračun meeting strategic input
2026-05-28 Storecove counter-offer poslan (no name drop Sveračun) CEO directive — leverage
2026-05-29 B2B prvo, B2C odgođen CEO scope decision
2026-05-29 Feature-enable / micro-frontend arch je obavezan prerequisite za sve daljnje builds CEO architecture directive

9. Next steps

  1. Storecove reply chase — ako nema odgovora do 2026-06-04, poslati follow-up sa ref na contract expiry 27-06-2026
  2. Sveračun follow-up email — CEO šalje za sandbox creds + 3-5 računovođa intro (target: 2026-05-30)
  3. Datavera dispatch za feature catalog vendor comparison (LaunchDarkly vs GrowthBook vs Unleash vs build-our-own)
  4. Mehanik gate za MC #102481 (feature-enable arch) prije CodeCraft Phase 0 dispatch
  5. Update MEMORY.md sa pointer-om na ovu BookStack stranicu (skip-list ako se promijeni status)

Bilko BUG-005 — revenueMTD / credit-note documentType + RLS migration landmine

Bilko BUG-005 — revenueMTD negative (credit-note documentType + RLS migration landmine)

MC: #103001 (child #102887) | Fixed: 2026-06-05 | Tag: v0.2.18 / PR #255

Symptom

Dashboard KPI revenueMTD showed -457.50 on bilko-demo. CEO-visible.

Root cause (two layers)

  1. Write-side: InvoiceService.createCreditNote insert omitted documentType. The Invoices.documentType column has no Kotlin .default, so the PG column default 'standard' applied. Every credit note was stored as STANDARD with a negative amount. The v0.2.17 read-side fix (ReportService.getRevenueForPeriod excludes CREDIT_NOTE) therefore could not exclude them.
  2. Backfill blocked by RLS: migration V65 (UPDATE invoices SET document_type='credit_note' WHERE document_type='standard' AND invoice_number LIKE 'CN-%') recorded success=t but affected 0 rows. The org_isolation RLS policy returns FALSE when app.current_org_id is unset; Flyway runs as bilko with no org GUC, so RLS hid every row. No SQL error → Flyway "succeeded".

Fix

Verification (Proveo independent PASS, live)

revenueMTD 375.00 (was -457.50); CN-2026-001..007 all documentType=credit_note; freshly created CN is credit_note; INV-2026-001 stays standard. Evidence: /tmp/verify-103001/proveo-validation.md.

Reusable lessons

Bilko Backoffice — Backend MVP (Sentry + audit request_id + support_tickets)

1. Overview

This backend slice (MC #103323, branch feat/103323-backoffice-backend, commit 6b214a00, PR #316) delivers the diagnostic and intake backbone for the Bilko support fix-loop.

Before this slice, when a customer hit an accounting error on app.bilko.cloud neither the platform team nor the customer had a way to identify which request failed or why. Three components address that gap:

  1. Sentry error capture — catch-all (INFRA-only) exception capture with PII scrub and Cloud Run release/serverName metadata. Inert until SENTRY_DSN secret is provisioned (OCD-1, CEO action).
  2. V71 audit_log.request_id — nullable correlation column added to every audit row, threaded from a single canonical source (call.callId) across all route handlers.
  3. V72 support_tickets + SupportTicketRoutes — customer intake channel (POST) and platform-admin triage queue (GET list + GET detail + PATCH status) with RLS, idempotency, and full status-transition audit trail.

This slice is deploy-gated behind prod cutover MC #103300. All three components were independently verified by Proveo (Angie Jones): 12/12 AC signals PASS, integration test 3/3 PASS, unit suite 1280/1280.

2. Component Map

2.1 Sentry capture — plugins/Sentry.kt + plugins/StatusPages.kt

2.2 V71 audit_log.request_id — AuditLogService.kt + migration

2.3 V72 support_tickets + SupportTicketRoutes — routes/SupportTicketRoutes.kt

3. Data Model

3.1 support_tickets columns

ColumnTypeNotes
idUUID PKgen_random_uuid() default
org_idUUID NOT NULLFK to organizations(id) ON DELETE CASCADE
user_idUUID NOT NULLFK to users(id)
error_codeTEXTNullable; currently generic VAL/INFRA (OCD-2 open CEO decision)
request_idTEXTCorrelation ID of originating failed request. NOT a FK to audit_log.request_id (one request_id maps to N audit rows). Join via equality.
context_bundleJSONB NOT NULLCHECK jsonb_typeof = 'object'. Allowlisted keys only (server-side enforced).
customer_descriptionTEXTFree text from customer
statusTEXT NOT NULLCHECK (status IN ('OPEN','TRIAGED','IN_PROGRESS','RESOLVED','CLOSED')). Default 'OPEN'.
triage_jsonJSONBNULL = not yet triaged. V2 AI agent writes here.
created_atTIMESTAMPTZ NOT NULLDEFAULT now()
updated_atTIMESTAMPTZ NOT NULLDEFAULT now(); maintained by BEFORE UPDATE trigger.
resolution_noteTEXTRequired (route-enforced) for RESOLVED/CLOSED transitions.
external_refTEXTV2 Zendesk/Linear sync. Nullable at MVP.

3.2 Indexes

3.3 RLS policies

All GUC SET statements use SET LOCAL (transaction-scoped) — pgBouncer transaction-mode pooling safe.

Customer UPDATE/DELETE immutability: no UPDATE or DELETE policy for customers. RLS ENABLED with no such policy = deny-by-default. Customers cannot modify or delete submitted tickets.

Production code audit (Proveo-confirmed): orgTransaction{} (OrgScopeSessionVariable.kt:131) always wraps SET LOCAL app.current_org_id inside transaction{}. The Testcontainers test failure (Proveo GAP-1) was caused by the test setup using a PostgreSQL superuser connection — superusers bypass RLS regardless of GUC values. Production code was never buggy.

3.4 Status transition machine

FromAllowed next states
OPENTRIAGED, CLOSED
TRIAGEDIN_PROGRESS, CLOSED
IN_PROGRESSRESOLVED, CLOSED
RESOLVEDCLOSED
CLOSED(no further transitions)

Invalid transitions return HTTP 422 with code: "INVALID_TRANSITION" and allowedNext.

3.5 context_bundle allowlist

Allowed keys (server-side enforced, rejection = HTTP 422): requestId, errorCode, httpStatus, instancePath, orgId, userId, appRoute, planTier, country, auditRef. IDs and codes only — never invoice content, names, amounts, or email addresses.

4. Diagnostic Join

SELECT al.*
FROM audit_log al
JOIN support_tickets st ON al.request_id = st.request_id
WHERE st.id = '<ticket-uuid>';

Framing (martin-kleppmann dissent): request_id is a correlation handle for cross-system debugging only — NOT tamper-evidence. The append-only guarantee for audit_log comes from the block_audit_mutation() trigger (V51), not from request_id. Platform-admin direct DB access is outside the threat model of this column.

5. Known Gaps and Follow-ups

ItemDetailStatus
OCD-1: Sentry DSN bilko-sentry-dsn / bilko-web-sentry-dsn must be provisioned in GCP Secret Manager. Inject via --update-secrets (never --set-env-vars). Sentry code is fully inert until then. CEO action required. Blocks production deploy; does not block feature branch merge.
OCD-2: error_code taxonomy Domain errors currently fall into generic VAL/INFRA codes, making ticket triage partly blind. Domain-specific codes are V2 scope (MC #103333). CEO confirmed proceed with V72 before those codes land. Open CEO decision. V2 follow-on MC #103333.
OCD-3: merge-order vs #103300 V71/V72 migration numbers must be confirmed/renumbered after #103300 merges. Open. Blocking deploy only.
Positive-path RLS assertion Integration test confirms negative proof (wrong-org INSERT rejected). Positive proof (correct-org INSERT succeeds) not explicitly asserted. Proveo: completeness gap, not safety-weakening gap. Follow-up test enhancement. Non-blocking.
CI runner quota Tracked as MC #103304. Separate MC.
Deploy gate Deploy-gated behind MC #103300 prod cutover. Dependent on #103300.

6. Verification Evidence

Bilko Backoffice — Ops Infra (Logging Views + support@ Forwarding + Preflight)

Bilko Backoffice — Ops Infra (MC #103325)

Branch: feat/103325-backoffice-infra | PR: #317 | Proveo verdict: PASS (2026-06-10) | Sibling: Backoffice Backend MVP (page 3100)


1. Cloud Logging Saved Views

GCP project: tribal-sign-487920-k0
Bucket: _Default (global)
Verified via: gcloud logging views list --bucket=_Default --location=global --project=tribal-sign-487920-k0

View IDScope filterIntended use / Log Explorer query to add
bilko-error-by-org resource.type="cloud_run_revision" AND resource.labels.service_name=~"bilko-api-(demo|stage)" Add query severity>=ERROR. Group results by orgId (parse via JSON_EXTRACT(textPayload, "$.orgId") — orgId lives in textPayload JSON, not jsonPayload).
bilko-request-trace resource.type="cloud_run_revision" AND resource.labels.service_name=~"bilko-(api|web)-(demo|stage)" Add query logName=~"stdout" OR logName=~"requests". Correlate requests end-to-end by requestId field in textPayload.
bilko-5xx-demo resource.type="cloud_run_revision" AND resource.labels.service_name=~"bilko-(api|web)-demo" Add query httpRequest.status>=500. Scoped to demo environment only.

GCP constraint — view filter expressiveness

GCP gcloud logging views create --log-filter only accepts log source, resource type, appHub fields, user labels, and log ID conditions. Severity comparisons (severity>=ERROR) and field comparisons (httpRequest.status>=500) are not valid in view filters — they must be added as Log Explorer query refinements on top of the saved view scope. This is a documented GCP platform limitation. Each view description in GCP documents this explicitly.

Log schema note: Bilko API logs structured data as JSON inside textPayload (not jsonPayload). The textPayload schema is: {"requestId":"...","method":"...","path":"...","status":N,"durationMs":N,"userId":"...","orgId":"...","ip":"..."}. ERROR logs are stack traces in textPayload; orgId is present on request-completion log lines, not on exception lines.


2. support@bilko.cloud Email Forwarding

MX provider — IMPORTANT

bilko.cloud MX = Migadu (aspmx1.migadu.com + aspmx2.migadu.com, confirmed via dig MX bilko.cloud). The CF Email Routing section in DEPLOY-MAP.md is STALE and must be corrected — Cloudflare does not handle bilko.cloud email.

Implemented forwarding

Mail flow: support@bilko.cloud (Migadu mailbox, may_receive=true, may_send=false) → Migadu forwarding → alem@alai.no

Key Migadu design constraint: Alias destinations only accept same-domain addresses — external addresses are silently rewritten to <localpart>@<same-domain>. The correct mechanism for external cross-domain delivery is a forwarding on a mailbox object (not an alias).

Implementation steps taken:

  1. Confirmed GET /v1/domains/bilko.cloud/mailboxes/support/forwardings — support@ was alias-only (no mailbox).
  2. Created support@bilko.cloud mailbox: may_receive=true, may_send=false, IMAP/POP3 disabled (receive-only).
  3. Added forwarding via POST /v1/domains/bilko.cloud/mailboxes/support/forwardings {"address":"alem@alai.no"} — response: is_active: true, confirmed_at: 2026-06-10T08:17:01Z, no confirmation email required.
  4. Deleted the old support@ alias (superseded by mailbox).
  5. Removed investigation-only forwarding from sales@bilko.cloudsales@ is left untouched (forwardings: []).

Verified state (Proveo independent GET):

GET /v1/domains/bilko.cloud/mailboxes/support/forwardings
{"forwardings":[{"address":"alem@alai.no","confirmed_at":"2026-06-10T08:17:01Z","blocked_at":null,"is_active":true}]}

GET /v1/domains/bilko.cloud/mailboxes/sales/forwardings
{"forwardings":[]}

Migadu admin path (for future changes)

To modify forwarding: admin.migadu.com → bilko.cloud → Mailboxes → support → Forwardings. Do not use the Aliases section for external cross-domain targets.


3. Preflight Rollback Script

File: scripts/ops/bilko-support-fix-preflight.sh (committed at 67ed0ce5, PR #317, mode 100755)

What it does

  1. STEP 1 — Cloud SQL backup (write, skipped in dry-run): Takes an on-demand Cloud SQL backup of the Bilko DB before any deploy action. Provides a restore point.
  2. STEP 2 — Capture current Cloud Run revision (read-only always): Records the live revision name and image SHA for both bilko-api-demo and bilko-web-demo.
  3. STEP 3 — Print rollback commands (print only, never executes): Outputs the exact gcloud run services update-traffic commands needed to roll back to the captured revisions. These are echo-wrapped — they are never executed by the script.

How to run

# Dry-run (safe, no writes — use to confirm rollback targets before deploy)
bash scripts/ops/bilko-support-fix-preflight.sh --dry-run

# Live run (takes SQL backup, captures revisions, prints rollback cmds)
bash scripts/ops/bilko-support-fix-preflight.sh

Deploy-fragility rule enforced

All example re-deploy commands in the printed output use --update-secrets. The script documents and enforces: NEVER use --set-env-vars for Bilko Cloud Run deploys — it overwrites the Secret Manager binding and exposes secrets as plaintext environment variables.


4. Known Follow-up

ItemStatusOwner
DEPLOY-MAP.md CF Email Routing section for bilko.cloud is stale (lists Cloudflare; MX is Migadu) Open John / next infra PR
PR #317 bundles MC #103323 application code (Sentry, SupportTickets, DB migrations V71+V72) — confirm separate QA validation for that scope Open (Proveo advisory) John
Merge PR #318 (smoke-test fix) before PR #317, or close #318 as superseded if #317 merges first Open John

Created by Skillforge for MC #103325. Evidence: /tmp/evidence-103325/verification.md + /tmp/alai/p2p-pairing-evidence/proveo-multi-317-318-verdict.md. All facts machine-verified.