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.
propertyId → lease/balance lookups fail) — hypothesis, not confirmed. Either way the gate protects nothing and burns live Claude spend every Monday.● pass ● fail ● cancelled (timeout) ● didn't run · strips read Jul 18 → Jul 24
| Suite | Cadence | Jul 18–24 | State | Verdict |
|---|---|---|---|---|
| Nightly renewal harness | daily 09:00 UTC | ●●●●●●● | GREEN NOW | Signal. 6-night red = stale assertions (rot), fixed by #4407. Two open defects inside it (below). |
| Nightly Stress Tests | daily 08:00 UTC | ●●●●●●● | RED / BLACKOUT | Degraded 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 WEEKS | Dead gate. Red every scheduled run since ≥May 25. No alert step exists. Top-priority fix. |
| Nightly suppression canary | daily 09:30 UTC | ●●●●●●● | FIX PENDING CRON | Noise (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 drift | daily 06:00 UTC | ●●●●●●● | GREEN | High-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 | ●●●●●●● | GREEN | Healthy backstop. |
| voice-comprehensive | daily 09:30 UTC | ●●●●●●● | GREEN | Healthy (live EL simulate-conversation sweep, report-only gate, ~10 job-min). |
| DLQ alarms refresh | daily 06:00 UTC | ●●●●●●● | GREEN | Healthy ops guard, 8/8 fires. |
| Audit runtime secret parity | daily 09:00 UTC | ●●●●●●● | GREEN | Healthy ops guard, 8/8 fires. |
| Cache Janitor | every 6 h | ●●●●●●● | GREEN | One GitHub-API 503 flake (Jul 20). Self-heals. No action. |
| Intent Evals | weekly Mon 09:00 UTC | ●●●●●●● | GREEN | Healthy (8 of last 9 weekly runs green). |
| Semgrep SAST / Lambda EOL | weekly Mon 13:00 UTC | ●●●●●●● | GREEN | Healthy. |
| Real-AppFolio renewal canary | cron commented out | ●●●●●●● | NEVER ARMED | 0 successful runs ever. The only harness that drives real AppFolio — dead since creation (May 26). |
| WO Creation E2E Harness | PR-triggered | — skips — | VACUOUS GREEN | 0 of last 30 runs executed the harness (no [preview] deploy → skip → green). |
| Pipeline Lab Evals | PR-triggered | — skips — | VACUOUS GREEN | Same 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 | — | ●●●●●●● | ORPHANED | YAML 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 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).
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).
workflow list failed … nothing exported (fail-soft) then passes having replayed zero histories. Likely a Temporal list-permission/namespace issue. A canary that greens on zero inputs is worse than no canary.[preview] — so 0 of the last 30 runs of each executed anything. By design (hard-failing normal PRs was judged worse), but the effect is a green check with zero signal; Pipeline Lab still runs npm ci 200×/week first.cancelled too.isStressSender() has zero callers. Its docstring says it's what keeps outbound Clara replies to stress synthetics off real Twilio. It is not wired up anywhere, and the CRUCIBLE outbound guard explicitly stands down in prod (VERCEL_ENV === 'production' → return false). Outbound replies to the +1-206-555-xxxx synthetics therefore reach client.messages.create(); the 555 exchange is non-routable so Twilio most likely rejects unbilled — unverified against Twilio. Moot during the blackout (no replies at all), live again the moment SMS-stress heals.NIGHTLY_ANTHROPIC_API_KEY, self-documented ≈$55–60/mo) — at odds with the standing subscription-token-only rule (the PR #4170 precedent).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.
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.
| Workflow | Runs / 7d | Est. job-min | Note |
|---|---|---|---|
| CI | 1,502 | ~17,650 | Dominates everything; the build-time regression (11–13 min) compounds here. |
| Claude Code Review | 818 | ~6,340 | |
| Deploy Renewal Worker (Fargate) | 345 | ~4,830 | Highest per-run deploy cost. |
| Deploy Lambdas | 252 | ~3,906 | Softest estimate (matrix fan-out varies 3–46 min). |
| Nested Package CI | 1,308 | ~2,616 | |
| Lambda bundle check | 591 | ~1,773 | Working as intended (replaced 7× deploy fan-out). |
| Semgrep SAST | 864 | ~1,512 | |
| CI (docs) | 362 | ~1,448 | 4 jobs per docs PR — trim candidate. |
| Turnover Intake Regression Harness | 566 | ~1,274 | 566 npm ci runs to execute 4 vitest files; path filter is effectively "every PR". Trim candidate. |
| Nightly Stress Tests | 7 | ~1,127 | Most expensive per-run (~161 job-min); 75% currently wasted on the dead SMS lane. Plus ≈$55–60/mo metered Anthropic. |
| voice-comprehensive | 123 | ~984 | |
| Pipeline Lab Evals | 202 | ~505 | 100% of it produces nothing (vacuous-green pattern). |
| # | Action | Why / payoff | Effort |
|---|---|---|---|
| P0 | Settle 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 |
| P0 | Standard 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 |
| P0 | Stop 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 |
| P1 | Re-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 |
| P1 | Fix 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 |
| P1 | Un-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 |
| P1 | Suppression-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 |
| P1 | Wire 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 |
| P2 | Decide 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 |
| P2 | Cleanup: 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 |
workflow list failed … nothing exported (fail-soft) again, so the P1 fix stands.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.