mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-09 01:49:01 +08:00
test: cover trajectory rows in sqlite flip proof
This commit is contained in:
@@ -1746,3 +1746,8 @@
|
||||
{"type":"comment","timestamp":"2026-07-03T17:36:01.634601Z","issue_id":"oc-08c.8","payload":{"body":"Final proof: focused trajectory/cleanup/doctor/budget test set passed (6 Vitest shards, 142 tests), pnpm tsgo:core passed, pnpm docs:list passed, and git diff --check passed. Autoreview was attempted but blocked before review by the local codex wrapper pointing at a missing vendored binary: /Users/josh/.nvm/versions/node/v22.22.1/lib/node_modules/@openai/codex/node_modules/@openai/codex-darwin-arm64/vendor/aarch64-apple-darwin/codex/codex ENOENT."}}
|
||||
{"type":"close","timestamp":"2026-07-03T17:36:05.80934Z","issue_id":"oc-08c.8","payload":{}}
|
||||
{"type":"close","timestamp":"2026-07-03T17:36:06.39963Z","issue_id":"oc-08c","payload":{}}
|
||||
{"type":"create","timestamp":"2026-07-03T17:41:59.889705Z","issue_id":"oc-a85","payload":{"description":"Extend the SQLite sessions/transcripts flip proof harness with trajectory runtime-row evidence: append a marker-backed trajectory event through the SDK/runtime seam, assert it lands in trajectory_runtime_events, and assert no JSONL or pointer sidecar appears during the flip proof.","priority":"1","title":"AN-0009: add trajectory coverage to SQLite flip E2E","type":"task"}}
|
||||
{"type":"status_update","timestamp":"2026-07-03T17:42:03.5015Z","issue_id":"oc-a85","payload":{"status":"in_progress"}}
|
||||
{"type":"rename","timestamp":"2026-07-03T17:42:03.5015Z","issue_id":"oc-a85","payload":{"new_id":"oc-08c.9"}}
|
||||
{"type":"dep_add","timestamp":"2026-07-03T17:42:03.5015Z","issue_id":"oc-08c.9","payload":{"dep_type":"parent-child","depends_on":"oc-08c"}}
|
||||
{"type":"close","timestamp":"2026-07-03T17:44:47.739182Z","issue_id":"oc-08c.9","payload":{}}
|
||||
|
||||
@@ -107,10 +107,14 @@ Each step snapshots before and after state and writes a structured assertion
|
||||
record:
|
||||
|
||||
- SQLite row counts advance only where expected.
|
||||
- Trajectory runtime rows advance for marker-backed proof sessions that record
|
||||
runtime events.
|
||||
- The proof session row has the expected `session_id`, status, timestamps,
|
||||
metadata, and route rows.
|
||||
- Gateway history/session projection matches the SQLite transcript tail.
|
||||
- No proof-session JSONL file is created or modified.
|
||||
- No proof-session `.trajectory.jsonl`, `.trajectory-path.json`, or
|
||||
marker-derived `trajectory/<session>.jsonl` sidecar is created.
|
||||
- Existing legacy JSONL files and `sessions.json` remain unchanged unless the
|
||||
step is explicitly an offline migration or archive operation.
|
||||
- The Gateway process does not open `.jsonl` or `sessions.json` handles.
|
||||
|
||||
@@ -22,6 +22,8 @@ import {
|
||||
getSessionEntry as getSdkSessionEntry,
|
||||
listSessionEntries as listSdkSessionEntries,
|
||||
loadTranscriptEventsSync as loadSdkTranscriptEventsSync,
|
||||
appendTrajectoryRuntimeEvents,
|
||||
type SessionStoreTrajectoryEvent,
|
||||
} from "../../src/plugin-sdk/session-store-runtime.js";
|
||||
import {
|
||||
appendSessionTranscriptMessageByIdentity,
|
||||
@@ -74,6 +76,7 @@ type SqliteSessionEntryEvidence = {
|
||||
entry?: Record<string, unknown>;
|
||||
sessionId: string;
|
||||
sessionKey: string;
|
||||
trajectoryEvents: number;
|
||||
transcriptEvents: number;
|
||||
};
|
||||
|
||||
@@ -82,6 +85,7 @@ type SqliteEvidence = {
|
||||
path: string;
|
||||
sessionEntries: number;
|
||||
sessions: number;
|
||||
trajectoryRuntimeEvents: number;
|
||||
transcriptEvents: number;
|
||||
trackedEntries: SqliteSessionEntryEvidence[];
|
||||
};
|
||||
@@ -98,6 +102,9 @@ type ProofCheckpoint = {
|
||||
|
||||
type PluginSdkConsumerEvidence = {
|
||||
activeJsonlForSessionExists: boolean;
|
||||
activeTrajectoryPointerForSessionExists: boolean;
|
||||
activeTrajectoryRuntimeSidecarForSessionExists: boolean;
|
||||
activeTrajectorySessionSidecarForSessionExists: boolean;
|
||||
appendedMessageId: string;
|
||||
identityMemoryKey: string;
|
||||
latestAssistantTextBeforeAppend: string;
|
||||
@@ -107,6 +114,9 @@ type PluginSdkConsumerEvidence = {
|
||||
sessionId: string;
|
||||
sessionKey: string;
|
||||
storeTranscriptEvents: number;
|
||||
trajectoryEventsAfterAppend: number;
|
||||
trajectoryEventsBeforeAppend: number;
|
||||
trajectoryEventType: string;
|
||||
transcriptEventsAfterAppend: number;
|
||||
transcriptEventsBeforeAppend: number;
|
||||
};
|
||||
@@ -1262,10 +1272,43 @@ async function runPluginSdkConsumerProbe(
|
||||
const transcriptEventsBeforeAppend = (await readSessionTranscriptEvents(scope)).length;
|
||||
const storeTranscriptEvents = loadSdkTranscriptEventsSync(scope).length;
|
||||
const activeJsonlPath = path.join(context.activeSessionsDir, `${sessionId}.jsonl`);
|
||||
const activeTrajectorySessionSidecarPath = path.join(
|
||||
context.activeSessionsDir,
|
||||
`${sessionId}.trajectory.jsonl`,
|
||||
);
|
||||
const activeTrajectoryPointerPath = path.join(
|
||||
context.activeSessionsDir,
|
||||
`${sessionId}.trajectory-path.json`,
|
||||
);
|
||||
const activeTrajectoryRuntimeSidecarPath = path.join(
|
||||
context.activeSessionsDir,
|
||||
"trajectory",
|
||||
`${sessionId}.jsonl`,
|
||||
);
|
||||
const activeJsonlForSessionExists = fsSync.existsSync(activeJsonlPath);
|
||||
if (activeJsonlForSessionExists) {
|
||||
throw new Error(`SDK probe found active JSONL for SQLite session at ${activeJsonlPath}`);
|
||||
}
|
||||
const activeTrajectorySessionSidecarForSessionExists = fsSync.existsSync(
|
||||
activeTrajectorySessionSidecarPath,
|
||||
);
|
||||
const activeTrajectoryPointerForSessionExists = fsSync.existsSync(activeTrajectoryPointerPath);
|
||||
const activeTrajectoryRuntimeSidecarForSessionExists = fsSync.existsSync(
|
||||
activeTrajectoryRuntimeSidecarPath,
|
||||
);
|
||||
if (
|
||||
activeTrajectorySessionSidecarForSessionExists ||
|
||||
activeTrajectoryPointerForSessionExists ||
|
||||
activeTrajectoryRuntimeSidecarForSessionExists
|
||||
) {
|
||||
throw new Error(
|
||||
`SDK trajectory probe found active sidecar paths: ${JSON.stringify({
|
||||
pointer: activeTrajectoryPointerPath,
|
||||
runtime: activeTrajectoryRuntimeSidecarPath,
|
||||
session: activeTrajectorySessionSidecarPath,
|
||||
})}`,
|
||||
);
|
||||
}
|
||||
|
||||
const appended = await appendSessionTranscriptMessageByIdentity({
|
||||
...scope,
|
||||
@@ -1293,9 +1336,44 @@ async function runPluginSdkConsumerProbe(
|
||||
`SDK transcript append did not increase event count for ${context.pluginSdkSessionKey}`,
|
||||
);
|
||||
}
|
||||
const trajectoryEventsBeforeAppend = countSqliteTrajectoryRuntimeEvents(
|
||||
context.agentDbPath,
|
||||
sessionId,
|
||||
);
|
||||
const trajectoryEventType = "e2e.sdk.trajectory";
|
||||
const trajectoryEvent: SessionStoreTrajectoryEvent = {
|
||||
traceSchema: "openclaw-trajectory",
|
||||
schemaVersion: 1,
|
||||
traceId: sessionId,
|
||||
source: "runtime",
|
||||
type: trajectoryEventType,
|
||||
ts: new Date().toISOString(),
|
||||
seq: 1,
|
||||
sourceSeq: 1,
|
||||
sessionId,
|
||||
sessionKey: context.pluginSdkSessionKey,
|
||||
runId: "sqlite-flip-e2e-sdk",
|
||||
data: { proof: "sqlite trajectory runtime row" },
|
||||
};
|
||||
appendTrajectoryRuntimeEvents({
|
||||
...scope,
|
||||
events: [trajectoryEvent],
|
||||
});
|
||||
const trajectoryEventsAfterAppend = countSqliteTrajectoryRuntimeEvents(
|
||||
context.agentDbPath,
|
||||
sessionId,
|
||||
);
|
||||
if (trajectoryEventsAfterAppend <= trajectoryEventsBeforeAppend) {
|
||||
throw new Error(
|
||||
`SDK trajectory append did not increase event count for ${context.pluginSdkSessionKey}`,
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
activeJsonlForSessionExists,
|
||||
activeTrajectoryPointerForSessionExists,
|
||||
activeTrajectoryRuntimeSidecarForSessionExists,
|
||||
activeTrajectorySessionSidecarForSessionExists,
|
||||
appendedMessageId: appended.messageId,
|
||||
identityMemoryKey: identity.memoryKey,
|
||||
latestAssistantTextBeforeAppend: latestBefore.text,
|
||||
@@ -1305,6 +1383,9 @@ async function runPluginSdkConsumerProbe(
|
||||
sessionId,
|
||||
sessionKey: context.pluginSdkSessionKey,
|
||||
storeTranscriptEvents,
|
||||
trajectoryEventsAfterAppend,
|
||||
trajectoryEventsBeforeAppend,
|
||||
trajectoryEventType,
|
||||
transcriptEventsAfterAppend,
|
||||
transcriptEventsBeforeAppend,
|
||||
};
|
||||
@@ -2069,6 +2150,22 @@ function countSqliteTranscriptEvents(dbPath: string, sessionId: string): number
|
||||
}
|
||||
}
|
||||
|
||||
function countSqliteTrajectoryRuntimeEvents(dbPath: string, sessionId: string): number {
|
||||
if (!fsSync.existsSync(dbPath)) {
|
||||
return 0;
|
||||
}
|
||||
const db = new DatabaseSync(dbPath, { readOnly: true });
|
||||
try {
|
||||
return scalarNumber(
|
||||
db,
|
||||
"SELECT COUNT(*) AS count FROM trajectory_runtime_events WHERE session_id = ?",
|
||||
[sessionId],
|
||||
);
|
||||
} finally {
|
||||
db.close();
|
||||
}
|
||||
}
|
||||
|
||||
async function captureCheckpoint(
|
||||
context: ProofContext,
|
||||
label: string,
|
||||
@@ -2221,6 +2318,7 @@ function readSqliteEvidence(dbPath: string, trackedSessionKeys: readonly string[
|
||||
sessionEntries: 0,
|
||||
sessions: 0,
|
||||
trackedEntries: [],
|
||||
trajectoryRuntimeEvents: 0,
|
||||
transcriptEvents: 0,
|
||||
};
|
||||
}
|
||||
@@ -2232,6 +2330,10 @@ function readSqliteEvidence(dbPath: string, trackedSessionKeys: readonly string[
|
||||
path: dbPath,
|
||||
sessionEntries: scalarNumber(db, "SELECT COUNT(*) AS count FROM session_entries"),
|
||||
sessions: scalarNumber(db, "SELECT COUNT(*) AS count FROM sessions"),
|
||||
trajectoryRuntimeEvents: scalarNumber(
|
||||
db,
|
||||
"SELECT COUNT(*) AS count FROM trajectory_runtime_events",
|
||||
),
|
||||
trackedEntries,
|
||||
transcriptEvents: scalarNumber(db, "SELECT COUNT(*) AS count FROM transcript_events"),
|
||||
};
|
||||
@@ -2262,6 +2364,11 @@ function readTrackedEntries(
|
||||
const result: SqliteSessionEntryEvidence = {
|
||||
sessionId,
|
||||
sessionKey: typeof row.sessionKey === "string" ? row.sessionKey : "",
|
||||
trajectoryEvents: scalarNumber(
|
||||
db,
|
||||
"SELECT COUNT(*) AS count FROM trajectory_runtime_events WHERE session_id = ?",
|
||||
[sessionId],
|
||||
),
|
||||
transcriptEvents: scalarNumber(
|
||||
db,
|
||||
"SELECT COUNT(*) AS count FROM transcript_events WHERE session_id = ?",
|
||||
|
||||
@@ -157,22 +157,32 @@ describe("SQLite sessions/transcripts flip proof harness", () => {
|
||||
).toBe(true);
|
||||
expect(report.pluginSdkConsumer).toMatchObject({
|
||||
activeJsonlForSessionExists: false,
|
||||
activeTrajectoryPointerForSessionExists: false,
|
||||
activeTrajectoryRuntimeSidecarForSessionExists: false,
|
||||
activeTrajectorySessionSidecarForSessionExists: false,
|
||||
latestAssistantTextBeforeAppend: report.fullTurnAssistantText,
|
||||
latestAssistantTextAfterAppend: "sqlite sdk consumer appended by identity",
|
||||
sessionKey: report.pluginSdkSessionKey,
|
||||
trajectoryEventType: "e2e.sdk.trajectory",
|
||||
});
|
||||
expect(report.pluginSdkConsumer?.sessionFileMarker.startsWith("sqlite:")).toBe(true);
|
||||
expect(report.pluginSdkConsumer?.listedSessionKeys).toContain(report.pluginSdkSessionKey);
|
||||
expect(report.pluginSdkConsumer?.transcriptEventsAfterAppend).toBeGreaterThan(
|
||||
report.pluginSdkConsumer?.transcriptEventsBeforeAppend ?? 0,
|
||||
);
|
||||
expect(report.pluginSdkConsumer?.trajectoryEventsAfterAppend).toBeGreaterThan(
|
||||
report.pluginSdkConsumer?.trajectoryEventsBeforeAppend ?? 0,
|
||||
);
|
||||
expect(
|
||||
report.checkpoints.some(
|
||||
(checkpoint) =>
|
||||
checkpoint.label === "after-plugin-sdk-consumer" &&
|
||||
checkpoint.sqlite.trajectoryRuntimeEvents >= 1 &&
|
||||
checkpoint.sqlite.trackedEntries.some(
|
||||
(entry) =>
|
||||
entry.sessionKey === report.pluginSdkSessionKey && entry.transcriptEvents >= 3,
|
||||
entry.sessionKey === report.pluginSdkSessionKey &&
|
||||
entry.trajectoryEvents >= 1 &&
|
||||
entry.transcriptEvents >= 3,
|
||||
),
|
||||
),
|
||||
).toBe(true);
|
||||
|
||||
Reference in New Issue
Block a user