Nightly & Scheduled Suite Audit

PropFlow-Technologies/propflowai · window Jul 18–24, 2026 (7 nights) · compiled Jul 24 ~22:00 MT from GitHub Actions run logs, workflow YAML, and merged-PR history. Four independent audit passes (renewal harness · stress tests · canaries/evals · full workflow inventory + cost). Anything not verifiable from logs is labeled unverified.

TL;DR

Scoreboard — last 7 nights

pass fail cancelled (timeout) didn't run  ·  strips read Jul 18 → Jul 24

SuiteCadenceJul 18–24StateVerdict
Nightly renewal harnessdaily 09:00 UTC●●●●●●GREEN NOWSignal. 6-night red = stale assertions (rot), fixed by #4407. Two open defects inside it (below).
Nightly Stress Testsdaily 08:00 UTC●●●●●●RED / BLACKOUTDegraded to noise. SMS lane dead 5 nights; 2 eval gates failing on fixture errors; 7 real Jul 19 regressions unowned.
Renewal Evals (live multi-turn job)weekly Mon 09:15 UTC●●●●●●RED 10 WEEKSDead gate. Red every scheduled run since ≥May 25. No alert step exists. Top-priority fix.
Nightly suppression canarydaily 09:30 UTC●●●●●●●FIX PENDING CRONNoise (expired prod cookie, not a gate regression). Fix verified by manual run Jul 24 09:08 MT; first unattended cron is tonight.
Nightly EL config driftdaily 06:00 UTC●●●●●GREENHigh-value signal — the Jul 19 red was 15 real duplicate EL workspace tools, cleared within 24h. Producer of the duplicates still unidentified.
CI (nightly full run)daily 07:00 UTC●●●●●●●GREENHealthy backstop.
voice-comprehensivedaily 09:30 UTC●●●●●●●GREENHealthy (live EL simulate-conversation sweep, report-only gate, ~10 job-min).
DLQ alarms refreshdaily 06:00 UTC●●●●●●●GREENHealthy ops guard, 8/8 fires.
Audit runtime secret paritydaily 09:00 UTC●●●●●●●GREENHealthy ops guard, 8/8 fires.
Cache Janitorevery 6 h●●●●●●GREENOne GitHub-API 503 flake (Jul 20). Self-heals. No action.
Intent Evalsweekly Mon 09:00 UTC●●●●●●GREENHealthy (8 of last 9 weekly runs green).
Semgrep SAST / Lambda EOLweekly Mon 13:00 UTC●●●●●●GREENHealthy.
Real-AppFolio renewal canarycron commented out●●●●●●●NEVER ARMED0 successful runs ever. The only harness that drives real AppFolio — dead since creation (May 26).
WO Creation E2E HarnessPR-triggered— skips —VACUOUS GREEN0 of last 30 runs executed the harness (no [preview] deploy → skip → green).
Pipeline Lab EvalsPR-triggered— skips —VACUOUS GREENSame pattern, 0/30 — and still burns npm ci on every run (~505 job-min/wk for nothing).
Voice base-prompt drift guard · Terraform Security Scan●●●●●●●ORPHANEDYAML deleted from main; GitHub still lists them "active". Pure clutter — can never fire.

Renewal Evals — SMS is deliberately PR-only (nightly.yml owns the scheduled sweep) and is green — healthy. The EL sync jobs (specialists / phone numbers / turnover-intake / renewal prompt) are live or on-demand-by-design and green. ADR→Reviewer-Lens cron first fires Aug 1 — not dormant, just not due yet.

The four headline findings

1 · Renewal Evals: a weekly gate that has been red for 10 straight runs, silently

The live-multiturn job (npm run test:eval:renewal, live Claude) has failed on every scheduled run since at least May 25 — 10/10, deterministic, the identical 5 tests each week, all with the same shape: the renewal tool never fires (accept, decline-confirm, term-pick, escalate-handoff, counter-offer). The logs show the fixture tenant resolving with no propertyId, making get_lease_terms / get_tenant_balance fail — a credible reason Clara never reaches the tool. Fixture-vs-real-regression is unsettled — treat "broken fixture" as the leading hypothesis (same class as the #3011 identity-spine fix), not a finding.

Why nobody saw it: PRs run only the promptfoo job (green 12/12), the failing job is schedule-only, and the workflow has no failure-alert step at all — the only one of the failing suites with none. Meanwhile it spends live Claude API money every Monday to produce a red nobody reads.

Fix: reproduce locally → stamp the fixture or confirm the regression; add the standard failure alert; or deschedule deliberately. Runs: 29734634698 (Jul 20), 29242546911, 28786221891, … back to 26401123596 (May 25).

2 · Nightly Stress Tests: 5-night SMS blackout disguised as "cancelled"

Since Jul 20, every SMS-stress scenario polls for a Clara reply for 11 minutes and gets nothing (attempts=214 … lastMatchCount=0 — reply is ABSENT, not late), the job runs into its 120-minute cap, and GitHub reports the run as cancelled — which reads as neutral, not red. Result: zero SMS regression coverage for 5 nights, ~75% of the workflow's ~160 job-min/night spent producing nothing, and no notification (the workflow has none; a comment claims "Agent Smith surfaces failures" — Smith has been down).

Root cause of the blackout is undiagnosed. Two candidates: the harness polls /api/conversations?limit=100 and filters client-side while the test property carries 1,351+ conversations (saturated window → replies exist but fall outside it), or the inbound pipeline genuinely broke ~Jul 20 (~40 PRs merged Jul 21 alone). Cheapest discriminator: post one synthetic inbound, then query DynamoDB directly for the conversation.

The last healthy run (Sun Jul 19 full sweep) proved the suite still earns its keep: it flagged 7 real failures — 6 in the reschedule-tour cluster (reschedule_tour never called; Clara books instead) — with clean quarantine hygiene and owners on older items. Those 7 are still unaddressed.

Two weekday eval gates are also red for the wrong reason: triage-routing 89.19% vs 93% and prospect-comms 60% vs 79% both show 0 failed / N errors — broken fixtures (the yml's own comments admit sibling: "maintenance" is stale post-split), counted as quality failures. These are the same two "unowned eval regressions" tracked in the CI-cost thread — reclassified by this audit as harness rot, not model regressions (suspect commit 0897bcb48 likely off the hook, unverified).

3 · Vacuous green: four checks that pass while verifying nothing

4 · Platform risks surfaced in passing (independent of any one suite)

What the 6-night renewal-harness streak actually was

PR #4054 (merged Jul 18 00:52 UTC) intentionally changed outreach-exhaustion to wrap up quietly instead of escalating to a PM (ADR-0100). Four matrix cells still asserted outcome='escalated' — one sibling cell in the same file was updated, these four were missed. Every red night (Jul 18–23) was the byte-identical 4-cell set, outcome=null, expected escalated; zero product regressions, zero flakes. PR #4407 (Jul 23, harness-only) aligned the cells and went green 57 seconds after merge; Jul 24's scheduled run: 22/23 pass.

The uncomfortable part: all six failure emails were delivered (SendGrid 202 to fede+ops and gera) and not acted on for ~6 days — #4407's own body says so. It added transition-based alert suppression (first-red + weekly reminder instead of nightly repeats), which is the right shape but unproven — it hasn't seen a real red yet.

Lesson encoded: when a PR intentionally changes a terminal behavior, the same PR must sweep harness assertions that encode the old behavior (#4054 updated 1 of 5 dependent cells). And repeat-identical alerts stop working after ~day 2.

Cost picture (7 days, estimated job-minutes)

GitHub's timing API returns 0 billable ms for every run sampled (why: unverified), so these are wall-clock job-minute estimates — rank-order reliable, ±30% on magnitude. 18,121 runs total across the week.

WorkflowRuns / 7dEst. job-minNote
CI1,502~17,650Dominates everything; the build-time regression (11–13 min) compounds here.
Claude Code Review818~6,340
Deploy Renewal Worker (Fargate)345~4,830Highest per-run deploy cost.
Deploy Lambdas252~3,906Softest estimate (matrix fan-out varies 3–46 min).
Nested Package CI1,308~2,616
Lambda bundle check591~1,773Working as intended (replaced 7× deploy fan-out).
Semgrep SAST864~1,512
CI (docs)362~1,4484 jobs per docs PR — trim candidate.
Turnover Intake Regression Harness566~1,274566 npm ci runs to execute 4 vitest files; path filter is effectively "every PR". Trim candidate.
Nightly Stress Tests7~1,127Most expensive per-run (~161 job-min); 75% currently wasted on the dead SMS lane. Plus ≈$55–60/mo metered Anthropic.
voice-comprehensive123~984
Pipeline Lab Evals202~505100% of it produces nothing (vacuous-green pattern).

Recommendations, ranked

#ActionWhy / payoffEffort
P0Settle the Renewal Evals red. Run npm run test:eval:renewal locally; if the fixture tenant lacks propertyId, stamp it (one-liner, #3011 class). If it's real, it's a 2-month-old Clara renewal-tool regression and jumps to the top of everything.Ends a 10-week dead gate; answers whether prod renewal tool-calling regressed.S
P0Standard alert block on every scheduled suite: if: failure() || cancelled() → one plain-English message to #alerts (plus the existing email where present). Renewal Evals and Nightly Stress currently have nothing.The audit's central lesson: alerted suites healed in ~1 day; unalerted ones rotted 5 nights–10 weeks. cancelled() matters — that's how the SMS blackout hid.S
P0Stop the stress-test bleed: drop sms-stress timeout 120→45 min + fail-fast after 3 consecutive no-reply scenarios; then diagnose the blackout (synthetic inbound → query DynamoDB directly, bypassing /api/conversations?limit=100).Recovers ~75% of the nightly's compute; restores the SMS lane that caught 7 real regressions on Jul 19.S–M
P1Re-triage the Jul 19 reschedule-tour cluster (6× reschedule_tour not called + 1). Real failures, 5 days old, unowned.Only confirmed live product signal in the whole window.M
P1Fix the errors-vs-failures eval gate: repair stale fixtures (sibling: "maintenance" post-split) and/or make check-eval-rate.mjs report fixture errors as broken-fixture, not quality-regression.Clears the standing "triage-routing 89% / prospect-comms 60%" reds — which this audit reclassifies as rot, not regressions.S
P1Un-vacuous the prod-history-replay job: zero exports must warn loudly or fail; fix the Temporal workflow-list error that has made it replay nothing since birth.Turns a fake canary into a real one for prod renewal replay.S–M
P1Suppression-canary cookie longevity: service credential for the reinstate route, or a weekly cookie-expiry preflight that warns before the canary reds.Second manual refresh cycle already; each expiry silently drops 1–2 nights of prod compliance coverage.M
P1Wire isStressSender() into the outbound dispatcher (platform fix, separate PR).The documented real-Twilio guard for synthetic traffic currently doesn't exist; live again once SMS-stress heals.S
P2Decide the Real-AppFolio canary: seed SSM + GH_REPO_READ_TOKEN, prove one dispatch run, arm the cron — or delete it. It's the only real-AppFolio harness and has never once succeeded.Deliberate state beats permanent limbo.M
P2Cleanup: deregister the two orphaned workflows (voice drift guard, tfsec); short-circuit Pipeline Lab's npm ci before preview-resolve; tighten Turnover Intake's src/lib/** path filter; trim CI (docs) jobs; move nightly evals off the metered Anthropic key per the standing rule.~2,000+ job-min/week and $55–60/mo, plus less dashboard clutter.S each

Overnight confirmations (Jul 25 crons — resolved)

Sources: run logs via gh run view --log-failed for every red cited; workflow YAML on main; merged-PR history (#4054, #4407, #4369, #3011 class); 43-window run inventory (18,121 runs, Jul 18–25). Claims that could not be verified from these are explicitly marked unverified — nothing here is inferred silently.

PropFlow Docs