CodeNomad/scripts/profile-outline-responsiveness.mts
Pascal André f6a18d00bd
fix: reduce compaction and cross-session responsiveness stalls
Aggregate compaction fragments before reactive SDK reduction while advancing
revision fences immediately and preserving native terminal and snapshot
authority. Keep task-tool shells keyed across payload updates and rescan bounded
membership so removals and replacements cannot leave phantom tools.

Isolate SSE callbacks and decode/dispatch diagnostics. Subscriber-local teardown
no longer destroys the healthy shared native client. Install HTTP cancellation
before preflight and fence late continuations without replaying or interrupting
already-admitted mutations. Preserve ownership and environment admission checks.

Move HTTP password derivation to async scrypt with serialized password changes
and login-authority fencing. Yield cooperatively in both structural-index phases
without changing snapshot/digest protocols; an individual SQLite step stays sync.

Add isolated red/green regressions, bounded browser/native qualification scripts,
independent reviews, source fingerprints and performance evidence for issue #824.
Synchronize the Git focus assertion with its async contract and make the opt-in
trace self-contained instead of serializing a tsx helper into the browser.

Validation includes 793 passing server tests and 6 skips after replay, UI/server/
Electron typechecks, UI/server/plugin builds, and private Windows native 2.0.21
fixtures. Renderer fixture median main-thread cost is 324.57 to 16.94 ms.
The latest full browser run remains 358 pass, 1 fail, 1 skip: its trace failure
was subsequently reproduced and corrected with a minimal Chromium check, but
the full suite has not been rerun. Store assertions use force-exit; natural
cleanup and Linux Electron to Windows LAN/HTTP with 2.0.19 remain unqualified.

Publish as a draft for review, not a universal resolution or issue-closing fix.
Refs #824.
2026-10-02 00:40:45 +02:00

57 lines
3.1 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { DatabaseSync } from "node:sqlite"
import { performance } from "node:perf_hooks"
import { setImmediate as turn } from "node:timers/promises"
import { execFileSync } from "node:child_process"
import { fileURLToPath } from "node:url"
import { buildSync } from "esbuild"
import { readSessionOutline as currentOutline } from "../packages/server/src/opencode/session-pruning/outline-index"
const baseline = process.argv.includes("--baseline")
let readSessionOutline = currentOutline
if (baseline) {
// Compile the immutable pre-mission source without changing the working tree.
const directory = fileURLToPath(new URL("../packages/server/src/opencode/session-pruning/", import.meta.url))
const source = execFileSync("git", ["show", "366830f6:packages/server/src/opencode/session-pruning/outline-index.ts"],
{ cwd: directory, encoding: "utf8" })
const built = buildSync({ stdin: { contents: source, loader: "ts", resolveDir: directory }, bundle: true,
platform: "node", format: "esm", write: false })
readSessionOutline = (await import(`data:text/javascript;base64,${Buffer.from(built.outputFiles[0].text).toString("base64")}`)).readSessionOutline
}
// Synthetic memory-only database. Never discovers or opens an OpenCode database.
const count = Number(process.argv[2] ?? 256)
const bytes = Number(process.argv[3] ?? 262144)
if (!Number.isInteger(count) || count < 1 || count > 1024 || !Number.isInteger(bytes) || bytes < 1 || bytes > 1048576) {
throw new Error("Use 1–1024 rows and 1–1048576 payload bytes per row")
}
const db = new DatabaseSync(":memory:")
try {
db.exec(`CREATE TABLE session_v2(id TEXT,directory TEXT,project_id TEXT,workspace_id TEXT,revert TEXT);
CREATE TABLE session_message(id TEXT PRIMARY KEY,session_id TEXT,type TEXT,seq INTEGER,data TEXT,time_updated INTEGER DEFAULT 0);
CREATE UNIQUE INDEX session_message_session_seq_idx ON session_message(session_id,seq);
INSERT INTO session_v2 VALUES ('s','/repo','p',NULL,NULL);`)
const data = JSON.stringify({ content: [{ type: "tool", name: "read", state: { content: [{ type: "text", text: "x".repeat(bytes) }] } }] })
const insert = db.prepare("INSERT INTO session_message(id,session_id,type,seq,data) VALUES (?,'s','assistant',?,?)")
for (let i = 0; i < count; i++) insert.run(`m${i}`, i, data)
for (let sample = 0; sample < 3; sample++) {
await turn()
const gaps: number[] = []
let previous = performance.now()
const start = previous
let stopped = false
const tick = () => {
if (stopped) return
const now = performance.now()
gaps.push(now - previous)
previous = now
setImmediate(tick)
}
setImmediate(tick)
const result = await readSessionOutline(db, { directory: "/repo", projectID: "p", sessionID: "s" }, undefined, new AbortController().signal)
const elapsed = performance.now() - start
await turn()
stopped = true
console.log(JSON.stringify({ baseline, sample, count, bytes, entries: result.status === "outline" ? result.entries.length : -1,
elapsedMs: Math.round(elapsed), maxTurnGapMs: Math.round(Math.max(...gaps)), turns: gaps.length }))
}
} finally { db.close() }