mirror of
https://github.com/multica-ai/multica.git
synced 2026-08-06 19:00:01 +02:00
fix/cloud-runtime-create-fields
3 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
7f9e4e829d |
feat(comments): thread-internal --tail pagination + reply cursor (MUL-2421) (#2846)
* feat(comments): thread-internal pagination via --tail + reply cursor (MUL-2421) Long threads inside a single issue still forced agents to read every reply once they used --thread, even after MUL-2387 fixed cross-thread noise. This adds reply-level paging so a 200-reply thread can be navigated tail-first without dragging the whole conversation into prompt context. - New SQL query ListThreadCommentsForIssuePaged: same recursive root walk as the legacy thread query, but caps reply count and supports an (created_at, id) composite cursor. Root is unconditional — even tail=0 emits it so the reader keeps the "what is this thread about" context. - Handler ListComments: parses `tail` (non-negative, ThreadTailSet flag preserves the tail=0 intent), threads it through to the paged query, and re-uses X-Multica-Next-Before / X-Multica-Next-Before-Id for the reply cursor. Cursor's meaning is now context-dependent: thread cursor under --recent, reply cursor under --thread + --tail. - CLI: new --tail flag (only valid with --thread; mutually exclusive with --recent), reply-cursor semantics for --before / --before-id when paired with --thread + --tail, stderr label flips to "Next reply cursor" so an operator copy-pasting the cursor knows which scope it scrolls. - Tests cover the new contract: tail=N keeps newest N + root, tail=0 is root-only, anchor on a nested reply still walks up, reply cursor scrolls older replies page-by-page, since combined with tail filters after the cut, and the negative-flag-combination matrix. Out of scope: prompt template update to hint at `--thread <id> --tail 30` on long threads — separate follow-up per the issue. Co-authored-by: multica-agent <github@multica.ai> * fix(comments): only emit reply cursor when older reply exists (MUL-2421) The thread-tail path emitted `X-Multica-Next-Before` whenever the page filled to exactly the requested reply count, even when there was nothing older to scroll to. So `--thread <root> --tail 3` on a thread with exactly 3 replies sent a cursor that, when followed, returned just the root — a wasted round-trip that surfaced as a phantom "older replies" affordance in the agent prompt. Switch to a `reply_limit + 1` probe: ask the SQL for one extra row, trim the oldest overflow before responding, and only emit the cursor when an older reply actually existed. The exact-boundary case (replyCount == tail with no overflow) now returns no cursor. Also documents `--thread/--tail/--recent/--before` and the cursor semantics in CLI_AND_DAEMON.md, which was the second must-fix in the MUL-2421 review. Co-authored-by: multica-agent <github@multica.ai> * fix(comments): suppress reply cursor when --since covers older replies (MUL-2421) In the thread + tail + since path the server still emitted a reply cursor whenever there was an older reply on disk, regardless of `since`. If the oldest retained reply on the page was already `<= since`, every older reply was guaranteed to be filtered out too, so the next page only ever returned the root — wasting round-trips until the agent walked the whole pre-`since` history. Mirror the recent + since suppression: when `replies[0].CreatedAt <= since`, drop the cursor. Test covers the exact case from Elon's review: tail=2 overflow, body keeps a fresher reply, but the cursor target (oldest retained reply) is already past `since` — header must be empty. Co-authored-by: multica-agent <github@multica.ai> * feat(prompt): default comment-trigger reads to --thread --tail 30 (MUL-2421) Comment-triggered agents previously defaulted the trigger-thread read to the unbounded `--thread <id> --output json`, which dumps the full thread into the prompt — exactly the kind of context bloat MUL-2387 fixed at the cross-thread layer but never bounded inside a single thread. Use the new `--tail` flag landed earlier in this PR (server + CLI) as the default for both the per-turn prompt and the runtime-config Workflow: - `--thread <trigger-id> --tail 30 --output json` is the new default. Root is always included so "what is this about" context survives. - If 30 replies aren't enough, the prompt now spells out the reply cursor: re-feed the stderr `Next reply cursor: --before <ts> --before-id <reply-id>` pair back to walk older replies. - `--recent 20` stays as the cross-thread background fallback, with an explicit callout that the same `--before` / `--before-id` flags walk *threads* (not replies) in that mode. - Available Commands core line now surfaces `--tail N` and both stderr cursor labels so non-workflow callers also discover the flag. - `--since` callouts reflect the post-MUL-2421 combinable mode names (`--thread --tail` / `--recent`). Tests (`prompt_test.go`, `execenv_test.go`) pin the new defaults and add a regression guard against the unbounded `--thread` recipe sneaking back in. Co-authored-by: multica-agent <github@multica.ai> --------- Co-authored-by: multica-agent <github@multica.ai> |
||
|
|
e19f7967b9 |
feat(prompt): thread-first comment reads for agent runs (MUL-2387) (#2816)
* feat(prompt): thread-first comment reads for agent runs (MUL-2387) PR #2787 added --thread / --recent / --before / --before-id to the ListComments API and CLI but kept the agent prompt steering at the legacy "dump everything" recipe. On a long-running issue the flat dump burns context on chatter unrelated to the trigger; agents acting on the trigger want the trigger's thread first. Prompt updates: - Comment-triggered Workflow (runtime_config.go) now anchors step 2 on `multica issue comment list <issue-id> --thread <trigger-comment-id> --output json`. Fallback offers `--recent 20 --output json` with the stderr `Next thread cursor: --before <ts> --before-id <root-id>` line feeding the next-page cursor. `--since` is preserved and explicitly marked combinable with --thread / --recent. - Per-turn buildCommentPrompt (prompt.go) carries the same thread-first guidance so a Codex-style runtime that re-reads the per-turn message every iteration gets the same steering, even if it ignores the injected runtime config. - Assignment-triggered Workflow keeps the mandatory full-history rule (MUL-1124) but now also points at `--recent 20` as the long-issue alternative — this is the place that previously had no thread-aware guidance at all. - Default fallback prompt (no trigger comment, no chat, no autopilot, no quick-create) gains the same --recent hint without --thread (no comment to anchor on). - Available Commands core line surfaces the new flags so the discovery path matches the workflow guidance. Default CLI/API semantics are unchanged: the unparameterized list still returns the full chronological dump capped at 2000, --since still works on its own, and the desktop UI is untouched. Tests: - prompt_test.go: TestBuildPromptCommentTriggerPromotesThreadReads pins --thread <triggerID>, --recent 20, the stderr cursor phrasing, and the absence of the legacy "returns all comments" prose. - prompt_test.go: TestBuildPromptDefaultMentionsRecent guards the no-trigger fallback (mentions --recent, must NOT mention --thread). - execenv_test.go: TestInjectRuntimeConfigCommentTriggerThreadFirstReads asserts the comment-triggered Workflow steers at --thread/--recent, the Available Commands line surfaces the new flags, and the legacy "read the conversation (returns all comments...)" string is gone. - execenv_test.go: TestInjectRuntimeConfigAssignmentTriggerMentionsRecent keeps the mandatory full-history rule pinned AND asserts --recent is offered as the long-issue alternative. Also fixes the recent+since cursor nit Elon flagged in #2787's second review: when `since` empties the page, the `len(seenRoot) >= recentN` check used to emit a cursor anyway. Pagination walks threads in strictly decreasing last_activity_at — if every comment in this page is <= since, every older thread's last_activity is also <= since by transitivity, so the cursor would only invite the caller into a guaranteed-empty walk. Now suppressed; new tests pin both branches (suppressed when empty, retained when at least one row passes since). MUL-2387 Co-authored-by: multica-agent <github@multica.ai> * fix(comments): suppress recent+since cursor when head thread past since (MUL-2387) Previous suppression only tripped when the `since` filter emptied the page. That missed the mixed case Elon flagged in #2787's second review: the page keeps rows from fresher threads but the head (oldest-active) thread already sits at or before `since`, so every older page is guaranteed empty too. Predicating on `headLast <= since` covers both cases. Add a recent=2 + since fixture that pins the mixed scenario: root1 (last_activity = base+3m) is filtered out, root2 stays, and the cursor is suppressed even though the body is non-empty. Co-authored-by: multica-agent <github@multica.ai> * fix(prompt): clarify --recent is paging, not a replacement (MUL-2387) Address Elon's second-pass nit on #2816: the assignment-trigger workflow in runtime_config.go used "you may switch to --recent 20", which reads as a replacement for the mandatory full-history rule. Rephrase --recent as a paging strategy ("read the full history page-by-page, not a shortcut that replaces it") so it cannot conflict with the rule it lives next to. The default per-turn prompt in prompt.go opened with "If you need comment history" — that soft conditional contradicts the runtime workflow's mandatory read. Move it to a neutral "For comment history, follow the rule in your runtime workflow file" framing that defers to whatever the workflow says (mandatory for assignment, optional elsewhere) instead of encoding its own policy. Keep the runtime/prompt dual-layer fallback intact — different runtimes propagate the config file vs. the per-turn user prompt with varying fidelity, so both surfaces need the guidance. Tests pin the new phrasing against regression: - TestBuildPromptDefaultMentionsRecent now also forbids "If you need comment history" from sneaking back in. - TestInjectRuntimeConfigAssignmentTriggerMentionsRecent now also forbids "you may switch to" / "switch to `--recent" replacement phrasing. Co-authored-by: multica-agent <github@multica.ai> --------- Co-authored-by: multica-agent <github@multica.ai> |
||
|
|
6f5fbb7813 |
feat(comments): thread-aware list with composite cursor (MUL-2340) (#2787)
* feat(comments): thread-aware list with composite cursor (MUL-2340)
Adds three optional query params to GET /api/issues/{id}/comments and the
matching `multica issue comment list` flags:
- `thread=<comment-uuid>` resolves the anchor to the thread root via a
recursive CTE (defends against any future nested replies) and returns
root + all descendants chronologically. Anchor can be any comment in
the thread, root or reply.
- `recent=<N>` returns the newest N comments for the issue, ordered
chronologically in the response.
- `before=<RFC3339>` + `before-id=<uuid>` form a composite cursor for
stable pagination of `recent`. Both must be set together; a
timestamp-only cursor is rejected because ties on `created_at` would
let the existing `(created_at ASC, id ASC)` total order skip or
duplicate rows across pages.
Flag combination rules: `thread` is exclusive with `recent` and the
cursor; both may combine with `since`. Server and CLI enforce the same
matrix; the CLI fails fast locally so callers don't pay for a 400
round-trip.
Default behaviour (no params) is unchanged — full chronological dump
capped at commentHardCap — so the desktop UI and existing `--since`
polling are untouched. Agent prompt updates land in a follow-up PR so
the new CLI capabilities ship and bake first.
Co-authored-by: multica-agent <github@multica.ai>
* fix(comments): reject cursor without recent and align CLI/server on invalid --recent (MUL-2340)
Elon's PR #2787 second review flagged two gaps in the flag combination
matrix:
- server: GET /comments?before=...&before_id=... without `recent` was
silently dropped by fetchCommentsForList (RecentN=0 fell through to
the default / since path), so callers got the full timeline instead
of the documented "before X" semantics. Now returns 400.
- CLI: --recent 0 / --recent -3 were collapsed with "flag not passed"
by `recent > 0`, so an explicit invalid value silently fell back to
the default list. Switched to Flags().Changed("recent") so explicit
non-positive values fail loudly. Also enforces that --before /
--before-id only appear with explicit --recent (mirrors the new
server-side rule).
Tests:
- server flag matrix gains `before + before_id without recent → 400`.
- CLI gains TestRunIssueCommentListFlagGuards covering `--recent 0`,
`--recent -3`, cursor-without-recent, and the thread/recent
exclusivity path under the new Changed()-based check. The mock
server fatals if a request reaches /comments, proving the guards
fire before any HTTP round-trip.
Co-authored-by: multica-agent <github@multica.ai>
* feat(comments): make `recent` thread-grouped with a thread cursor (MUL-2340)
Bohan pushed back on the row-based `recent=N` shape: comments form a tree,
not a list, and the newest N rows can come from N unrelated threads, giving
the agent N disjoint conversational tails. Replace the row-based query with
a thread-grouped one before #2787 merges so we never ship the wrong shape:
- `recent=N` now returns the N most recently active threads (root + every
descendant per thread). A thread's recency is MAX(created_at) across its
whole subtree, so a stale-but-recently-replied thread outranks an old
quiet one — exactly the property row-recent loses.
- The cursor is now a *thread* cursor: `before` = a thread's
last_activity_at, `before_id` = its root comment id. The pair walks
threads strictly less recent than the page's oldest-active thread. The
cursor surfaces via `X-Multica-Next-Before` / `X-Multica-Next-Before-Id`
response headers (empty when there are no older threads); the CLI
forwards the same pair to stderr after listing.
- Row-based `recent` is gone — there is no internal caller and the prompt
update has not shipped yet, so there is no compat surface to preserve.
- Response body shape unchanged (flat JSON array, chronological). Default
and `--since` paths untouched. Desktop UI keeps working.
Tests:
- recent=1 returns the freshest-active thread fully; recent=2 returns both
with the older-active thread first (oldest-active → freshest tail).
- Stale-but-fresh: a thread whose root is older but has a fresh reply
outranks a thread whose root is newer but quiet.
- Cursor headers emitted only on full pages; empty on the final page.
- Pagination walks threads root2 → root1 → empty, no skips/duplicates.
- Tie-break: three threads sharing last_activity_at paginate one-at-a-time
using (last_activity_at, root_id) ordering — verifies the timestamp-only
cursor failure mode is fixed for the thread case too.
Co-authored-by: multica-agent <github@multica.ai>
---------
Co-authored-by: multica-agent <github@multica.ai>
|