6 · Integrations
Objective — surface the integration choices — Slack notifications for errors/deploys/backups, GitHub project management (labels, issue forms, project board, PR template), and privacy-first Rybbit analytics — so operations and feedback loops are wired before users arrive.
Steps at a glance:
- Wire Slack notifications — Automated Slack alerts for Sentry errors, deployments, and backup status.
- Set up project management — Set up GitHub Issues, typed labels, YAML issue forms, a project board, and a PR template.
- Set up Rybbit analytics — Privacy-first, cookieless analytics — session replay, funnels, goals, web vitals, and error tracking with no consent banner.
Background
Section titled “Background”These are valuable integrations, not mystery work to silently skip. Treat each row as adopt or evidence-backed N/A: configure it now when Zaj-PROJECT.md shows the account/tool is adopted or the setup is free and routine; mark N/A only when the project has no license, no account, no token scope, or an explicit operator decision not to adopt it. Every N/A or blocked integration gets a Zaj-BACKLOG.md row with owner, trigger, and acceptance criterion.
flowchart LR Sentry["Sentry errors"] --> Alerts["#app-alerts"] Deployer["Deployer"] --> Deploys["#app-deploys"] Backup["Backups"] --> Backups["#app-backups"] Alerts --> Slack(["Slack workspace"]) Deploys --> Slack Backups --> Slack1. Wire Slack notifications
Section titled “1. Wire Slack notifications”Automated Slack alerts for Sentry errors, deployments, and backup status.
-
Set up the workspace (first project only). If you don’t have a workspace, create one at slack.com/create. Create an incoming-webhook app: api.slack.com/apps → Create New App → From scratch → name
<Org> Notifications→ Incoming Webhooks → Activate ON → Add New Webhook to Workspace → pick a channel → copy the URL into 1Password. Store the webhook URL, workspace, app name, and creation date in your password manager; never paste the live URL into docs, commits, screenshots, or chat.- ✅ The workspace + incoming-webhook app exist, URL stored in the shared infra vault for this account-wide credential.
-
Create the project channels & webhooks — one channel per concern, then a channel-specific webhook for each.
Channel Purpose #<app>-alertsSentry errors and critical alerts #<app>-deploysDeployment notifications #<app>-backupsBackup success/failure (optional) Add deploy webhook URLs to
~/Admin-Server/config/server.envby default. Use projectshared/.envonly when this one app needs its own override:Terminal window ## === SLACK NOTIFICATIONS ===SLACK_ALERTS_WEBHOOK=<your-slack-alerts-webhook-url>SLACK_WEBHOOK_DEPLOYS=<your-slack-deploys-webhook-url>SLACK_BACKUPS_WEBHOOK=<your-slack-backups-webhook-url># Expected: real deploy webhook in server.env; project override only when needed- ✅ Per-concern channels exist with webhook URLs wired into
server.envor a deliberate project override.
- ✅ Per-concern channels exist with webhook URLs wired into
-
Wire the integrations.
-
Sentry → Slack (requires Sentry Team plan) — Settings → Integrations → Slack → authorize, then Alerts → Create Alert Rule (
> 0 errors in 1 min, filterproduction, action: send to#<app>-alerts). On the free plan, capture the paid-plan trigger and acceptance criterion inZaj-BACKLOG.md. -
Deployer → Slack — add success/failure tasks to
deploy.php:use Deployer\Utility\Httpie;task('slack:notify:success', function () {$webhookUrl = getenv('SLACK_WEBHOOK_DEPLOYS');if (!$webhookUrl) { writeln('<comment>SLACK_WEBHOOK_DEPLOYS not set - skipping</comment>'); return; }$app = get('application', 'App');$stage = get('labels')['stage'] ?? 'unknown';$branch = run('git rev-parse --abbrev-ref HEAD', ['timeout' => 5])->toString();$commit = run('git log -1 --oneline', ['timeout' => 5])->toString();Httpie::post($webhookUrl)->body(['text' => "*{$app}* deployed to *{$stage}*\nBranch: `{$branch}`\nCommit: {$commit}",'username' => 'Deployer', 'icon_emoji' => ':rocket:',])->send();})->desc('Send deploy success notification to Slack');task('slack:notify:failure', function () {$webhookUrl = getenv('SLACK_WEBHOOK_DEPLOYS');if (!$webhookUrl) { return; }$app = get('application', 'App');$stage = get('labels')['stage'] ?? 'unknown';Httpie::post($webhookUrl)->body(['text' => "*{$app}* deployment to *{$stage}* failed\nCheck logs for details.",'username' => 'Deployer', 'icon_emoji' => ':x:',])->send();})->desc('Send deploy failure notification to Slack');after('deploy:success', 'slack:notify:success');after('deploy:failed', 'slack:notify:failure'); -
Backups → Slack — easiest is to rely on Sentry Cron Monitors (no extra config). Otherwise add a
slackblock toconfig/backup.php’snotificationswith'webhook_url' => env('SLACK_BACKUPS_WEBHOOK'). -
✅ The Sentry, Deployer, and backup notification paths are wired, or each unavailable paid/provider path has a
Zaj-BACKLOG.mdrow with owner, trigger, and acceptance criterion.
-
-
Test the webhook.
Terminal window set -a && source .env && set +a # or export each SLACK_*_WEBHOOK you testcurl -X POST -H 'Content-type: application/json' \--data '{"text":"Slack webhook test successful!"}' \"$SLACK_ALERTS_WEBHOOK"# Expected: "ok" and the message appears in the channelThen run
dep deploy "$DEPLOY_TARGET"for the selected non-production target fromZaj-PROJECT.mdand confirm the notification lands in#<app>-deploys. Record the production webhookop://pointers in the production env template/vault entry now; Phase 12 renders/uploads production.envand clears the production config cache during the production deploy.- ✅ The test
curlreturnsokand a real deploy notifies#<app>-deploys.
- ✅ The test
2. Set up project management
Section titled “2. Set up project management”Set up GitHub Issues, typed labels, YAML issue forms, a project board, and a PR template.
-
Create the typed labels. At
github.com/<org>/<repo>/labels: delete the redundant defaults (bug,documentation,enhancement,question— replaced by typed labels), keepduplicate/good first issue/help wanted/invalid/wontfix, then create four typed families (~25 labels total).Family Labels (color) Priority priority: critical#b60205,high#d93f0b,medium#fbca04,low#0e8a16Type type: bug#d73a4a,feature#a2eeef,enhancement#7057ff,docs#0075ca,refactor#f9d0c4,security#ee0701,question#d876e3Status status: triage#cfd3d7,confirmed#1d76db,in-progress#5319e7,blocked#e99695,needs-info#d876e3Area area: frontend#bfd4f2,backend#b4a7d6,database#c5def5,devops#d4c5f9(+ optionalauth/payments/api)- ✅ The four typed label families exist.
-
Create the issue forms under
.github/ISSUE_TEMPLATE/. Examplebug_report.yml:name: Bug Reportdescription: Report a bug or unexpected behaviortitle: "[BUG] "labels: ["type: bug", "status: triage"]body:- type: textareaid: descriptionattributes: { label: Bug Description }validations: { required: true }- type: textareaid: stepsattributes: { label: Steps to Reproduce }validations: { required: true }- type: textareaid: expectedattributes: { label: Expected Behavior }validations: { required: true }- type: dropdownid: severityattributes:label: Severityoptions:- Critical (app unusable, data loss)- High (major feature broken)- Medium (workaround exists)- Low (minor inconvenience)validations: { required: true }- type: checkboxesid: checklistattributes:label: Pre-submission Checklistoptions:- label: I searched existing issues to ensure this is not a duplicaterequired: trueAdd
feature_request.yml(problem statement, proposed solution, importance dropdown) andtask.yml(description, acceptance criteria, area dropdown) on the same pattern, plusconfig.ymlto disable blank issues and route security reports privately:blank_issues_enabled: falsecontact_links:- name: Documentationurl: https://docs.<domain>about: Check the docs before opening an issue- name: Security Vulnerabilityurl: https://github.com/<org>/<repo>/security/advisories/newabout: Report security vulnerabilities privately- ✅ Bug/feature/task issue forms +
config.ymlexist under.github/ISSUE_TEMPLATE/.
- ✅ Bug/feature/task issue forms +
-
Create the project board. Organization → Projects → New project → Kanban → name
<App> Development; Status field: Backlog, Triage, Todo, In Progress, In Review, Done; custom fieldsPriority(Critical/High/Medium/Low), optionalEstimate(XS–XL); workflows item added → Backlog, reopened → Todo, closed / PR merged → Done, Enable Auto-add with filteris:issue is:open; link it:gh project link <number> --owner <owner> --repo <owner>/<repo>.- ✅ The Kanban board exists, is linked, and auto-adds open issues.
-
Create the PR template & discussions. Create
.github/PULL_REQUEST_TEMPLATE.mdwith sections: Description, Type of Change, Related Issues, Changes Made, Screenshots, Testing Instructions, Checklist, Deployment Notes. Optionally enable Discussions (Settings → General → Features). Commit.github/once done.- ✅ The PR template is committed and Discussions are enabled if wanted.
3. Set up Rybbit analytics
Section titled “3. Set up Rybbit analytics”Privacy-first, cookieless analytics — session replay, funnels, goals, web vitals, and error tracking with no consent banner. A complement to (not replacement for) Google Analytics.
-
Activate & add the site. app.rybbit.io/redeem → redeem license → Dashboard → Sites → Add Website (domain, no
https://) → set Public Analytics Off, User ID Salting On → copy the Site ID.- ✅ The site is added and its Site ID is copied.
-
Configure the site settings — Block Bot Traffic On, Track IP Address Off, Web Vitals On, Error Tracking On, Track Outbound Links On, Track SPA Navigation On; connect Google Search Console.
- ✅ The site settings match the privacy-first profile.
-
Install the tracking script — find layout files with a
<head>and add the tracking script (ideally a sharedpartials/analytics.blade.php).Terminal window grep -r "<!DOCTYPE html" resources/views/ packages/ --include="*.blade.php" -l# Expected: the layout files with a <head> to inject the script.env RYBBIT_SITE_ID=<your-site-id>config/services.php 'rybbit' => ['site_id' => env('RYBBIT_SITE_ID')],- ✅ The tracking script is wired via a shared partial and the Site ID is in env/config.
-
Set skip & mask patterns — Dashboard → Settings → Tracking Script: skip
/admin/**,/setting/**; mask/users/*/profile.- ✅ Admin paths are skipped and sensitive paths masked.
-
Enable the dashboard features — Performance (Web Vitals); Session Replay On with Masking + Mask All Inputs On; Error Tracking On.
- ✅ Performance, masked session replay, and error tracking are on.
-
Wire custom events & identity.
<button data-rybbit-event="signup_clicked">Sign Up</button>window.rybbit.event("plan_purchased", { plan: "pro", interval: "yearly" });// inside an @auth block:window.rybbit.identify("{{ auth()->id() }}");- ✅ Custom events and authenticated-user identity are wired.
-
Create goals & funnels — goals (Signup Completed, Pricing Page Viewed, Trial Started) and a funnel (
/→/pricing→/register→ event).- ✅ Goals and a conversion funnel exist.
-
Deploy & verify. Deploy, set
RYBBIT_SITE_IDin production,php artisan config:clear && php artisan view:clear, then visit the site in incognito and confirm hits in the Rybbit real-time dashboard. Store the Site ID, account email, dashboard URL, tier, and any API key in your password manager.- ✅ Real-time hits appear in the Rybbit dashboard after deploy.
Checklist
Section titled “Checklist”Do not mark this step done until every box below is checked.
- 🔀 Slack webhook test posts — Deployer notifies
#<app>-deploys, or the unavailable/waived integration has aZaj-BACKLOG.mdrow with owner, trigger, and acceptance criterion. - 🤖 GitHub labels, issue forms, project board, PR template — in place; only token-scope or policy blockers may move this to
Zaj-BACKLOG.md. - 🔀 Rybbit live and recording hits — admin paths skipped and inputs masked, or a no-license/no-adoption row is tracked in
Zaj-BACKLOG.md. - 🔀 Every N/A or blocker was accounted for — with evidence in
Zaj-BACKLOG.md, not a silent skip.