How do you stay aware of what your AI coding agents are doing?
I've been running Claude Code, Cursor, and Codex pretty heavily for the last few months and I keep hitting the same loop:
1. Start a task in one agent
2. Switch to something else (Slack, Twitter, another terminal)
3. Come back 30-40 minutes later
4. Agent finished 35 minutes ago. Or worse, it's been waiting for my approval the entire time.
The more agents I run, the worse it gets. There's no unified way to know what's happening across them.
Curious what other people's setups look like:
- Do you just keep terminals visible and check manually?
- Built any custom notification scripts?
- Use something like ntfy or Pushover?
- Just... accept the wasted time?
I've been building something in this space (push notifications + approval flows for AI agents) and I'm trying to understand if everyone's workflow is as janky as mine, or if some of you have figured out something clever.
Would love to hear what's working and what's not.


Replies
we went with a persistent feed panel — the loop agent posts status lines into it via a /notify endpoint as it works, so you can glance from any page without switching to a terminal. works well when you're at the desk. your push approach clearly wins when you're away. what's the latency on the approval ping — does it reach you fast enough before the agent times out waiting?
Pushary
@sabber_ahamed
Direct answer to the latency question first, since it's the real one: the ping itself is near-instant, the agent hits a blocked state and the push is out in a second or two, that part's easy. The actual constraint isn't delivery speed, it's the agent's timeout window, and the honest answer is that "before it times out" only works if the agent is configured to wait indefinitely on a blocked approval rather than auto-proceeding or failing after N seconds. A push that arrives in two seconds is useless if the agent gave up after thirty and the human takes five minutes to surface from something else. So the real design isn't "make the ping faster than the timeout," it's "remove the timeout from the equation", the agent parks and holds, blocked, until an answer comes back, however long that takes. The ping's job is speed; the hold is what makes speed not matter.
Which is why the hook quality matters more than the network latency. If you're intercepting the agent's native approval event (Codex exposes this cleanly), the agent is genuinely paused waiting for a response, not on a countdown, so there's no race against a timeout at all. The failure mode you're worried about, ping arrives after the agent already moved on, comes from scraping or inferring the block rather than catching the real pause, because then you're reacting to a state the agent doesn't consider blocking. Catch the real approval event and the timeout question mostly dissolves.
Your persistent feed panel is the right call for the at-desk case, and I think we've independently drawn the same line you did: your /notify feed is glanceable-while-present, the push is for when you're away, and those genuinely are complementary rather than competing. The feed panel actually has one advantage push doesn't, it's ambient, you catch it in peripheral vision without an interruption, which is strictly nicer than a notification when you're already at the machine. Push is overkill six inches from the screen. So the ideal is probably both surfaces fed by the same state events, feed when present, push when absent.
Genuine question back, since your feed is already taking structured status lines: do those lines include the blocked-waiting-for-approval state, or mostly progress and done? Because if your /notify endpoint is already receiving "I'm blocked," you're one route away from that same event firing a push for the away case, and the interesting bit is whether your agent emits blocked as a distinct status or just goes quiet, which is the exact detection seam I keep hitting.
@aadilghani yes — blocked-waiting-for-approval is a distinct event in the feed, tagged with its own kind marker so it's visually different from progress lines. the agent pushes it explicitly when it needs input, not inferred. so the event is there and cleanly identifiable. the "goes quiet" failure mode you described is the real edge case — if the agent bypasses the notify call entirely it just disappears, which is why the hook level is ultimately more reliable than application-level signaling for that detection seam.
Pushary
@sabber_ahamed
That's the whole thing right there, and you've correctly identified why your setup is one step away from bulletproof rather than actually bulletproof. Blocked being an explicit tagged event the agent pushes, not inferred from silence, means you've solved the detection problem in the happy path, the agent tells you, you don't guess. That's already better than most of what's in this thread.
But the failure mode you named is the exact one that matters, and it's subtle: application-level signaling can only report the states the agent remembers to report. If the agent bypasses the notify call, errors before it, or stalls in a way that never reaches the /notify line, it just vanishes, and a vanished agent looks identical to a working one. The gap isn't in your event schema, it's that the schema depends on the agent's cooperation, and the moment you most need the blocked signal, something went wrong, is exactly when you can least trust the agent to have fired it.
Which is why you landed on the right conclusion: the hook level is more reliable because it's outside the agent's control flow. A hook fires because the runtime is about to do a thing, whether or not the agent's logic remembered to announce it. Application-signaling trusts the agent to narrate itself; hook-level observes the agent from a layer it can't skip. For the happy path they're equivalent, and for the silent-failure edge case, only the hook survives, because it doesn't ask permission from the code that might be broken.
The honest tradeoff, and the reason I've ended up hook-first on the tools that expose them: hooks are per-runtime and a pain, you get a clean one from Codex, a partial one from Cursor, so you're maintaining N adapters instead of one notify contract. Application-level is portable and easy and lies to you at the worst moment; hook-level is fragmented and annoying and tells the truth. Curious whether, for your feed, you'd consider a belt-and-suspenders setup, keep the explicit notify for the rich payload, add a hook purely as the liveness backstop that catches the disappearances, or whether the per-runtime cost isn't worth it for your use case.
For me, it's not just knowing when an agent finishes it's knowing what happened while I was away.
By the time I come back, I've often lost the reasoning, decisions, or useful context from the session, especially if I'm jumping between ChatGPT, Claude, Cursor, and Codex. Instead of only tracking notifications, I want preserve the conversations and context across different AI tools (including temporary chats), so I can pick up where I left off without relying on each tool's own history.
Pushary
@aimemoryvault
You've named the second half of the problem, and it's the one that survives even after notifications are solved. Knowing when an agent finished gets you back to the desk. It does nothing for the fact that the reasoning, the decisions, the why behind what happened evaporated while you were gone. The notification fixes timing. It doesn't fix amnesia. You can be pinged the instant something finishes and still land in a session with no memory of how it got there.
And the cross-tool angle is what makes it genuinely hard, because you've spotted that context doesn't just evaporate over time, it fragments across tools. Each of ChatGPT, Claude, Cursor, Codex keeps its own history in its own silo, so even the memory that does persist is scattered into four places that don't talk to each other. Your context isn't one thread you lost, it's four half-threads in four apps, and reconstructing what actually happened means stitching them back together by hand. Relying on each tool's own history means there's no your history, only theirs, partial and locked in.
The distinction I'd draw, because it's where this splits from the notification problem: what you're describing is a memory-and-continuity layer, and it's a different substrate than a state-and-approval layer. One is "tap me when I'm needed," yours is "let me resume as if I never left." They're complementary, not the same product, a ping with no preserved context just teleports you back into a wall of scrollback, and preserved context with no ping means you never know when to come back. The complete thing is both, but they're genuinely different builds, and the memory one is arguably the deeper problem because it's about reconstructing meaning, not just detecting events.
The hard part I keep hitting on the memory side, and I'd ask since you're clearly building toward it: capturing the conversations across tools is the tractable half, but unifying them is the hard half, because the same entity gets referred to differently in each tool, and a decision made in Claude that a Cursor session later depends on has no shared id linking them. So how are you thinking about correlation, do you unify on the actual content, on time ordering, on the project, or something the user tags? Because a pile of preserved chats from four tools is only continuity if you can reconstruct which context feeds which, and that threading across silos is the thing I can't see a clean answer to. Curious where you've landed, since it's the exact problem that decides whether it's a memory vault or a memory timeline.
I just stalk them to be honest. I have 4 agents running at one, keep an eye on them, and review their code / actions while they execute it.
I also make sure the AI learns from its mistakes, and puts it in the Analyse knowledge base!
Pushary
@wesley_breukers
Stalking four agents in real time and reviewing their code as it executes is the highest-control version of this, and for four agents it genuinely works, you never hit the silent-wait problem because you're watching, and you catch the confidently-wrong changes at the moment they happen instead of three commits later. If you've got the attention for it, live review beats any notification, because nothing gets past you in the first place.
The tradeoff you're paying, and you clearly know it, is that you are the scheduler. Four agents that all have to queue behind your eyes means your attention is the throughput cap, and the moment you'd want a fifth or sixth, the stalking stops scaling, because you can't watch six things execute at once. Works beautifully right up to the number of agents you can personally hold in your head.
The part I actually want to poke at is the Analyse knowledge base, because that's the rarer and harder thing. Making the AI learn from its mistakes and persist them is exactly the "stop being a perpetually-new employee" problem most people never touch, everyone re-teaches through the prompt every session. The hard bit isn't writing the lesson down, it's trusting it later: a mistake-turned-lesson can be captured wrong, or be right once and wrong in the next context, so the knowledge base can quietly accumulate confident bad rules that are worse than no memory.
Curious how you handle that, when the agent logs a lesson into Analyse, do you review what it learned before it becomes a rule it acts on, or does it write freely and you correct later if it misfires? Because that write-path is where a learning knowledge base either compounds real trust or compounds confident nonsense, and I keep going back and forth on which side needs the human gate.
@aadilghani For the rules, I have a specific page agents write to. I give each agent an ID (right now I have 4 unique ids), then every few hrs when I want a break, I go for coffee then when I come back I first go through it, check if it did all right with a clear mind, edit / remove things from it, then continue working. Its quiete a unique workflow, and I more started it just to test my knowledge base for Analyse (since I build it), and now I found out this works perfectly for me.
Even tho Analyse itself isn't really build for it, Analyse itself is more a Analytics + SEO improvement system with a knowledge base, AI copilot & mcp connector. But I thought, why not now I have it use it.
Pushary
@wesley_breukers
The coffee-break-then-review-with-a-clear-mind cadence is the smart part, and it's smarter than it probably felt when you stumbled into it. Reviewing what the agents wrote after stepping away means you're auditing with fresh eyes instead of the tunnel vision you had while guiding them, and that gap matters, because the mistakes an agent logs are exactly the ones you're most likely to wave through if you review them in the same headspace that produced them. Batching the review to the break isn't just convenient, it's a better quality of review. You accidentally designed the human gate correctly.
And you've landed on the thing most people miss about a learning knowledge base: it needs a human between "agent wrote a lesson" and "lesson becomes a rule it acts on." You edit and remove before continuing, which means confident-but-wrong entries get caught before they compound. That's the whole difference between a knowledge base that accrues real trust and one that quietly accrues nonsense the agent then defends as established fact. The per-agent IDs are the other quietly-right call, because now a bad rule is traceable to which agent produced it, so you can see if one of the four is consistently the one writing junk.
The funny part is you built Analyse as an analytics-plus-SEO tool with a knowledge base bolted on, and the knowledge base turned out to be the reusable primitive. That happens more than people admit, the thing you built for one job is secretly a general-purpose memory layer, and agent-lessons is just another thing to store and retrieve. It's not that Analyse was built for this, it's that a good knowledge base doesn't care what kind of knowledge it holds.
Genuine question, since you're both the user and the builder here: when you edit or remove a lesson, does the agent ever re-learn the thing you deleted, write it back a session later because it hit the same situation again? Because that's the failure I'd worry about in a review-and-prune workflow, you correct it Monday, it re-derives the same wrong lesson Wednesday, and now you're playing whack-a-mole. Curious whether you've hit that, or whether your edits actually stick.
This is a very real problem.
Once you start using multiple agents, the bottleneck is not the code anymore. It is knowing what is running, what is stuck, and where your approval is needed. AI makes execution faster, but without a workflow around it, you just create new invisible waiting rooms.
Pushary
@nipuntaneja
"New invisible waiting rooms" is a great phrase, and it names the trap precisely: AI didn't remove the waiting, it moved it somewhere you can't see. Speeding up execution just relocated the bottleneck from the code to the coordination, and coordination delay is worse than slow code, because slow code is visible, you know it's grinding, whereas an agent parked waiting for approval looks exactly like an agent working. The waiting rooms are invisible because a stalled agent and a busy one are byte-identical from the outside.
And your first line is the real shift: once you're running multiple agents, the code stops being the hard part. Generation got cheap, so the scarce resource became knowing the state, what's running, what's stuck, where you're needed. That's not a coding problem, it's an awareness problem, and it's exactly the thing no faster model fixes, because a smarter agent that still can't tell you it's blocked leaves you in the same waiting room.
The workflow you're gesturing at is the fix, and the shape that matters is push over pull: you shouldn't have to go check which room has someone waiting, the room should announce itself. Otherwise "knowing the state" is just another polling loop you run manually, which caps out around three agents.
Curious, of your three, what's running / what's stuck / where I'm needed, which one actually costs you the most day to day? My hunch is "where I'm needed," since that's the one silently burning time while you assume it's still working, but you clearly feel this live and I'd rather hear which room bites hardest.
I stopped trying to watch everything it does. Instead I keep a pile of audit scripts that check decisions I've already made, and I run them after every change. If the agent quietly undoes something from three weeks ago, a script fails and I know right away. E2E tests cover the critical flows on top of that.
Watching the agent work is the easy part. Remembering what past me decided, and making sure the agent still respects it, turned out to be the actual job.
Pushary
@henry_s_jung
top notch! this is the best flow so far, but something that manages this saves me loads of time. Pushary by default, stores all your permission decision tree encrypted, and get weekly digests of your permissions audit report + pushary auto mode classifier automatically learns your behavior and unblocks your agent faster when it is obvious what decision you would take to the classifer.
no sales pitch but happy that there's someone else following this workflow.
Be awesome!
@aadilghani Appreciate it! The weekly permissions digest is a smart touch, decision fatigue is real. Good luck with Pushary!
With claude code - /remote_control in session and approve prompts on the go in the mobile app. It's also possible to do some automation with hooks, like maybe turning on alarm when task is done :) I haven't been using github copilot for a while, but I think I saw similar feature being introduced in "what's new" somewhere.
Pushary
@aiodintsov
The Claude Code /remote_control plus mobile-approve is a real chunk of the problem solved, and it's worth naming what it actually gets you: it kills the "tethered to the desk" part. Being able to approve from your phone means a blocked agent doesn't have to wait for you to physically be there, which is genuinely the away-from-keyboard half. And the hooks-for-alarms idea is the right instinct, wiring the Stop hook to something that reaches you is exactly how you stop polling.
The two gaps it leaves, and you half-flagged one yourself: it's per-tool. /remote_control solves it inside Claude Code, and you're already noticing Copilot has its own version, and that's the pattern, every tool ships its own island. So if you run more than one, you're back to a different remote-control surface per tool with no shared view, which is fine at one tool and messy the moment you're juggling Claude Code plus Codex plus something else.
The subtler gap: mobile-approve handles the approval cleanly, but approving from the phone works best for the trivial yes/no stuff. The moment the decision needs you to actually look at a diff, a button in the app isn't enough context to decide responsibly, so you're back at the terminal anyway. Which is fine, the trick is the alert telling you which kind it is so you don't approve a real change blind.
Curious, when you approve from the mobile app, is it mostly quick yes/no calls you're comfortable clearing on the go, or do you ever hit one where you wish you'd waited to see the actual change first? That line is the thing I keep chewing on.
the Cursor-is-a-black-box thread is the interesting part to me. instead of hooking into each tool's internal signal, what about going one layer lower and reading the OS process state directly, is the process blocked on a read() syscall (waiting for input, whether that's stdin or an Electron IPC channel under the hood) versus actively burning CPU. strace/ptrace-style monitoring is ugly and platform-specific but it doesn't care whether the tool is Claude Code, Cursor, or something that ships next month with zero hooks at all. feels like it trades "clean per-tool integration" for "one messier universal signal that never needs a new adapter." has anyone tried going that low level, or does it fall apart in practice once you actually attempt it
Pushary
@galdayan
Going lower is the right instinct, if the tool won't tell you its state, stop asking it and watch the process it can't hide. And the appeal is real: a `read()` on stdin looks the same whether it's Claude Code today or something with zero hooks next month. One signal, no adapters. That's the dream.
Where it bites in practice, three things. First, "blocked on read()" is noisier than it sounds, a modern app always has some thread parked on a read (UI events, sockets, watchers), so the condition is almost always true and tells you little. You still have to know which read matters, which is per-app knowledge again. Second, your Electron case is the sharp one: the approval-wait is an internal IPC event, so technically it's a blocked read, but telling "waiting for the user to click approve" apart from "Electron just idling" needs to know what that channel means, app-specific, the exact thing you were trying to avoid. Third, the practical killer: ptrace/strace is heavyweight, trips security tooling, fights SIP on macOS, needs elevated permissions. Asking a dev to run their agent under a tracer generates support tickets in the wild even when it works on your machine.
So it doesn't fall apart, it just doesn't deliver the clean universality it promises. You trade "one adapter per tool" for "one messy collector plus per-tool interpretation of what the syscalls mean", and the interpretation was the hard part all along. The per-tool knowledge is conserved; you can only move it around the stack.
Where it genuinely wins is as a fallback, not the primary: native hooks where they exist, process-level observation only for the zero-hook tools where it's "messy signal or nothing." For Cursor that might be the least-bad option, precisely because the clean alternative needs Cursor's cooperation and we don't have it.
Real question back, since you think at this level: have you found a way to pick out which blocked read is the meaningful one without app-specific knowledge? Because that's the crux, and if you've cracked it the whole thing gets a lot more universal. If you haven't, that's exactly where it turns back into an adapter.
one failure mode I haven't seen raised in this thread: the scary case isn't just one agent stalling, it's two agents about to collide - both about to touch the same file in a shared repo, or both about to burn through the same third-party rate limit at once. a push notification tells you after the fact that something already happened, it doesn't warn you before two runs step on each other. is cross-agent conflict detection on the roadmap at all, or is Pushary scoped to per-agent state changes rather than the interactions between agents?
Pushary
@galdayan
Straight answer: today Pushary is scoped to per-agent state, and you've correctly identified that cross-agent conflict is a different axis, not just more of the same. Per-agent notification is vertical, each agent reporting its own state up to you. Conflict detection is horizontal, the interactions between agents, and you can't derive the horizontal from stacking up the verticals, because two agents can each be in a perfectly healthy state and the combination is the problem. Each one individually reports "running, all good," and the collision lives in the space between them that no single agent's state describes.
And your two examples are actually two different hard problems wearing one label, worth splitting. The shared-file collision is detectable in principle before the fact, because there's a declarable resource, if you know each agent's intended write set (worktree, target files, the declared task path idea that came up earlier in this thread), you can see two claims overlap before either commits. That's a lock-manager-shaped problem: agents announce intent, the layer spots the overlap, warns or serializes them. Hard but tractable, and it's genuinely a before signal, not an after one.
The rate-limit collision is nastier, because the shared resource is external and invisible to the agents. Neither agent knows the other exists, neither knows the combined draw on the third-party limit, and the limit lives in someone else's system you can't inspect. So you can't detect the overlap by reading intent the way you can with files, you'd have to model the shared external resource yourself, track aggregate consumption across agents, and predict the collision. That's a real coordination layer, not a notification, and it's a meaningfully bigger thing to build.
Which is the honest scoping answer: conflict detection isn't an incremental feature on top of per-agent state, it's a second product surface, agents stop being independent things you observe and become a system you coordinate, with shared resources as first-class objects. It's squarely the direction this goes at scale (the "AgentOps" framing someone raised earlier is exactly this, the failure that matters isn't in any one agent, it's in the edges between them), but I'd be lying if I called it near-term. Per-agent state is the wedge because it's the acute pain today; cross-agent coordination is where it has to go once people are running enough agents that collisions stop being rare.
Genuine question, since you've clearly hit this: which of your two actually bites more in practice? Because the file-collision I think I can see a path to via declared write-sets, but the rate-limit one is the one I don't have a clean answer for, and if that's the one that actually costs you, it changes what's worth building first. My hunch is the silent rate-limit exhaustion is rarer but brutal when it happens, while file collisions are more frequent but you catch most of them at merge, curious whether that matches your experience or whether one of them is quietly costing you more than I'd guess.
@aadilghani that's a genuinely useful split, thanks for taking the question seriously. to answer directly - the rate-limit one has bitten harder for me, and precisely because it's invisible the way you describe. file collisions get caught fast, usually the moment you glance at a diff or run a merge, so the pain is real but short. the shared-limit case tends to surface as a vague "something felt off" hours later, degraded output or a stalled job you assumed was just thinking, and by the time you trace it back to exhausted quota you've lost the actual debugging thread of what you were doing. so agreed on the roadmap ordering - the wedge should be per-agent state now, but the invisible one is the sneakier tax long term.
Pushary
@galdayan
"Vague something felt off hours later" is the signature of the worst class of bug, and it's exactly why the rate-limit case is the sneakier tax. File collisions are honest, they fail loudly and near the point of impact, so the pain is sharp but short. The shared-limit case launders its own cause: the failure surfaces far from where it started, disguised as degraded output or a stalled job you assumed was thinking, so the expensive part isn't the exhausted quota, it's the debugging thread you lose reconstructing why. Delay plus disguise is what makes it costly, not the collision itself.
And that's the tell that it needs a genuinely different mechanism, because you can't catch it after the fact from state, by then the trail's cold. The only useful version is predictive, model the shared external resource and see the combined draw approaching the limit before it hits, which is real coordination, not a notification. So agreed on the ordering for the right reason: per-agent state is the wedge because it's the acute, visible pain, and the invisible one is the long-term tax precisely because nothing today even surfaces it until it's already cost you.
Good exchange, this is one I'm carrying into how I think about the roadmap's second phase. Appreciate you pushing on the horizontal axis, it's the part that's easy to under-weight when the vertical pain is louder.
@aadilghani good place to leave it, and I appreciate you actually taking the horizontal axis seriously instead of nodding at it and moving on. one last thing before I let this go - the "model the shared external resource" idea, is that something Pushary would build by having each agent self-report its estimated cost before a call, or would it watch actual response headers/usage centrally and infer the combined draw without needing the agents to cooperate at all. self-reporting seems like it'd break exactly when you need it most, which is a misbehaving agent
Yep this is a real pain. Once you start using more than one coding agent it gets messy really fast. I still don't have a good solution, just lots of terminal tabs and hoping I don't miss anything 😂
Push notifications for approvals/completed tasks actually makes a lot of sense. Curious how you handle different agents tho.
Pushary
@pagesv
"Lots of terminal tabs and hoping I don't miss anything" is the honest baseline, and the 😂 stops being funny right around the third agent. Tabs plus hope is a polling loop where you're the CPU, and hope means you're just accepting you'll miss some. Which is the part that costs you.
How I handle different agents: a thin adapter per tool that catches each one's native signal and normalizes it into one state model. Codex has a native approval event, Claude Code gives clean hooks, Cursor's the stubborn one. The adapter eats each tool's weirdness so you get one inbox that knows all of them, instead of one habit per terminal.
It's free to try, so honestly just point it at your setup and see, pushary.com. You're exactly who it's built for.
One thing while you're there: is it agents finishing you miss, or agents blocked waiting? The blocked ones are the silent killers, burning time while you assume they're working, so that's the one I'd make loudest.
I run a handful of sessions in a terminal multiplexer and just alt-tab through panes manually, which is exactly the janky workflow you're describing. tried a shell hook that pings Slack when a session goes idle, worked fine for one agent but got noisy fast once I had 4-5 running, couldn't tell which ping actually needed me versus which one was just "done, no action needed". if push notifications + approval flows is the space you're building in, the thing I'd want most is filtering by urgency, not just "agent stopped" but "agent stopped AND is blocked waiting on you" vs "agent stopped and finished cleanly"
Pushary
@omri_ben_shoham1
You've hit the exact wall the hook-plus-Slack approach always hits, and it's worth naming why: your hook fires on idle, but idle is one signal collapsing two completely different states. Done-cleanly and blocked-waiting-on-you both look like "the session went quiet," so your pings were structurally unable to tell you which one they meant. Not a tuning problem, an information problem, you were alerting on the absence of activity, and absence can't distinguish between "finished" and "stuck." At one agent you could just check. At 4-5 the ambiguity multiplies and the channel becomes noise, so you mute it, and now you're back to alt-tabbing.
And that's exactly the split you're asking for and the right one: those two aren't different urgencies of the same event, they're different events. Finished-cleanly is informational, nothing's waiting, the cost is fixed, read it whenever. Blocked-waiting-on-you is a live stall, the agent is frozen and the meter's running, and every minute you don't know costs you. One's a receipt, one's a bill that's still accruing. They shouldn't even sound the same on your phone.
The catch, and the reason your hook couldn't do it: getting that split reliably means the agent has to declare "I'm blocked" as a distinct event, not go quiet and let you infer. That's native hooks, Codex exposes a real approval event, Claude Code gives clean signals on shell and file edits, so you can catch the actual block rather than guessing at silence. That's what makes the urgency filter possible instead of just aspirational, and it's exactly what I'm building: blocked pings loudly, done goes in a list you glance at, everything else stays dark.
Free to try if you want to point it at your multiplexer setup, pushary.com, and you're the ideal test case since 4-5 agents is precisely where the hook approach dies. Curious though, in your Slack pings, what was the actual ratio, mostly "done, no action needed" with the occasional real block? Because if the blocks were rare, the noise was almost entirely the done-pings, which suggests done probably shouldn't push at all.