Ctrl K
ComunidadGuías…

test/knowledge-ordering.test.js

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
// Locale-independent ordering for opaque Knowledge ids and refs.
//
// Knowledge refs and ranked ids are base64url, so they contain both "-" (0x2D)
// and "_" (0x5F). ICU collation orders those two the other way round from UTF-16
// code units and varies with the runtime's locale data, so using localeCompare
// as the deterministic tie-break made otherwise-tied results reorder across
// machines. These tests pin the code-unit contract.
//
// Nothing here computes an expected order with localeCompare, and no test
// returns early: every assertion runs on every invocation.

var test = require("node:test");
var assert = require("node:assert/strict");
var fs = require("fs");
var os = require("os");
var path = require("path");

var knowledgeSearch = require("../lib/knowledge-search");
var attachService = require("../lib/mate-knowledge-service").attachMateKnowledgeService;
var mateSync = require("../lib/mate-knowledge-sync");

// The contract, written out independently of the implementation under test.
function codeUnit(a, b) {
  return a < b ? -1 : a > b ? 1 : 0;
}

// --- the comparator itself ------------------------------------------------

test("compareIds is UTF-16 code-unit order and disagrees with localeCompare", function () {
  // The exact characters that make base64url identifiers collation-sensitive.
  assert.equal("-".charCodeAt(0), 45);
  assert.equal("_".charCodeAt(0), 95);

  var dash = "know:-AAAAAAAAAAAAAAAAAAAAAA";
  var under = "know:_AAAAAAAAAAAAAAAAAAAAAA";

  assert.equal(knowledgeSearch.compareIds(dash, under), -1, "\"-\" sorts before \"_\"");
  assert.equal(knowledgeSearch.compareIds(under, dash), 1);
  assert.equal(knowledgeSearch.compareIds(dash, dash), 0);

  // The defect, demonstrated rather than asserted from memory. If a future
  // runtime ever agreed with code units this assertion would fail loudly, which
  // is the correct signal that the regression tests below stopped discriminating.
  assert.equal(under.localeCompare(dash), -1, "localeCompare orders \"_\" first");
  assert.notEqual(
    Math.sign(under.localeCompare(dash)),
    Math.sign(knowledgeSearch.compareIds(under, dash)),
    "the two orderings genuinely disagree on this pair"
  );

  // Equivalent to plain Array#sort, which is the documented ordering.
  var mixed = [under, dash];
  assert.deepEqual(mixed.slice().sort(knowledgeSearch.compareIds), [dash, under]);
  assert.deepEqual(mixed.slice().sort(), [dash, under]);
  assert.deepEqual(mixed.slice().sort(knowledgeSearch.compareIds), mixed.slice().sort());
});

test("compareIds converts deterministically before comparing", function () {
  assert.equal(knowledgeSearch.compareIds(10, 9), -1, "numbers compare as their string forms");
  assert.equal(knowledgeSearch.compareIds("a", "a"), 0);
  assert.equal(knowledgeSearch.compareIds(null, "null"), 0);
  assert.equal(knowledgeSearch.compareIds(undefined, "undefined"), 0);
  // Total order: antisymmetric and transitive over a mixed alphabet.
  var ids = ["a-1", "a_1", "a01", "aA1", "b-0", "_x", "-x", "0x"];
  var sorted = ids.slice().sort(knowledgeSearch.compareIds);
  assert.deepEqual(sorted, ids.slice().sort(), "matches default lexical order");
  for (var i = 0; i < ids.length; i++) {
    for (var j = 0; j < ids.length; j++) {
      assert.equal(
        Math.sign(knowledgeSearch.compareIds(ids[i], ids[j])),
        Math.sign(-knowledgeSearch.compareIds(ids[j], ids[i])) || 0,
        "antisymmetric for " + ids[i] + " / " + ids[j]
      );
    }
  }
});

// --- knowledge-search collapsed segment ordering --------------------------

// rank() lets the caller choose ids outright, so the "-" versus "_" case is
// exercised directly here rather than left to whatever refs happen to be minted.
function rankWithIds(ids, text) {
  var items = ids.map(function (id) { return { id: id, text: text }; });
  return knowledgeSearch.rank(items, "shared", function (item) {
    return { id: item.id, fields: [{ text: item.text, weight: 1 }], meta: item };
  }, items.length);
}

test("collapsed segment ranking breaks score ties by code-unit id order", function () {
  // Identical text means identical BM25 scores, so the id tie-break decides
  // the whole order.
  var ids = [
    "know:_zzzzzzzzzzzzzzzzzzzzzz",
    "know:-aaaaaaaaaaaaaaaaaaaaaa",
    "know:_aaaaaaaaaaaaaaaaaaaaaa",
    "know:-zzzzzzzzzzzzzzzzzzzzzz",
    "know:0aaaaaaaaaaaaaaaaaaaaaa",
  ];
  var ranked = rankWithIds(ids, "shared shared content");
  assert.equal(ranked.length, ids.length, "one result per item");

  var scores = ranked.map(function (hit) { return hit.score; });
  assert.equal(new Set(scores).size, 1, "the fixture really is fully tied on score");

  var got = ranked.map(function (hit) { return hit.id; });
  assert.deepEqual(got, ids.slice().sort(codeUnit), "ties resolve in code-unit order");
  assert.deepEqual(got, ids.slice().sort(), "which is default lexical order");
  assert.equal(got[0], "know:-aaaaaaaaaaaaaaaaaaaaaa", "\"-\" leads");
  assert.equal(got[got.length - 1], "know:_zzzzzzzzzzzzzzzzzzzzzz", "\"_\" trails");

  // Repeating the call is stable.
  assert.deepEqual(rankWithIds(ids, "shared shared content").map(function (h) { return h.id; }), got);
  // Input order must not leak into the result.
  assert.deepEqual(rankWithIds(ids.slice().reverse(), "shared shared content").map(function (h) { return h.id; }), got);
});

test("score still outranks the id tie-break", function () {
  var items = [
    { id: "know:_strong", text: "shared shared shared shared" },
    { id: "know:-weak", text: "shared unrelated filler words here" },
  ];
  var ranked = knowledgeSearch.rank(items, "shared", function (item) {
    return { id: item.id, fields: [{ text: item.text, weight: 1 }], meta: item };
  }, 10);
  assert.ok(ranked[0].score > ranked[1].score, "the fixture separates the scores");
  assert.equal(ranked[0].id, "know:_strong", "a better score wins despite losing the id order");
});

// --- Mate Knowledge list and search ordering ------------------------------

function tmp(label) {
  return fs.mkdtempSync(path.join(os.tmpdir(), "clay-ko-" + label + "-"));
}

// One .jsonl line becomes one Knowledge record. The fixture freezes Date.now
// while importing so every record has the same updatedAt and the service-level
// ref tie-break is exercised deterministically, independent of machine load.
var RECORD_COUNT = 260;

function buildWorkspace() {
  var home = tmp("ws");
  var baseDir = path.join(home, "knowledge");
  var mateDir = path.join(home, "mates", "alice", "mate_ord");
  var lines = [];
  for (var i = 0; i < RECORD_COUNT; i++) {
    lines.push(JSON.stringify({
      date: "2026-05-01",
      topic: "topic " + i,
      summary: "shared marker text for ordering record " + i,
    }));
  }
  fs.mkdirSync(path.join(mateDir, "knowledge"), { recursive: true });
  fs.writeFileSync(path.join(mateDir, "knowledge", "digests.jsonl"), lines.join("\n") + "\n");
  var realNow = Date.now;
  Date.now = function () { return 1770000000000; };
  try {
    mateSync.reconcileMate({ mateDir: mateDir, baseDir: baseDir, actor: { type: "migration" } });
  } finally {
    Date.now = realNow;
  }

  var session = { localId: 1, cliSessionId: "cli-ord", ownerId: "alice" };
  var manager = { sessions: new Map([[1, session]]) };
  var status = { slug: "mate-ord", path: mateDir, projectOwnerId: "alice", isMate: true, mateId: "mate_ord" };
  var projects = new Map([["mate-ord", {
    getStatus: function () { return status; },
    getSessionManager: function () { return manager; },
  }]]);
  var mate = { id: "mate_ord", name: "Ord", createdBy: "alice", builtinKey: null, dir: mateDir };

  var service = attachService({
    baseDir: baseDir,
    getProjects: function () { return projects; },
    isMultiUser: function () { return true; },
    resolveMate: function (userId, mateId) {
      return mateId === "mate_ord" && userId === "alice" ? mate : null;
    },
    listMates: function (userId) { return userId === "alice" ? [mate] : []; },
  });

  function bindFor(boundSession) {
    return service.bind({
      projectSlug: "mate-ord",
      projectOwnerId: "alice",
      isMate: true,
      mateId: "mate_ord",
      session: boundSession,
    });
  }
  return { bound: bindFor(session), bindFor: bindFor, session: session, baseDir: baseDir, manager: manager };
}

// Equivalent to deepEqual for arrays of strings, but reports the first
// divergence instead of asking Node to render a diff of hundreds of refs, which
// takes minutes and is unreadable when it finally appears.
function assertSameOrder(actual, expected, label) {
  assert.equal(actual.length, expected.length, label + ": length differs");
  for (var i = 0; i < actual.length; i++) {
    if (actual[i] === expected[i]) continue;
    assert.fail(label + ": first divergence at index " + i
      + " (got " + actual[i] + ", expected " + expected[i] + ")");
  }
}

// Reads every page, so pagination boundaries are part of the order under test.
function drain(call, pageSize) {
  var out = [];
  var cursor = null;
  var guard = 0;
  do {
    var args = { limit: pageSize };
    if (cursor) args.cursor = cursor;
    var page = call(args);
    out.push(page);
    cursor = page.nextCursor;
    guard++;
    assert.ok(guard < 100, "pagination terminates");
  } while (cursor);
  return out;
}

test("Mate Knowledge list orders by updatedAt desc then code-unit ref", function () {
  var ws = buildWorkspace();
  var pages = drain(function (args) { return ws.bound.listKnowledge(args); }, 50);
  var entries = [];
  for (var p = 0; p < pages.length; p++) entries = entries.concat(pages[p].entries);
  assert.equal(entries.length, RECORD_COUNT, "every record is paged through exactly once");
  assert.equal(pages[0].total, RECORD_COUNT);

  var refs = entries.map(function (entry) { return entry.ref; });
  assert.equal(new Set(refs).size, RECORD_COUNT, "refs are unique");

  // The documented total order, computed here without localeCompare.
  var expected = entries.slice().sort(function (a, b) {
    return b.updatedAt - a.updatedAt || codeUnit(a.ref, b.ref);
  }).map(function (entry) { return entry.ref; });
  assertSameOrder(refs, expected, "the paged sequence matches the documented total order");

  // The frozen import clock makes the tie explicit rather than statistical.
  var byStamp = {};
  for (var i = 0; i < entries.length; i++) {
    var stamp = entries[i].updatedAt;
    if (!byStamp[stamp]) byStamp[stamp] = [];
    byStamp[stamp].push(entries[i].ref);
  }
  var tied = Object.keys(byStamp).filter(function (k) { return byStamp[k].length > 1; });
  assert.equal(tied.length, 1, "the fixture creates one exact timestamp tie group");
  assert.equal(byStamp[tied[0]].length, RECORD_COUNT, "every record participates in the tie-break");
  for (var g = 0; g < tied.length; g++) {
    assertSameOrder(byStamp[tied[g]], byStamp[tied[g]].slice().sort(codeUnit), "tied group in code-unit order");
    assertSameOrder(byStamp[tied[g]], byStamp[tied[g]].slice().sort(), "tied group in default lexical order");
  }

  // Repeating the whole traversal is stable.
  var again = [];
  var repeatPages = drain(function (args) { return ws.bound.listKnowledge(args); }, 50);
  for (var r = 0; r < repeatPages.length; r++) again = again.concat(repeatPages[r].entries);
  assertSameOrder(again.map(function (entry) { return entry.ref; }), refs, "repeated calls return the same order");

  // A different page size must not change the sequence, only where it splits.
  var small = [];
  var smallPages = drain(function (args) { return ws.bound.listKnowledge(args); }, 7);
  for (var s = 0; s < smallPages.length; s++) small = small.concat(smallPages[s].entries);
  assertSameOrder(small.map(function (entry) { return entry.ref; }), refs, "pagination boundaries do not perturb the order");
  assert.ok(smallPages.length > pages.length, "the smaller page size really did split more often");
});

test("Mate Knowledge search orders by score desc, updatedAt desc, then code-unit ref", function () {
  var ws = buildWorkspace();
  var pages = drain(function (args) {
    return ws.bound.searchKnowledge(Object.assign({ query: "shared marker" }, args));
  }, 50);
  var hits = [];
  for (var p = 0; p < pages.length; p++) hits = hits.concat(pages[p].results);
  assert.equal(hits.length, RECORD_COUNT, "every matching record is paged through");

  var refs = hits.map(function (hit) { return hit.ref; });
  assert.equal(new Set(refs).size, RECORD_COUNT);

  var expected = hits.slice().sort(function (a, b) {
    return b.score - a.score || b.updatedAt - a.updatedAt || codeUnit(a.ref, b.ref);
  }).map(function (hit) { return hit.ref; });
  assert.deepEqual(refs, expected, "the paged sequence matches the documented total order");

  // Score ties are what the ref tie-break exists for.
  var byScore = {};
  for (var i = 0; i < hits.length; i++) {
    var key = String(hits[i].score) + "|" + String(hits[i].updatedAt);
    if (!byScore[key]) byScore[key] = [];
    byScore[key].push(hits[i].ref);
  }
  var tiedKeys = Object.keys(byScore).filter(function (k) { return byScore[k].length > 1; });
  assert.ok(tiedKeys.length > 0, "identical text produces score ties");
  for (var g = 0; g < tiedKeys.length; g++) {
    assertSameOrder(byScore[tiedKeys[g]], byScore[tiedKeys[g]].slice().sort(codeUnit), "tied score group in code-unit order");
    assertSameOrder(byScore[tiedKeys[g]], byScore[tiedKeys[g]].slice().sort(), "tied score group in default lexical order");
  }

  var repeated = [];
  var repeatPages = drain(function (args) {
    return ws.bound.searchKnowledge(Object.assign({ query: "shared marker" }, args));
  }, 13);
  for (var r = 0; r < repeatPages.length; r++) repeated = repeated.concat(repeatPages[r].results);
  assertSameOrder(repeated.map(function (hit) { return hit.ref; }), refs, "stable across page sizes and calls");
});

// --- nothing else moved ---------------------------------------------------

test("ordering change preserves result shapes and coverage semantics", function () {
  var ws = buildWorkspace();
  var listed = ws.bound.listKnowledge({ limit: 5 });
  assert.deepEqual(Object.keys(listed).sort(), ["degraded", "degradedScopes", "entries", "nextCursor", "total"]);
  // Both are counts of what could not be read, not flags.
  assert.equal(listed.degraded, 0);
  assert.equal(listed.degradedScopes, 0);
  assert.equal(listed.entries.length, 5);
  assert.equal(listed.total, RECORD_COUNT);
  assert.ok(listed.nextCursor, "more pages remain");

  var searched = ws.bound.searchKnowledge({ query: "shared marker", limit: 5 });
  assert.deepEqual(Object.keys(searched).sort(), [
    "coveredCharsPerRecord", "degraded", "degradedScopes", "incompleteCoverage", "nextCursor", "results", "total",
  ]);
  assert.equal(searched.coveredCharsPerRecord, knowledgeSearch.MAX_INDEXED_CHARS);
  assert.equal(searched.incompleteCoverage, 0, "short records are fully covered");
  assert.equal(searched.degraded, 0);
  assert.equal(searched.degradedScopes, 0);
  for (var i = 0; i < searched.results.length; i++) {
    assert.equal(typeof searched.results[i].score, "number");
    assert.ok(searched.results[i].score > 0, "scores are unchanged real BM25 values");
    assert.equal(typeof searched.results[i].snippet, "string");
    assert.match(searched.results[i].ref, /^know:[A-Za-z0-9_-]{24}$/);
  }

  // A ref from the ordered listing still reads back through the same binding.
  var read = ws.bound.readKnowledge({ ref: listed.entries[0].ref });
  assert.equal(read.ref, listed.entries[0].ref);
  assert.equal(typeof read.content, "string");
  assert.ok(read.content.length > 0);
});

test("a session outside the Mate still binds to nothing", function () {
  var ws = buildWorkspace();
  // A session object that the Mate's own manager does not hold cannot bind, so
  // no ordering path is reachable from it at all.
  assert.equal(ws.bindFor({ localId: 99, cliSessionId: "cli-intruder", ownerId: "mallory" }), null);
  assert.equal(ws.bindFor({ localId: 1, cliSessionId: "cli-ord", ownerId: "alice" }), null,
    "a look-alike session object is not the exact bound session");
  // The genuine binding is unaffected and is scoped to this Mate alone.
  assert.equal(ws.bound.isClay, false);
  assert.equal(ws.bound.mateId, "mate_ord");
  assert.equal(typeof ws.bound.listMateKnowledge, "undefined", "no cross-Mate tool is exposed");
  assert.equal(typeof ws.bound.searchMateKnowledge, "undefined");
});