* CLI: clarify config vs configure descriptions
* CLI: improve top-level command descriptions
* CLI: make direct command help more descriptive
* CLI: add commands hint to root help
* CLI: show root help hint in implicit help output
* CLI: add help example for command-specific help
* CLI: tweak root subcommand marker spacing
* CLI: mark clawbot as subcommand root in help
* CLI: derive subcommand markers from registry metadata
* CLI: escape help regex CLI name
Bare numeric Discord IDs (e.g. '1470130713209602050') in cron
delivery.to caused 'Ambiguous Discord recipient' errors and silent
delivery failures.
Adds normalizeDiscordOutboundTarget() to the existing Discord
normalize module (channels/plugins/normalize/discord.ts) alongside
normalizeDiscordMessagingTarget. Defaults bare numeric IDs to
'channel:<id>', matching existing behavior.
Both the Discord extension plugin and standalone outbound adapter
use the shared helper via a one-liner resolveTarget.
Fixes#14753. Related: #13927
The test verifies that cooldownUntil IS cleared when it equals exactly
`now` (>= comparison), but the test name said "does not clear". Fixed
the name to match the actual assertion behavior.
When an auth profile hits a rate limit, `errorCount` is incremented and
`cooldownUntil` is set with exponential backoff. After the cooldown
expires, the time-based check correctly returns false — but `errorCount`
persists. The next transient failure immediately escalates to a much
longer cooldown because the backoff formula uses the stale count:
60s × 5^(errorCount-1), max 1h
This creates a positive feedback loop where profiles appear permanently
stuck after rate limits, requiring manual JSON editing to recover.
Add `clearExpiredCooldowns()` which sweeps all profiles on every call to
`resolveAuthProfileOrder()` and clears expired `cooldownUntil` /
`disabledUntil` values along with resetting `errorCount` and
`failureCounts` — giving the profile a fair retry window (circuit-breaker
half-open → closed transition).
Key design decisions:
- `cooldownUntil` and `disabledUntil` handled independently (a profile
can have both; only the expired one is cleared)
- `errorCount` reset only when ALL unusable windows have expired
- `lastFailureAt` preserved for the existing failureWindowMs decay logic
- In-memory mutation; disk persistence happens lazily on the next store
write, matching the existing save pattern
Fixes#3604
Related: #13623, #15851, #11972, #8434