* fix(outbound): preserve queued delivery custody on provider abort without send evidence
A provider-side abort with no platform send evidence acked the durable queue
entry (dropping the message) whenever no producer claim id existed. Send
evidence is now computed once (delivered results, pre/post-send marks,
sentBeforeError, stable outcomes); aborts with evidence keep the
platform_send failure path, aborts without evidence preserve the queued
entry as failed for retry unless the operator's own abort signal fired.
* chore: re-fire CI
* chore: re-fire CI against fixed main baseline
* fix(daemon): reload systemd user manager after removing legacy units
uninstallLegacySystemdUnits removed unit files without a daemon-reload, so the
user manager kept the deleted unit definitions loaded and startable until an
unrelated reload. Legacy-unit removal now reuses the shared
disable-or-tolerate-missing helper, surfaces non-ENOENT unlink failures instead
of swallowing them, and reloads the user manager once after any unit file was
removed (matching uninstallUserSystemdGatewayUnit).
* chore: re-fire CI
* chore: re-fire CI against fixed main baseline
* fix(plugins): preserve multi-entry pack identity and safe install compensation
Multi-entry plugin packs registered every entry under the single manifest id,
so only one entry survived registry normalization; discovery now records an
entry-scoped effectivePluginId that the manifest registry honors. Managed
install compensation no longer silences runtime cache invalidation on the
success path, and local linked installs never delete operator-owned source
directories on persistence failure.
* fix(plugins): reject colliding derived entry ids in multi-entry packs
ClawSweeper P1: ./a/index.ts and ./b/index.ts both derived pack/index and one
entry silently lost same-root dedupe. Colliding entries now emit an error
diagnostic naming the sources and register no candidate; docs note the
entry-scoped plugins.entries key for multi-entry packs.
* fix(acpx): stop/start races cannot resurrect a stopped runtime backend
The deferred acpx runtime service published its lazily started backend without
lifecycle ownership checks: a stop during in-flight activation let the stale
service become the active runtime, and stop cleared shared state while startup
still used it. Start/stop now use a lifecycle revision; only the current
revision may publish a runtime, stop invalidates every deferred proxy before
awaiting in-flight startup, and restart waits for the previous stop to settle.
* chore: re-fire CI
* fix(acpx): fence deferred runtime ownership
* refactor(acpx): centralize backend lifecycle ownership
* fix(acpx): annotate deferred runtime cycle
* fix(qqbot): recover credentials from backup deterministically at startup
Credential recovery previously keyed off a loose truthiness check that
misclassified partially configured accounts; recovery state is now a closed
discriminated union (configured/recoverable/partial/missing) so a live-config
wipe restores from the per-account backup only when the account is actually
recoverable, and partial configs surface a clear diagnostic instead of a
silent skip.
* chore: re-fire CI
deriveSessionTitle no longer falls back to an 8-hex session-id prefix
plus date; derived titles are human content only, so consumers'
key-based fallbacks (Control UI "New thread", TUI formatted key, ACP
displayName/label/key) render instead of an opaque machine title.
Reuse the canonical Control UI plain-text responder so plugin and catalog icon HEAD misses retain the same Content-Length metadata as GET without duplicating response policy.
Punchcard-Session: cobalt-harbor-harbor-2b
Co-authored-by: Vincent Koc <vincentkoc@users.noreply.github.com>
* refactor(test): exercise real Teams monitor lifecycle
* refactor(test): use real gateway lifecycle state
* refactor(test): use real restart delivery queue
* test: type Teams SDK boundary fixture