cxs is a local-first CLI for searching Codex session logs. It is designed for progressive retrieval: find the right session first, then read
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

test(query): 锁定 mixed/session-only/message-only displayRow 契约

加 3 条测试覆盖 ranking.shouldUseDisplayRow 的展示行选择规则:
- mixed: 同 session 同时含 strong session hit 与 weak message hit 时,
displayRow 选 message (matchSeq 是 number,read-range 可重锚),
但 sessionScore 仍由 titlePhrase/titleTermHits 顶上去压过 message-only
对照 session
- session-only: 仅 session-level 命中 → matchSource = "session", matchSeq = null
- message-only: 仅 message body 命中 → matchSource = "message", matchSeq 为
number,作为对照基线

锁定"session signal 提排名 + message signal 给定位"双轨契约。对应
review 报告 P1-2。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Entire-Checkpoint: de3ec2613fde

cat 3a8bbf92 4e1136dd

+177
+177
query.test.ts
··· 457 457 expect(found.results[0]?.snippet).toContain("check"); 458 458 }); 459 459 460 + test("mixed session/message hit prefers message displayRow but keeps session ranking signal", () => { 461 + const base = mkdtempSync(join(tmpdir(), "cxs-mixed-match-")); 462 + tempDirs.push(base); 463 + const dbPath = join(base, "index.sqlite"); 464 + const db = openWriteDb(dbPath); 465 + 466 + // Mixed session: title carries the query (strong session hit) AND one 467 + // message body also carries it (weaker message hit). The display row 468 + // must come from the message hit so read-range can re-anchor on a real 469 + // seq, but the session-level signal still has to outrank a peer that 470 + // only has the message hit. 471 + replaceSession(db, { 472 + sessionUuid: "60606060-6060-4606-8606-606060606060", 473 + filePath: join(base, "mixed.jsonl"), 474 + title: "payloadbeacon retry handoff", 475 + summaryText: "", 476 + compactText: "", 477 + reasoningSummaryText: "", 478 + cwd: "/tmp/mixed", 479 + model: "gpt-5.4", 480 + startedAt: "2026-04-24T01:00:00.000Z", 481 + endedAt: "2026-04-24T01:00:00.000Z", 482 + messages: [ 483 + { 484 + role: "user", 485 + contentText: "noticed payloadbeacon stalled in production", 486 + timestamp: "2026-04-24T01:00:00.000Z", 487 + seq: 0, 488 + sourceKind: "event_msg", 489 + }, 490 + { 491 + role: "assistant", 492 + contentText: "checking retry queue depth and surface", 493 + timestamp: "2026-04-24T01:00:30.000Z", 494 + seq: 1, 495 + sourceKind: "event_msg", 496 + }, 497 + ], 498 + }, 1, 1, INDEX_VERSION); 499 + 500 + // Message-only control: query appears only in a message body, neither 501 + // title nor any session-level field carries it. 502 + replaceSession(db, { 503 + sessionUuid: "70707070-7070-4707-8707-707070707070", 504 + filePath: join(base, "message-only.jsonl"), 505 + title: "neutral retry surface review", 506 + summaryText: "", 507 + compactText: "", 508 + reasoningSummaryText: "", 509 + cwd: "/tmp/message-only", 510 + model: "gpt-5.4", 511 + startedAt: "2026-04-24T01:00:00.000Z", 512 + endedAt: "2026-04-24T01:00:00.000Z", 513 + messages: [ 514 + { 515 + role: "user", 516 + contentText: "saw payloadbeacon mentioned once in passing", 517 + timestamp: "2026-04-24T01:00:00.000Z", 518 + seq: 0, 519 + sourceKind: "event_msg", 520 + }, 521 + ], 522 + }, 1, 1, INDEX_VERSION); 523 + 524 + db.close(); 525 + 526 + const found = findSessions(dbPath, "payloadbeacon", 5); 527 + 528 + const mixed = found.results.find( 529 + (result) => result.sessionUuid === "60606060-6060-4606-8606-606060606060", 530 + ); 531 + const messageOnly = found.results.find( 532 + (result) => result.sessionUuid === "70707070-7070-4707-8707-707070707070", 533 + ); 534 + 535 + expect(mixed).toBeDefined(); 536 + expect(messageOnly).toBeDefined(); 537 + 538 + // Display row must come from the message hit so read-range can anchor 539 + // on a real seq. 540 + expect(mixed?.matchSource).toBe("message"); 541 + expect(typeof mixed?.matchSeq).toBe("number"); 542 + 543 + // Session-level signal still wins overall ranking and score. 544 + expect(found.results[0]?.sessionUuid).toBe("60606060-6060-4606-8606-606060606060"); 545 + expect(mixed!.score).toBeGreaterThan(messageOnly!.score); 546 + }); 547 + 548 + test("session-only hit reports matchSource session and null matchSeq", () => { 549 + const base = mkdtempSync(join(tmpdir(), "cxs-session-only-match-")); 550 + tempDirs.push(base); 551 + const dbPath = join(base, "index.sqlite"); 552 + const db = openWriteDb(dbPath); 553 + 554 + replaceSession(db, { 555 + sessionUuid: "80808080-8080-4808-8808-808080808080", 556 + filePath: join(base, "session-only.jsonl"), 557 + title: "payloadbeacon postmortem outline", 558 + summaryText: "", 559 + compactText: "", 560 + reasoningSummaryText: "", 561 + cwd: "/tmp/session-only", 562 + model: "gpt-5.4", 563 + startedAt: "2026-04-24T01:00:00.000Z", 564 + endedAt: "2026-04-24T01:00:00.000Z", 565 + messages: [ 566 + { 567 + role: "user", 568 + contentText: "everything looked fine on the surface", 569 + timestamp: "2026-04-24T01:00:00.000Z", 570 + seq: 0, 571 + sourceKind: "event_msg", 572 + }, 573 + { 574 + role: "assistant", 575 + contentText: "agreed, no anomalies in the queue depth", 576 + timestamp: "2026-04-24T01:00:30.000Z", 577 + seq: 1, 578 + sourceKind: "event_msg", 579 + }, 580 + ], 581 + }, 1, 1, INDEX_VERSION); 582 + 583 + db.close(); 584 + 585 + const found = findSessions(dbPath, "payloadbeacon", 5); 586 + 587 + expect(found.results).toHaveLength(1); 588 + expect(found.results[0]?.sessionUuid).toBe("80808080-8080-4808-8808-808080808080"); 589 + expect(found.results[0]?.matchSource).toBe("session"); 590 + expect(found.results[0]?.matchSeq).toBeNull(); 591 + }); 592 + 593 + test("message-only hit reports matchSource message with a numeric matchSeq", () => { 594 + const base = mkdtempSync(join(tmpdir(), "cxs-message-only-match-")); 595 + tempDirs.push(base); 596 + const dbPath = join(base, "index.sqlite"); 597 + const db = openWriteDb(dbPath); 598 + 599 + replaceSession(db, { 600 + sessionUuid: "90909090-9090-4909-8909-909090909090", 601 + filePath: join(base, "message-only-baseline.jsonl"), 602 + title: "neutral retry surface review", 603 + summaryText: "", 604 + compactText: "", 605 + reasoningSummaryText: "", 606 + cwd: "/tmp/message-only-baseline", 607 + model: "gpt-5.4", 608 + startedAt: "2026-04-24T01:00:00.000Z", 609 + endedAt: "2026-04-24T01:00:00.000Z", 610 + messages: [ 611 + { 612 + role: "assistant", 613 + contentText: "kicked off neutral diagnostics", 614 + timestamp: "2026-04-24T01:00:00.000Z", 615 + seq: 0, 616 + sourceKind: "event_msg", 617 + }, 618 + { 619 + role: "user", 620 + contentText: "found payloadbeacon in the trace", 621 + timestamp: "2026-04-24T01:00:30.000Z", 622 + seq: 1, 623 + sourceKind: "event_msg", 624 + }, 625 + ], 626 + }, 1, 1, INDEX_VERSION); 627 + 628 + db.close(); 629 + 630 + const found = findSessions(dbPath, "payloadbeacon", 5); 631 + 632 + expect(found.results).toHaveLength(1); 633 + expect(found.results[0]?.matchSource).toBe("message"); 634 + expect(found.results[0]?.matchSeq).toBe(1); 635 + }); 636 + 460 637 test("find keeps distinct sessions even when titles collapse to the same normalized key", async () => { 461 638 const base = mkdtempSync(join(tmpdir(), "cxs-dedup-")); 462 639 tempDirs.push(base);