Files
AgentHub/server/__tests__/import-correctness.test.js
nntrivi2001 57dc91585d feat: Claude Code Monitor — lanes, pipelines and a merged workspace
Internal SmartGift build of a Claude Code monitoring dashboard.

Lanes: a durable unit of parallel agent work, one per working directory,
tracked across session restarts. Managed lanes are git worktrees the
dashboard provisions and can reset or remove behind a three-check destroy
guard and a counted preflight; adopted lanes are directories you already
own and are never destroyable.

Pipelines: a lane moves through pipeline stages. A stage the agent declares
with evidence renders green; a stage inferred from the tool-event stream
renders dashed amber and never counts as done. Detection is forward-only
within a 30-minute window, and never writes the declared stage.

Workspace: one page at /run with a lane grid, the selected lane's pipeline,
and a full Claude console behind a disclosure.
2026-07-30 14:39:03 +07:00

149 lines
5.2 KiB
JavaScript

/**
* @file Import-pipeline correctness regressions.
*
* Covers three defects found in an import audit:
* 1. Duplicate subagent rows — importSession must NOT create `-subagent-N`
* rows from the main transcript's Agent blocks when subagent TRANSCRIPTS
* (parsedSubagents) exist, since those `-jsonl-` rows are authoritative.
* The fallback (no transcripts) must still create `-subagent-N` rows.
* 2. transcript_path must be persisted on import (was NULL for every imported
* session, breaking the abandon sweep / compaction scan / cost backfill).
* 3. classifyJsonl must treat the dynamic-workflow tree
* (subagents/workflows/<run>/agent-*.jsonl) as a subagent, not a top-level
* session.
*
* @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
*/
const { describe, it, before, after } = require("node:test");
const assert = require("node:assert/strict");
const path = require("path");
const fs = require("fs");
const os = require("os");
const TEST_DB = path.join(os.tmpdir(), `dashboard-import-correct-${Date.now()}-${process.pid}.db`);
process.env.DASHBOARD_DB_PATH = TEST_DB;
const dbModule = require("../db");
const { db, stmts } = dbModule;
const importHistory = require("../../scripts/import-history");
after(() => {
if (db) db.close();
for (const suffix of ["", "-wal", "-shm"]) {
try {
fs.unlinkSync(TEST_DB + suffix);
} catch {
/* ignore */
}
}
});
// A parsed subagent as parseSubagentFile would return it.
function makeSubData(agentId, agentType) {
return {
agentId,
agentType,
task: "recon",
model: "claude-haiku-4-5-20251001",
startedAt: "2026-04-18T12:00:10.000Z",
endedAt: "2026-04-18T12:01:00.000Z",
userMessages: 1,
assistantMessages: 2,
tokensByModel: {},
toolNames: ["Bash", "Read"],
thinkingBlockCount: 0,
toolEvents: [],
spawnedChildren: [],
};
}
// A parsed session as parseSessionFile would return it, with one Agent tool_use
// block. `parsedSubagents` is attached by the caller (importFromDirectory), so
// we set it explicitly here.
function makeSession(sessionId, { withTranscripts, transcriptPath }) {
const agentTs = "2026-04-18T11:59:00.000Z"; // >tolerance before the sub's start
return {
sessionId,
name: "T",
customTitle: null,
aiTitle: null,
cwd: "/tmp/proj",
model: "claude-opus-4-8",
version: null,
slug: "slug",
gitBranch: null,
transcriptPath,
startedAt: "2026-04-18T11:58:00.000Z",
endedAt: "2026-04-18T12:01:00.000Z",
teams: [],
userMessages: 1,
assistantMessages: 1,
tokensByModel: {},
messageTimestamps: ["2026-04-18T12:01:00.000Z"],
toolUses: [
{
id: "toolu_agent1",
name: "Agent",
input: { subagent_type: "Explore", description: "do x", prompt: "p" },
timestamp: agentTs,
},
],
compactions: [],
apiErrors: [],
fileModifiedAt: 0,
turnDurations: [],
entrypoint: null,
permissionMode: null,
thinkingBlockCount: 0,
toolResultErrors: [],
usageExtras: { service_tiers: [], speeds: [], inference_geos: [] },
parsedSubagents: withTranscripts ? [makeSubData("aaaa0001", "Explore")] : [],
};
}
describe("import correctness", () => {
it("does NOT create duplicate -subagent-N rows when subagent transcripts exist", () => {
const SID = "11110000-0000-4000-8000-000000000001";
importHistory.importSession(
dbModule,
makeSession(SID, { withTranscripts: true, transcriptPath: `/tmp/proj/${SID}.jsonl` })
);
const subs = stmts.listAgentsBySession.all(SID).filter((a) => a.type === "subagent");
const nSubagentN = subs.filter((a) => a.id.includes("-subagent-")).length;
const nJsonl = subs.filter((a) => a.id.includes("-jsonl-")).length;
assert.equal(nJsonl, 1, "one authoritative -jsonl- row");
assert.equal(nSubagentN, 0, "no duplicate -subagent-N row from the Agent block");
});
it("still creates -subagent-N rows when there are NO subagent transcripts", () => {
const SID = "11110000-0000-4000-8000-000000000002";
importHistory.importSession(
dbModule,
makeSession(SID, { withTranscripts: false, transcriptPath: `/tmp/proj/${SID}.jsonl` })
);
const subs = stmts.listAgentsBySession.all(SID).filter((a) => a.type === "subagent");
assert.equal(subs.filter((a) => a.id.includes("-subagent-")).length, 1, "fallback row created");
});
it("persists transcript_path on import", () => {
const SID = "11110000-0000-4000-8000-000000000003";
const tp = `/tmp/proj/${SID}.jsonl`;
importHistory.importSession(
dbModule,
makeSession(SID, { withTranscripts: false, transcriptPath: tp })
);
assert.equal(stmts.getSession.get(SID).transcript_path, tp);
});
it("classifyJsonl treats the workflow subagent tree as a subagent, not a session", () => {
const base = "/x/.claude/projects/-Users-x/sid";
assert.equal(importHistory.classifyJsonl(`${base}/subagents/agent-1.jsonl`), "subagent");
assert.equal(
importHistory.classifyJsonl(`${base}/subagents/workflows/wf_abc/agent-2.jsonl`),
"subagent"
);
assert.equal(importHistory.classifyJsonl(`/x/.claude/projects/-Users-x/sid.jsonl`), "session");
});
});