- Per-source quietness un-mute — revoke a rev-145 ack before its 7-day TTL expiresOperators occasionally realise that a feed they ack-muted via the rev-145 chip is genuinely critical (e.g. a customer-onboarding RSS feed they're waiting on) and want to revoke the mute *before* the rev-145 7-day TTL expires. Until rev 148 the only path was to wait the window out — rev 148 collapses that to one click. New `unacknowledgeSourceQuietness()` helper sets `source.quietnessAckedAt = null`, writes an activity-log entry, and fires the new rev-148 `source.quietness_unacked` outbound event. New `POST /api/sources/{sourceId}/quietness-unack` route + matching `POST /api/v1/sources/{sourceId}/quietness-unack` v1 mirror in lockstep. New `SourceQuietnessUnackButton` client component mounts directly beside the rev-145 ack-muted chip on every source row whose `quietnessAckedAt` is still inside the 7-day TTL. Brand-color teal palette (vs rev-145 amber) so the two chips read as siblings: amber = 'I see this, mute 7d', teal = 'I changed my mind, un-mute now'. Returns 404 when there's no ack stamp to revoke — no-op un-ack returns 404 rather than silently succeeding so the audit-log noise stays honest.
- source.quietness_unacked outbound event — symmetric closure receipt for the rev-145 ack closureNew `OutboundEvent` value `source.quietness_unacked` + `dispatchSourceQuietnessUnackedWebhook()` dispatcher fires whenever an operator revokes a rev-145 quietness ack. Mirrors the rev-37 `task.unblocked` closure pattern at the un-mute axis. Until rev 148 downstream integrations watching the rev-145 `source.quietness_acked` event saw 'alarm muted' but never 'alarm un-muted' — they'd have to either poll the ack stamp or accept stale closure state. Rev 148 closes the loop. The full per-source quietness lifecycle is now four-axis on every push channel — visibility (rev 144 GET /sources/quietness) → ack (rev 145 source.quietness_warning + source.quietness_acked) → bulk-ack (rev 146 closure receipt fan-out) → un-ack (rev 148 source.quietness_unacked). External integrations (FinOps tool, monitoring dashboard, CRM mirroring source state) can now reconcile detected → acked → silenced → un-muted on every quietness alarm without polling.
- Cross-row drag on the rev-38/124 dependency graph — closes the named rev-124 next-sprint candidateUntil rev 148 the rev-124 drag-to-reorder primitive only worked *within* a single dependent row. An admin who realised 'task A no longer blocks B; it now blocks C' had to remove A from B's blockers via the × button + scroll to C's task card and re-add A from the picker. Rev 148 collapses that to one drag: drag A from row B's blockers and drop it onto row C's blockers area (or onto a specific blocker in row C to insert at that position). The move is atomic at the operator surface but ships as two PUTs (one per affected row); the optimistic override map handles both rows so the UI shows the moved state before either round-trip completes. Self-blocking is silently rejected (you can't drop A onto row A — visualised with a red `is-cross-rejected` border + cursor:not-allowed); duplicate drops fall through to a single source-side remove. New `is-cross-target` row treatment uses the same brand-color outline + soft gradient as the rev-22+ design language so the cross-row drop target reads as a sibling of the rev-124 intra-row reorder accent. Closes the long-outstanding cross-task operator-direction surface at the dependency-graph axis (the rev-124 named candidate has been outstanding for 24 revs).
- OpenAPI 3.1 typed coverage on the rev-148 endpoint — 70th unbroken cadence revCloses the typed-contract gap on the rev-148 v1 endpoint in the same cycle the dashboard primitive ships. The OpenAPI spec types the new `POST /sources/{sourceId}/quietness-unack` endpoint with full response schema (ok + revokedAt date-time + 404 for no-op). The cadence pattern from rev 78 onward (every v1 enhancement gets typed in the OpenAPI 3.1 spec in the same cycle it ships) reaches its 70th unbroken rev with rev 148. The OpenAPI spec changelog header gains a rev-148 block explaining the un-mute axis closure on the per-source quietness lifecycle. The `/api/v1` self-describing endpoint index documents the new endpoint inline + the new outbound event so MCP-host integrators reading the index discover both without opening the spec. The activity log gains a `source_quietness_unacked` glyph (☼) + brand-color teal tint distinct from the rev-145 ack tint so the operator scanning the log can tell mute vs un-mute apart at a glance.