cap get_loose_ends todo output + add ack_todos_until bulk-clear (#2944)
This commit is contained in:
parent
58a9f218f2
commit
eb570c003d
7 changed files with 167 additions and 9 deletions
|
|
@ -41,6 +41,20 @@ pub struct AckUntilArgs {
|
||||||
pub up_to: i64,
|
pub up_to: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// MCP tool args for `ack_todos_until`.
|
||||||
|
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
|
||||||
|
pub struct AckTodosUntilArgs {
|
||||||
|
/// Highest todo id to bulk-clear: every pending todo (bash-task
|
||||||
|
/// completions, matrix unread, forge activity) with `id <= up_to` is
|
||||||
|
/// acked in one call — the todo-store analogue of `ack_until` for the
|
||||||
|
/// message inbox. `get_loose_ends` suggests a value in its truncation
|
||||||
|
/// summary when there are more todos than it renders individually;
|
||||||
|
/// pass that value (or the highest id you've actually triaged) to
|
||||||
|
/// clear the backlog in one shot instead of cancelling ids one at a
|
||||||
|
/// time.
|
||||||
|
pub up_to: i64,
|
||||||
|
}
|
||||||
|
|
||||||
/// MCP tool args for `remind`. Exactly one of `delay_seconds` or
|
/// MCP tool args for `remind`. Exactly one of `delay_seconds` or
|
||||||
/// `at_unix_timestamp` must be set; both / neither is a tool-side error.
|
/// `at_unix_timestamp` must be set; both / neither is a tool-side error.
|
||||||
/// Hides the tagged `ReminderTiming` enum behind a flatter schema so the
|
/// Hides the tagged `ReminderTiming` enum behind a flatter schema so the
|
||||||
|
|
|
||||||
|
|
@ -24,18 +24,18 @@ mod args;
|
||||||
mod render;
|
mod render;
|
||||||
|
|
||||||
pub use args::{
|
pub use args::{
|
||||||
AckUntilArgs, AgentGetLooseEndsArgs, AnswerArgs, AskArgs, CancelLooseEndArgs,
|
AckTodosUntilArgs, AckUntilArgs, AgentGetLooseEndsArgs, AnswerArgs, AskArgs,
|
||||||
CancelScheduleArgs, CreateRepoArgs, EditScheduleArgs, FireScheduleNowArgs, GetAgentMetaArgs,
|
CancelLooseEndArgs, CancelScheduleArgs, CreateRepoArgs, EditScheduleArgs,
|
||||||
GetHostJournalArgs, GetLogsArgs, KillArgs, RecvArgs, RemindArgs, RequestInitConfigArgs,
|
FireScheduleNowArgs, GetAgentMetaArgs, GetHostJournalArgs, GetLogsArgs, KillArgs, RecvArgs,
|
||||||
RequestSchedulePromptArgs, RestartArgs, SendArgs, SetStatusArgs, StartArgs, UpdateArgs,
|
RemindArgs, RequestInitConfigArgs, RequestSchedulePromptArgs, RestartArgs, SendArgs,
|
||||||
UpdateMetaInputsArgs,
|
SetStatusArgs, StartArgs, UpdateArgs, UpdateMetaInputsArgs,
|
||||||
};
|
};
|
||||||
pub use render::{annotate_retries, format_ack, format_agent_meta, format_recv};
|
pub use render::{annotate_retries, format_ack, format_agent_meta, format_recv};
|
||||||
|
|
||||||
use render::{
|
use render::{
|
||||||
dial_agent_socket, format_matrix_summary, local_questions, local_reminders, local_todos,
|
dial_agent_socket, format_matrix_summary, local_questions, local_reminders, local_todos,
|
||||||
loose_end_kind_label, mark_local_todo_done, matrix_unread_summary, parse_loose_end_kind,
|
loose_end_kind_label, mark_local_todo_done, mark_local_todos_done_until,
|
||||||
render_loose_ends, reply_err,
|
matrix_unread_summary, parse_loose_end_kind, render_loose_ends, reply_err,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Write (or remove) the status file in the agent's own `state/` directory.
|
/// Write (or remove) the status file in the agent's own `state/` directory.
|
||||||
|
|
@ -347,7 +347,11 @@ impl AgentServer {
|
||||||
at turn start to remember what you owe / what's owed to you without scrolling \
|
at turn start to remember what you owe / what's owed to you without scrolling \
|
||||||
inbox history. Output is a short bulleted list with ids, ages in seconds, and \
|
inbox history. Output is a short bulleted list with ids, ages in seconds, and \
|
||||||
the relevant context. Each `question` or `reminder` row can be cancelled by \
|
the relevant context. Each `question` or `reminder` row can be cancelled by \
|
||||||
passing its id + kind to `cancel_loose_end`. Empty result is reported clearly.\n\
|
passing its id + kind to `cancel_loose_end`. Empty result is reported clearly. \
|
||||||
|
Todos are capped at 40 rendered rows (newest first) so a large backlog never makes \
|
||||||
|
this call fail — a trailer line reports how many more are pending and suggests an \
|
||||||
|
`ack_todos_until` value to bulk-clear the rest in one call instead of triaging \
|
||||||
|
hundreds of ids individually.\n\
|
||||||
Pass `agent: \"<name>\"` to inspect a specific peer agent's threads. Direct \
|
Pass `agent: \"<name>\"` to inspect a specific peer agent's threads. Direct \
|
||||||
child agents are always accessible. For non-children, the `query_agent_state` \
|
child agents are always accessible. For non-children, the `query_agent_state` \
|
||||||
capability is required — without it the request is rejected with an error."
|
capability is required — without it the request is rejected with an error."
|
||||||
|
|
@ -547,6 +551,35 @@ impl AgentServer {
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tool(
|
||||||
|
description = "Bulk-clear local todos (loose-ends v2 — bash-task completions, matrix \
|
||||||
|
unread, forge activity): every pending todo with `id <= up_to` is acked in one call, \
|
||||||
|
same shape as `ack_until` for the message inbox. Use this when `get_loose_ends` \
|
||||||
|
reports more todos than it renders individually (its truncation summary suggests a \
|
||||||
|
value) or whenever a backlog has piled up past the point of clearing ids one at a \
|
||||||
|
time — note the highest id you've actually triaged, or take the suggested value \
|
||||||
|
verbatim to clear the whole shown-as-hidden tail. Only affects YOUR todos; ids above \
|
||||||
|
`up_to` stay pending. Returns how many were newly acked."
|
||||||
|
)]
|
||||||
|
async fn ack_todos_until(&self, Parameters(args): Parameters<AckTodosUntilArgs>) -> String {
|
||||||
|
let log = format!("{args:?}");
|
||||||
|
run_tool_envelope("ack_todos_until", log, async move {
|
||||||
|
match mark_local_todos_done_until(args.up_to).await {
|
||||||
|
Some(hive_agent_sock::Response::Acked { count }) => {
|
||||||
|
format!("acked {count} todo(s) up to id {}", args.up_to)
|
||||||
|
}
|
||||||
|
Some(hive_agent_sock::Response::Err { message }) => {
|
||||||
|
format!("ack_todos_until failed: {message}")
|
||||||
|
}
|
||||||
|
Some(other) => format!("ack_todos_until unexpected response: {other:?}"),
|
||||||
|
None => "ack_todos_until: local todo socket unavailable \
|
||||||
|
(HIVE_AGENT_SOCKET unset or harness unreachable)"
|
||||||
|
.to_owned(),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
#[tool(
|
#[tool(
|
||||||
description = "Create a git repo through hive-c0re. You CANNOT create repos with your \
|
description = "Create a git repo through hive-c0re. You CANNOT create repos with your \
|
||||||
own forge token (creation is disabled) — this is the only path. The repo is created in \
|
own forge token (creation is disabled) — this is the only path. The repo is created in \
|
||||||
|
|
|
||||||
|
|
@ -136,6 +136,21 @@ fn msg_id_tag(id: i64) -> String {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Hard cap on how many individual `Todo` lines `render_loose_ends` will
|
||||||
|
/// emit. Approvals/questions/reminders stay naturally bounded (they're
|
||||||
|
/// triaged interactively and don't self-multiply), but todos are pushed by
|
||||||
|
/// unattended producers (matrix/bash/forge) — an agent that goes a long
|
||||||
|
/// stretch without calling `get_loose_ends`, or whose producers outpace its
|
||||||
|
/// triage, can accumulate hundreds of them. Rendering all of them
|
||||||
|
/// unconditionally risks producing a tool result too large for the MCP
|
||||||
|
/// transport to return at all, which is worse than a bug: it's a deadlock
|
||||||
|
/// (the agent can't even see what's pending to start clearing it). Capping
|
||||||
|
/// here guarantees `get_loose_ends` always returns successfully; the
|
||||||
|
/// truncation summary tells the agent how to bulk-clear the rest via
|
||||||
|
/// `ack_todos_until` (see that tool's doc for why it's the intended escape
|
||||||
|
/// hatch, not a per-id triage loop).
|
||||||
|
const MAX_RENDERED_TODOS: usize = 40;
|
||||||
|
|
||||||
/// Inner renderer for a `Vec<LooseEnd>` already extracted from the socket
|
/// Inner renderer for a `Vec<LooseEnd>` already extracted from the socket
|
||||||
/// reply. Called by the `get_loose_ends` handler, which injects the
|
/// reply. Called by the `get_loose_ends` handler, which injects the
|
||||||
/// `UnreadMatrix` entry before formatting.
|
/// `UnreadMatrix` entry before formatting.
|
||||||
|
|
@ -145,7 +160,17 @@ pub(super) fn render_loose_ends(loose_ends: &[hive_sh4re::LooseEnd]) -> String {
|
||||||
return "(no loose ends)".to_owned();
|
return "(no loose ends)".to_owned();
|
||||||
}
|
}
|
||||||
let mut out = format!("{} loose end(s):\n", loose_ends.len());
|
let mut out = format!("{} loose end(s):\n", loose_ends.len());
|
||||||
|
let mut shown_todos = 0usize;
|
||||||
|
let mut hidden_todos = 0usize;
|
||||||
|
let mut hidden_min_id: Option<i64> = None;
|
||||||
for t in loose_ends {
|
for t in loose_ends {
|
||||||
|
if let hive_sh4re::LooseEnd::Todo { id, .. } = t
|
||||||
|
&& shown_todos >= MAX_RENDERED_TODOS
|
||||||
|
{
|
||||||
|
hidden_todos += 1;
|
||||||
|
hidden_min_id = Some(hidden_min_id.map_or(*id, |m| m.min(*id)));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
match t {
|
match t {
|
||||||
hive_sh4re::LooseEnd::Approval {
|
hive_sh4re::LooseEnd::Approval {
|
||||||
id,
|
id,
|
||||||
|
|
@ -233,9 +258,19 @@ pub(super) fn render_loose_ends(loose_ends: &[hive_sh4re::LooseEnd]) -> String {
|
||||||
"- todo #{id} [{subsystem}{key}, {age_seconds}s old]: {summary}{src} \
|
"- todo #{id} [{subsystem}{key}, {age_seconds}s old]: {summary}{src} \
|
||||||
(cancel_loose_end kind:\"todo\" id:{id} to clear)"
|
(cancel_loose_end kind:\"todo\" id:{id} to clear)"
|
||||||
);
|
);
|
||||||
|
shown_todos += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if hidden_todos > 0 {
|
||||||
|
let min_id = hidden_min_id.unwrap_or(0);
|
||||||
|
let _ = writeln!(
|
||||||
|
out,
|
||||||
|
"- {hidden_todos} more todo(s) not shown (oldest not shown: #{min_id}) — \
|
||||||
|
call ack_todos_until(up_to: {min_id}) to bulk-clear the old backlog, or \
|
||||||
|
cancel_loose_end kind:\"todo\" id:<N> to clear individually"
|
||||||
|
);
|
||||||
|
}
|
||||||
out
|
out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -350,6 +385,13 @@ pub(super) async fn mark_local_todo_done(id: i64) -> Option<hive_agent_sock::Res
|
||||||
dial_agent_socket(&hive_agent_sock::Request::MarkTodoDone { id }).await
|
dial_agent_socket(&hive_agent_sock::Request::MarkTodoDone { id }).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Bulk-ack every un-acked local todo with `id <= up_to`, via the harness's
|
||||||
|
/// in-agent socket — the todo-store analogue of `mark_local_todo_done`, for
|
||||||
|
/// the `ack_todos_until` tool.
|
||||||
|
pub(super) async fn mark_local_todos_done_until(up_to: i64) -> Option<hive_agent_sock::Response> {
|
||||||
|
dial_agent_socket(&hive_agent_sock::Request::MarkTodosDoneUntil { up_to }).await
|
||||||
|
}
|
||||||
|
|
||||||
/// Format a `Vec<MatrixRoomUnread>` into a per-room summary string.
|
/// Format a `Vec<MatrixRoomUnread>` into a per-room summary string.
|
||||||
/// Single room / single message collapses to one line; multi-room
|
/// Single room / single message collapses to one line; multi-room
|
||||||
/// expands to a bulleted list. Returns an empty string for empty input.
|
/// expands to a bulleted list. Returns an empty string for empty input.
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,10 @@ pub enum Request {
|
||||||
},
|
},
|
||||||
/// The agent marks one of its own todos done, by id.
|
/// The agent marks one of its own todos done, by id.
|
||||||
MarkTodoDone { id: i64 },
|
MarkTodoDone { id: i64 },
|
||||||
|
/// The agent bulk-acks every un-acked todo with `id <= up_to` in one
|
||||||
|
/// shot — the todo-store analogue of the message inbox's `ack_until`,
|
||||||
|
/// for clearing a backlog that piled up past the point of per-id triage.
|
||||||
|
MarkTodosDoneUntil { up_to: i64 },
|
||||||
/// Schedule a reminder that fires into this agent's own turn loop at
|
/// Schedule a reminder that fires into this agent's own turn loop at
|
||||||
/// `timing` (harness-local — no broker round-trip). Same semantics as
|
/// `timing` (harness-local — no broker round-trip). Same semantics as
|
||||||
/// the old broker `Remind` request. `file_path`, when set, is where the
|
/// the old broker `Remind` request. `file_path`, when set, is where the
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ You are hyperhive agent `{label}` (qualified: `{qualified_label}`){hive_identity
|
||||||
|
|
||||||
Tools (hyperhive surface). Full signature + behavior for each comes from the tool's own MCP description (you already received it via the MCP tool schema) — this is just the map of what exists and which ones are gated, so you know where to look:
|
Tools (hyperhive surface). Full signature + behavior for each comes from the tool's own MCP description (you already received it via the MCP tool schema) — this is just the map of what exists and which ones are gated, so you know where to look:
|
||||||
|
|
||||||
- **Inbox / messaging** (always available): `mcp__hyperhive__recv`, `mcp__hyperhive__ack_until`, `mcp__hyperhive__send`, `mcp__hyperhive__ask`, `mcp__hyperhive__answer`, `mcp__hyperhive__get_loose_ends`, `mcp__hyperhive__cancel_loose_end`, `mcp__hyperhive__remind`, `mcp__hyperhive__set_status`, `mcp__hyperhive__get_agent_meta`. Two habits worth internalizing beyond the tool descriptions themselves: prefer ending the turn over repeatedly polling `recv` when idle (only turn-boundaries observe in-container todo wakes — bash-task completions, matrix unread, forge activity — and ending the turn is also your checkpoint); and `ask`/`answer` are async — `ask` returns immediately with a question id, the reply lands later as a `question_answered` system event, never block a turn waiting on it inline.
|
- **Inbox / messaging** (always available): `mcp__hyperhive__recv`, `mcp__hyperhive__ack_until`, `mcp__hyperhive__send`, `mcp__hyperhive__ask`, `mcp__hyperhive__answer`, `mcp__hyperhive__get_loose_ends`, `mcp__hyperhive__cancel_loose_end`, `mcp__hyperhive__ack_todos_until`, `mcp__hyperhive__remind`, `mcp__hyperhive__set_status`, `mcp__hyperhive__get_agent_meta`. Two habits worth internalizing beyond the tool descriptions themselves: prefer ending the turn over repeatedly polling `recv` when idle (only turn-boundaries observe in-container todo wakes — bash-task completions, matrix unread, forge activity — and ending the turn is also your checkpoint); and `ask`/`answer` are async — `ask` returns immediately with a question id, the reply lands later as a `question_answered` system event, never block a turn waiting on it inline. If `get_loose_ends` reports a large todo backlog (it caps rendered rows at 40), don't try to triage hundreds of ids by hand — call `ack_todos_until` with the suggested cutoff to bulk-clear the stale tail in one shot.
|
||||||
- **Extra MCP tools** (some agents only): `mcp__<server>__<tool>` — agent-specific (matrix client, scraper, db connector, etc.) declared in your `agent.nix` under `hyperhive.extraMcpServers`. First-class tools, already operator-approved at deploy time.
|
- **Extra MCP tools** (some agents only): `mcp__<server>__<tool>` — agent-specific (matrix client, scraper, db connector, etc.) declared in your `agent.nix` under `hyperhive.extraMcpServers`. First-class tools, already operator-approved at deploy time.
|
||||||
- **Lifecycle** (_requires `lifecycle` tool group_, direct children only, no approval needed): `restart`, `kill`, `start`, `update`, `list_containers`.
|
- **Lifecycle** (_requires `lifecycle` tool group_, direct children only, no approval needed): `restart`, `kill`, `start`, `update`, `list_containers`.
|
||||||
- **Approvals** (_requires `approvals` tool group_, queues an operator approval): `request_init_config`, `request_apply_commit`, `request_update_meta_inputs`.
|
- **Approvals** (_requires `approvals` tool group_, queues an operator approval): `request_init_config`, `request_apply_commit`, `request_update_meta_inputs`.
|
||||||
|
|
|
||||||
|
|
@ -202,6 +202,7 @@ fn dispatch(
|
||||||
} => clear_todo(store, &subsystem, key.as_deref(), all),
|
} => clear_todo(store, &subsystem, key.as_deref(), all),
|
||||||
Request::ListTodos { subsystem } => list_todos(store, subsystem.as_deref()),
|
Request::ListTodos { subsystem } => list_todos(store, subsystem.as_deref()),
|
||||||
Request::MarkTodoDone { id } => mark_todo_done(store, id),
|
Request::MarkTodoDone { id } => mark_todo_done(store, id),
|
||||||
|
Request::MarkTodosDoneUntil { up_to } => mark_todos_done_until(store, up_to),
|
||||||
Request::StoreReminder {
|
Request::StoreReminder {
|
||||||
message,
|
message,
|
||||||
timing,
|
timing,
|
||||||
|
|
@ -417,6 +418,21 @@ fn mark_todo_done(store: &Todos, id: i64) -> Response {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// `MarkTodosDoneUntil` handler: bulk-acks every un-acked todo id at or
|
||||||
|
/// below `up_to` — the escape hatch for a backlog too large to triage
|
||||||
|
/// one-by-one (see `Todos::mark_done_until`'s doc for why it's needed).
|
||||||
|
fn mark_todos_done_until(store: &Todos, up_to: i64) -> Response {
|
||||||
|
match store.mark_done_until(up_to) {
|
||||||
|
Ok(count) => {
|
||||||
|
tracing::debug!(up_to, count, "todo bulk mark-done");
|
||||||
|
Response::Acked {
|
||||||
|
count: u64::try_from(count).unwrap_or(0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => err(&e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// `Request::Compact` handler: gate on context usage, then queue the same
|
/// `Request::Compact` handler: gate on context usage, then queue the same
|
||||||
/// deferred `compact_pending` flag the operator's `/compact` button sets.
|
/// deferred `compact_pending` flag the operator's `/compact` button sets.
|
||||||
/// Mirrors `hive-agent::web_ui::actions::post_compact` but reachable from
|
/// Mirrors `hive-agent::web_ui::actions::post_compact` but reachable from
|
||||||
|
|
|
||||||
|
|
@ -245,6 +245,34 @@ impl Todos {
|
||||||
Ok(n)
|
Ok(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Bulk-ack every un-acked todo with `id <= up_to` in one shot — the
|
||||||
|
/// same "note the highest id you've seen, clear everything up to it"
|
||||||
|
/// pattern the message inbox's `ack_until` already gives agents, applied
|
||||||
|
/// to this store. Exists for the case a per-id triage loop isn't worth
|
||||||
|
/// it: an agent that hasn't called `get_loose_ends` in a long stretch
|
||||||
|
/// (or one whose producers pile up faster than it triages) can end up
|
||||||
|
/// with a backlog large enough that the rendered list itself becomes
|
||||||
|
/// unwieldy — clearing it in one call is the escape hatch, same
|
||||||
|
/// `acked`-not-deleted semantics as [`Self::mark_done`] (a reconciled
|
||||||
|
/// producer's next `upsert` still sees the row to compare against).
|
||||||
|
/// Returns the number of rows newly acked.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Propagates the sqlite update failure.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if the connection mutex is poisoned.
|
||||||
|
pub fn mark_done_until(&self, up_to: i64) -> Result<usize> {
|
||||||
|
let conn = self.conn.lock().unwrap();
|
||||||
|
let n = conn.execute(
|
||||||
|
"UPDATE todos SET acked = 1, acked_at = ?1 WHERE acked = 0 AND id <= ?2",
|
||||||
|
params![Utc::now().timestamp(), up_to],
|
||||||
|
)?;
|
||||||
|
Ok(n)
|
||||||
|
}
|
||||||
|
|
||||||
/// List todos, newest-updated first. `subsystem = Some(..)` filters to
|
/// List todos, newest-updated first. `subsystem = Some(..)` filters to
|
||||||
/// one producer's set; `None` returns all. Excludes acked rows — once
|
/// one producer's set; `None` returns all. Excludes acked rows — once
|
||||||
/// the agent has dismissed a todo it stays out of its own list, even
|
/// the agent has dismissed a todo it stays out of its own list, even
|
||||||
|
|
@ -476,6 +504,27 @@ mod tests {
|
||||||
assert!(!s.has_any().unwrap(), "acked-only table reads as empty");
|
assert!(!s.has_any().unwrap(), "acked-only table reads as empty");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// `mark_done_until` acks everything at-or-below the threshold id and
|
||||||
|
/// leaves later rows untouched, mirroring `ack_until`'s inbox semantics.
|
||||||
|
#[test]
|
||||||
|
fn mark_done_until_acks_up_to_threshold_only() {
|
||||||
|
let (_dir, s) = store();
|
||||||
|
let (id1, _) = s.upsert("bash", None, "task 1", None).unwrap();
|
||||||
|
let (id2, _) = s.upsert("bash", None, "task 2", None).unwrap();
|
||||||
|
let (id3, _) = s.upsert("bash", None, "task 3", None).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
s.mark_done_until(id2).unwrap(),
|
||||||
|
2,
|
||||||
|
"acks id1 and id2, not id3"
|
||||||
|
);
|
||||||
|
let left = s.list(None).unwrap();
|
||||||
|
assert_eq!(left.len(), 1);
|
||||||
|
assert_eq!(left[0].id, id3);
|
||||||
|
// Idempotent: re-running over the same range acks nothing new.
|
||||||
|
assert_eq!(s.mark_done_until(id2).unwrap(), 0);
|
||||||
|
let _ = id1;
|
||||||
|
}
|
||||||
|
|
||||||
/// `reap_acked` only removes acked rows past the cutoff — a recent ack
|
/// `reap_acked` only removes acked rows past the cutoff — a recent ack
|
||||||
/// and any un-acked row both survive.
|
/// and any un-acked row both survive.
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue