Gap
PR #4250 routes owner-triggered evaluate_agent_quality runs to canonical compliance state, so the public registry reflects the verdict immediately. But the badge fan-out (`processAgentBadges` in `compliance-heartbeat.ts:131-219`) is heartbeat-coupled — owner_test runs deliberately skip it to avoid notification spam.
Effect: an owner who just fixed compliance has to wait up to 1 hour for the next heartbeat tick to re-issue their AAO Verified badge. The compliance status flips to passing in real time; the badge does not.
Fix shape
Extract the badge-issuance block from `compliance-heartbeat.ts` into a shared helper (e.g. `services/badge-issuance.ts` already exists — add a `processBadgesForCompletedRun()` wrapper there). Call from:
The Slack-spam concern was about `notifyComplianceChange` and `notifyVerificationChange` — both fire DMs/channels. Badge processing itself is internal state issuance/revocation; firing it without notifications is the correct split.
Acceptance criteria
Why this needs to ship soon
Owners iterating on compliance fixes see a confusing UX: dashboard says "passing" but their public badge still says "untested." The 1h gap teaches developers to trust the badge less than the status, undermining the whole verification pipeline.
Discovered
PR #4250 review iteration (#4250). Deferred as out-of-scope for that PR because the heartbeat fan-out is ~70 lines and extracting/sharing is its own refactor.
Related
Gap
PR #4250 routes owner-triggered
evaluate_agent_qualityruns to canonical compliance state, so the public registry reflects the verdict immediately. But the badge fan-out (`processAgentBadges` in `compliance-heartbeat.ts:131-219`) is heartbeat-coupled — owner_test runs deliberately skip it to avoid notification spam.Effect: an owner who just fixed compliance has to wait up to 1 hour for the next heartbeat tick to re-issue their AAO Verified badge. The compliance status flips to passing in real time; the badge does not.
Fix shape
Extract the badge-issuance block from `compliance-heartbeat.ts` into a shared helper (e.g. `services/badge-issuance.ts` already exists — add a `processBadgesForCompletedRun()` wrapper there). Call from:
The Slack-spam concern was about `notifyComplianceChange` and `notifyVerificationChange` — both fire DMs/channels. Badge processing itself is internal state issuance/revocation; firing it without notifications is the correct split.
Acceptance criteria
Why this needs to ship soon
Owners iterating on compliance fixes see a confusing UX: dashboard says "passing" but their public badge still says "untested." The 1h gap teaches developers to trust the badge less than the status, undermining the whole verification pipeline.
Discovered
PR #4250 review iteration (#4250). Deferred as out-of-scope for that PR because the heartbeat fan-out is ~70 lines and extracting/sharing is its own refactor.
Related