Skip to content

Form Monitor

Form Monitor #5

Workflow file for this run

name: Form Monitor
# Weekly production check that the public lead-capture forms on scanopy.net
# actually submit to Brevo and show their success state. These forms are the
# inbound channel for the highest-value deals and have silently broken before
# (June 2026: /commercial "Request a Quote" did nothing on click).
# See README "Form Monitoring" for the sentinel convention and coverage.
on:
schedule:
- cron: '0 6 * * 1' # Mondays 06:00 UTC
workflow_dispatch:
permissions:
contents: read
issues: write
jobs:
monitor:
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: .nvmrc
# --ignore-scripts skips the heavy docs-site postinstall; the E2E suite
# only needs @playwright/test.
- name: Install dependencies
run: npm ci --ignore-scripts
- name: Install Playwright browser
run: npx playwright install --with-deps chromium
- name: Run form monitor suite against production
run: npx playwright test
- name: Upload Playwright report
if: always()
uses: actions/upload-artifact@v4
with:
name: playwright-report
path: playwright-report/
retention-days: 30
- name: Create or update alert issue
if: failure()
uses: actions/github-script@v7
with:
script: |
const label = 'form-monitor';
const title = 'Form monitor: production lead form check failed';
const runUrl = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`;
try {
await github.rest.issues.getLabel({ ...context.repo, name: label });
} catch {
await github.rest.issues.createLabel({
...context.repo,
name: label,
color: 'd73a4a',
description: 'Automated production form monitoring alerts',
});
}
const { data: open } = await github.rest.issues.listForRepo({
...context.repo,
state: 'open',
labels: label,
});
if (open.length > 0) {
await github.rest.issues.createComment({
...context.repo,
issue_number: open[0].number,
body: `Still failing as of the latest run: ${runUrl}`,
});
} else {
await github.rest.issues.create({
...context.repo,
title,
labels: [label],
body: [
`The weekly production form check failed: ${runUrl}`,
'',
'A lead-capture form on scanopy.net may be silently broken — a prospect',
'filling it out could be getting no response. Treat as revenue-impacting.',
'',
'The Playwright report (screenshots + traces) is attached to the run as an artifact.',
'Failure messages decode as follows:',
'',
'- `FORM DID NOT SUBMIT` — clicking submit fired no request to Brevo.',
' Broken page JS or unwired handler (the June 2026 failure mode).',
'- `BREVO HTTP ERROR <status>` — the request fired but Brevo returned an error.',
' Check whether the Brevo form still exists / the endpoint URL changed.',
'- `BREVO RESPONSE NOT JSON` — endpoint is not behaving like a Brevo form',
' (redirect, HTML error page, moved URL).',
'- `BREVO REJECTED SUBMISSION` — 2xx but `success:false`; body included in the message.',
'- A success-UI assertion failure — Brevo accepted the data but the user-visible',
' confirmation never appeared.',
'',
'See the "Form Monitoring" section of the README.',
].join('\n'),
});
}