--- type: feature-survey created: 2026-07-25 question: "What features do tools in active use ship so a human does not have to actively monitor a queue?" scope: complements 2026-07-25-cross-repo-delivery-research.md (which covered Unix/email prior art) --- # Features that remove the need to watch Companion to `2026-07-25-cross-repo-delivery-research.md`. That brief surveyed Unix and email prior art (maildir, biff, RFC 3834, cron/launchd). It explicitly reported the "what do comparable projects actually ship" question as **not answered**. This closes that gap. ## The reframe that makes everything tractable > With no daemon, no scheduler and no network, **nothing in this system can fire > on its own** — the only moment code runs is when the developer opens some > repository. So the discriminating question per feature is not "does it need a > daemon?" but **"can it be computed at read time from timestamps and files > already on disk?"** Every aging and escalation tool surveyed (`actions/stale`, PagerDuty, `probot/no-response`) depends on an external clock. **Their mechanics transfer; their triggering does not.** A state machine evaluated at read time needs no clock of its own — only a comparison against `now`. ## Candidate features VERIFIED = the cited page was fetched and says this. Defaults are quoted only where documented. | Feature | Tool | Mechanism | Fit | |---|---|---|---| | **Dependency Dashboard** — one issue summarizing all pending work, body rewritten every run via `ensureIssue({title, reuseTitle, body})`, sectioned into Pending Approval / Awaiting Schedule / Rate-Limited / Errored / Open / … | Renovate | one always-current artifact instead of N notices | **direct** | | **Dashboard as input device** — "click on a checkbox below" turns the summary into the control surface | Renovate | read and act in the same object | **direct** | | **Severity bypasses the caps** — "Security update pull requests are not subject to this limit and do not count toward it" | Dependabot | a priority field exempts an item from batching | **direct** | | **One continuously-updated comment** — found by a hidden header marker, not by search; modes `append`/`recreate`/`delete`/`hide`/`only_create` | sticky-pull-request-comment | identity-by-marker | **direct** | | **Living release PR** — one PR updated as new changesets land | changesets/action | same pattern, different substrate | **direct** | | **Read and done are orthogonal** — `is:read` "doesn't include notifications marked as Done"; `PATCH` marks read, `DELETE` marks done. Two verbs, two axes | GitHub | seen ≠ resolved | **direct** | | **Unread-only by default** — `all` defaults to `false` | GitHub REST | the primitive behind any pending count | **direct** | | **Typed `reason` on every item** — 14 values (`mention`, `review_requested`, `state_change`, …) | GitHub | triage keys off *why*, not just *what* | **direct** | | **One command, all repos** — `gh status` prints assigned issues, PRs, review requests, mentions and activity across every subscribed repo in one view | gh CLI | **the closest analogue to the actual complaint** | **direct** | | **Auto-de-escalation** — `remove-stale-when-updated` defaults to `true`; any activity clears the mark | actions/stale | mtime change recomputes at read time | **direct** | | **Gap detection instead of guaranteed delivery** — monotonic event IDs; missed events "can be detected by noting a discontinuity in the event IDs" | Syncthing | proves at read time that nothing was silently dropped | **direct** | | **Snooze that resurfaces itself** — 1/4/8/24h or custom, max 168h; only on *acknowledged* incidents; on expiry the incident "returns to a triggered state and notifies you again" | PagerDuty | store a resurface timestamp, compare at read time | **adapts** | | **Grouping** — "All updates sharing the same `groupName` will be placed into the same branch/PR"; Dependabot `groups`: first matching rule wins | Renovate, Dependabot | N items become 1 | **adapts** | | **Schedule gates the *action*, not the *run*** — `schedule` restricts "times… during which Renovate may create or update branches and PRs" | Renovate | a gate evaluated inside a run survives having no scheduler | **adapts** | | **Concurrency cap** — `prConcurrentLimit` default `10`; Dependabot raises max 5 and "no further pull requests are raised until some… are merged or closed" | Renovate, Dependabot | cap what is *displayed*, not what is *stored* | **adapts** | | **Age gate before auto-action** — `minimumReleaseAge` (default `null`); maintainers recommend `"14 days"` before automerging third-party deps | Renovate | age-gated automatic disposal | **adapts** | | **Persisted cursor replayed on restart** — triggers "are saved and re-established across a Watchman process restart", re-evaluated from the last captured clock | Watchman | needs the daemon; the cursor idea does not | **needs daemon** | | **Aging → stale → close** — `days-before-stale: 60`, `days-before-close: 7` | actions/stale | state machine fits, triggering does not | **needs daemon** | | Hourly rate caps; `operations-per-run: 30` | Renovate, actions/stale | need a clock / a rate limit that does not exist locally | **no fit** | ## Failure modes — the findings that change the design **The living-summary pattern breaks on *identity*, not on content.** Renovate discussion #12131: a repository got a *new* Dependency Dashboard roughly once an hour. `ensureIssue` locates the existing dashboard by searching for issues **authored by the bot account**; custom auth meant issues were created under a different account, the lookup returned zero, and every run created a fresh dashboard. **Design constraint: the summary artifact must live at a fixed, deterministic path and must never be located by searching on a mutable attribute.** **Its second failure mode is latency, and it reads as breakage.** Discussion #9905: users reported dashboard checkboxes "doing nothing". The checkbox is a *request queued for the next run*, not an immediate action. An input surface on a passively-regenerated artifact is inherently deferred — **and it must say so, or deferral is read as failure.** **Never auto-close on age.** This is the single most-criticized behavior in the survey. The signal argument (fvsch.com/stale-bots, Feb 2023) is that auto-closing destroys the queue's meaning: "Closed" stops distinguishing fixed from rejected from arbitrarily hidden, and closed items drop out of default search, so users "will not find that identical issue which was auto-closed" and file duplicates. Sentiment is genuinely split — maintainers defend it on capacity grounds — but capacity is not the constraint for a single operator. **Escalate presentation only.** **A silent cap makes a queue look shorter than it is.** `actions/stale`'s own README warns that with `operations-per-run: 30` "you might end up with unprocessed issues or pull requests after a stale action run". Any cap must be *stated* in the summary, never silent. **Maintainers' own defaults are not the shipped defaults.** Renovate recommends `config:best-practices` over `config:recommended`, and the Dependency Dashboard option itself defaults to `false` while the onboarding preset turns it on. **Nobody has solved this for agents.** `avivsinai/agent-message-queue` (~76 stars) is a genuine close analogue — Maildir `tmp`→`new`→`cur` semantics, atomic rename, no daemon by default — and its awareness story is still "poll, or run a monitor, or use an experimental terminal-injection wake hack". It does **not** solve the monitoring problem either. Claude Code's own agent messaging is in-session, not cross-session. LangChain's Agent Inbox is server-backed. **No surveyed project ships a daemon-free mechanism that makes an unopened repository's backlog visible from elsewhere.** The strongest transferable idea came from outside the space entirely: `gh status`. ## Ranked shortlist — cheapest first Each passes the test: computable at read time, from what is already on disk, with no daemon and no clock of its own. 1. **Surface every mailbox at every session start, not just the current repository's.** (`gh status`.) The only item that attacks the stated complaint head-on — an unopened repository is invisible precisely because injection is repo-scoped. The enumerate-all logic already exists; what changes is *where its output appears*. Must use a plain file count, **never** the consuming read path, and must exclude `_broadcast`. 2. **One living summary at a fixed path, regenerated on every read.** Three independent tools converge on this. Address it by deterministic path, never by search. 3. **Seen and done as two independent states.** Without it the pending count becomes a lie the moment something is read without being acted on — and the count is the whole basis of item 1. Default view shows un-acted-on only. 4. **Explicit defer-until-timestamp that resurfaces itself.** The self-returning version of "leaving one pending". Copy two constraints: cap the deferral, and permit it **only on items already acknowledged** — which is why item 3 lands first. 5. **Age computed at read time, escalating presentation, automatic de-escalation.** An adaptation — no surveyed tool ships this daemon-free. Take the state machine and both guardrails (any touch clears the escalation; exemptions never age). Do not take auto-close. **Explicitly not recommended:** hourly rate caps, scheduled digest windows, and auto-close — the first two need a clock this system does not have, the third is the most-criticized behavior in the survey. ## Sources Renovate: [configuration options](https://docs.renovatebot.com/configuration-options/), [schema](https://docs.renovatebot.com/renovate-schema.json), [dependency-dashboard.ts](https://raw.githubusercontent.com/renovatebot/renovate/main/lib/workers/repository/dependency-dashboard.ts), [presets](https://docs.renovatebot.com/presets-config/), [best practices](https://docs.renovatebot.com/upgrade-best-practices/), [#12131](https://github.com/renovatebot/renovate/discussions/12131), [#9905](https://github.com/renovatebot/renovate/discussions/9905) · Dependabot: [options reference](https://docs.github.com/en/code-security/dependabot/working-with-dependabot/dependabot-options-reference), [pull requests](https://docs.github.com/en/code-security/concepts/supply-chain-security/dependabot-pull-requests), [auto-triage](https://docs.github.com/en/code-security/dependabot/dependabot-auto-triage-rules/about-dependabot-auto-triage-rules) · GitHub: [managing notifications](https://docs.github.com/en/subscriptions-and-notifications/how-tos/viewing-and-triaging-notifications/managing-notifications-from-your-inbox), [REST notifications](https://docs.github.com/en/rest/activity/notifications), [gh status](https://cli.github.com/manual/gh_status) · [actions/stale](https://raw.githubusercontent.com/actions/stale/main/README.md) · [fvsch: don't use stale bots](https://fvsch.com/stale-bots) · [PagerDuty snooze](https://support.pagerduty.com/main/docs/edit-incidents) · [Watchman trigger](https://facebook.github.io/watchman/docs/cmd/trigger.html) · [Syncthing events](https://docs.syncthing.net/dev/events.html) · [sticky-pull-request-comment](https://raw.githubusercontent.com/marocchino/sticky-pull-request-comment/main/README.md) · [changesets/action](https://raw.githubusercontent.com/changesets/action/main/README.md) · [agent-message-queue](https://github.com/avivsinai/agent-message-queue) ### Not verified — do not build on `prHourlyLimit`'s default (two extractions of one page said `10`, no second source); the `vulnerabilityAlerts` object's default; what Dependabot does when a "dismiss until a patch is available" patch actually lands (absent from both pages checked); **whether a GitHub notification marked Done returns to the inbox on new thread activity** — two pages checked, neither states it, so do not assume resurface-on-activity. `probot/no-response` is archived (2021-07-13) and deprecated by its owner. `langchain-ai/agent-inbox` was search-level only, not fetched.