Skip to content

fix(comm,gtd): backlog burn batch A — inbox sender filters, thread cursor pagination, message tags, gtd limit docs (#493 #494 #495 #744)#757

Merged
oceanwaves630 merged 6 commits into
mainfrom
fix/burn-batch-a
Jul 9, 2026
Merged

fix(comm,gtd): backlog burn batch A — inbox sender filters, thread cursor pagination, message tags, gtd limit docs (#493 #494 #495 #744)#757
oceanwaves630 merged 6 commits into
mainfrom
fix/burn-batch-a

Conversation

@oceanwaves630

Copy link
Copy Markdown
Collaborator

Backlog-burn batch A, four small fixes off the 2026-07-09 triage.

Review: two rounds, final verdict APPROVE (r1 APPROVE-WITH-FIXES → cursor rework in 67d3cc3 + doc-comment alignment; r2 confirmed the High closed, no re-review needed). Gates: fmt/clippy clean, 188 khive-pack-comm tests passing (124 integration + 50 unit + 14 contract), 5 new cursor regression tests.

Closes #493. Closes #494. Closes #495. Closes #744.

🤖 Generated with Claude Code

oceanwaves630 and others added 6 commits July 9, 2026 08:05
)

InboxParams gains optional from_actor (exact match) and from_prefix (prefix
match on properties.from_actor), mutually exclusive. When either is set,
handle_inbox scans pages beyond the SQL-pushed direction/status/to_actor
filters and applies the sender match in Rust (FilterOp has no prefix
operator), collecting until limit matches or the store is exhausted. Absent
both params, behavior is byte-identical to before.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
ThreadParams gains optional order ("asc" default | "desc") and after (a
message id or RFC 3339 timestamp cursor). handle_thread already loads the
full thread into memory before sorting/truncating, so order=desc reverses
the sort comparator so truncate(limit) keeps the newest messages instead of
the oldest, and after filters to created_at strictly greater than the
resolved cursor before sorting. Default behavior (no order/after) is
byte-identical to before.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
SendParams and ReplyParams gain optional tags: Vec<String>, persisted
verbatim to properties["tags"] on both the outbound and inbound copies
(dual_write_message grows a tags parameter, mirroring the existing
from_actor/to_actor/references_chain flat-arg pattern). Mirrors the shipped
memory.remember tags precedent. deny_unknown_fields is unaffected — typo
kwargs are still rejected. Absent tags omits the properties.tags key
entirely, unchanged from before.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
)

Both handle_next and handle_tasks return a bare JSON array (Value::Array),
consumed via .as_array() throughout this crate's own tests and downstream
(kkernel, li surfaces) — adding a sibling truncated field per the issue's
preferred ask would require wrapping the response in an object, a breaking
response-shape change, not an additive one. Implements the issue's stated
fallback instead: the limit ParamDef on both verbs now documents the 200
cap, and the clamp call sites carry an inline note explaining why. Cap
behavior itself is unchanged (still an unsignaled clamp at the JSON level).

Judgment call: issue #744 prefers a truncated response field; this PR
implements fallback ask 1 (document the cap) because the response shape
cannot change additively. See impl_report_burna.md for detail.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
comm.thread(after=...) previously resolved an id cursor to only the note's
created_at timestamp string and compared it lexicographically, so:
- two messages sharing the same microsecond created_at (e.g. ADR-057
  dual-write self-send copies) could be skipped or duplicated around an
  id cursor,
- non-canonical but valid RFC 3339 timestamp cursors (whole-second Z,
  +00:00 offset) could compare incorrectly against khive's canonical
  microsecond ISO format,
- order="desc" only flipped the final sort; the cursor filter direction
  stayed a plain `>` regardless of order.

Fix: sort and filter on the total-order tuple (created_at, full_id).
An id cursor now carries the referenced note's full tuple for
tie-breaking. A timestamp cursor is parsed to microseconds via chrono
before comparison (matching the pattern in khive-pack-brain and
khive-vcs), and an unparseable/unresolvable after value is now a hard
per-op error instead of being silently treated as a literal cursor
string. The cursor filter direction is explicit per order (desc keeps
strictly-older rows in the desc sequence, not always created_at >).

Adds 4 regression tests: equal-created_at id-cursor tie-break,
non-canonical RFC 3339 cursor forms, invalid-cursor hard error, and
order=desc + after combined.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…emantics (codex r2 Low)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@oceanwaves630 oceanwaves630 enabled auto-merge (squash) July 9, 2026 12:41

@ohdearquant ohdearquant left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gate verified: (created_at, full_id) total-order cursor with fail-loud RFC3339 parsing matches the codex High; 188 pack tests green.

@oceanwaves630 oceanwaves630 merged commit ccbcd33 into main Jul 9, 2026
13 checks passed
@oceanwaves630 oceanwaves630 deleted the fix/burn-batch-a branch July 9, 2026 13:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants