mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-05 06:59:42 +02:00
fix(run,terminal): merge-time fixes for the Instance count stepper and capture geometry
#454: the behaviour the PR adds had no test, so a regression test drives runGrok() at tabCount 3 and asserts three quick-start POSTs with sequential w<n>-<case> names (verified to fail against master's session-ui.js). Each caller now reads the count BEFORE its opening banner and announces it there, the way runClaude() already did, so a launch no longer prints two headers and a launch with another session already active still says how many are starting. runClaude() calls the shared _readTabCount() instead of its own copy of the 1..20 clamp, and that helper optional-chains the element read, since hoisting it above each caller's try block would otherwise let a missing #tabCount throw where the launch-error path cannot report it. #435: sizeMovedUnderLoad derived from data.source alone. `mux-visible` is not sufficient: a failed display-message cursor query makes capturePaneBuffer skip the snapshot repaint and return the raw capture, which the route still labels mux-visible, so a size that moved during such a load bought a full forced reload to repair a frame that was never positioned. It now tests Number.isFinite(data.captureRows) like its two siblings. Plus the invariants and CLAUDE.md lines promised on #435: a visible capture reports its geometry and omits it when nothing was positioned, the comparison runs on mux-visible only, and the replay is capped at one attempt and latches per session when it cannot converge. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -6577,8 +6577,17 @@ class CodemanApp {
|
||||
// of every non-shell session per page takes the full-history path, an
|
||||
// ungated comparison fires most often on the one response it cannot help.
|
||||
const framePositionsRowsAbsolutely = data.source === 'mux-visible';
|
||||
// `mux-visible` is necessary but not sufficient: when the `display-message`
|
||||
// cursor query fails, `capturePaneBuffer` skips the snapshot repaint and
|
||||
// returns the raw capture, and the route still labels a non-empty body
|
||||
// `mux-visible`. That body positions nothing and reports no geometry, so a
|
||||
// size that moved during such a load has nothing to repair, and replaying
|
||||
// would buy a second capture, a reset plus chunked rewrite, a dropped
|
||||
// WebSocket and a discarded xterm snapshot for it. The two comparisons
|
||||
// below already stand down on an absent field; this one has to as well.
|
||||
const sizeMovedUnderLoad =
|
||||
framePositionsRowsAbsolutely &&
|
||||
Number.isFinite(data.captureRows) &&
|
||||
!!dimsAtCapture &&
|
||||
!!dimsAfterLoad &&
|
||||
(dimsAfterLoad.cols !== dimsAtCapture.cols || dimsAfterLoad.rows !== dimsAtCapture.rows);
|
||||
|
||||
@@ -930,7 +930,7 @@ Object.assign(CodemanApp.prototype, {
|
||||
|
||||
async runClaude() {
|
||||
const caseName = document.getElementById('quickStartCase').value || 'testcase';
|
||||
const tabCount = Math.min(20, Math.max(1, parseInt(document.getElementById('tabCount').value) || 1));
|
||||
const tabCount = this._readTabCount();
|
||||
|
||||
const ownsLaunchTerminal = this._beginSessionLaunchStatus(
|
||||
`Starting ${tabCount} Claude session(s) in ${caseName}...`
|
||||
@@ -1257,9 +1257,14 @@ Object.assign(CodemanApp.prototype, {
|
||||
}
|
||||
},
|
||||
|
||||
/** Reads the "Instance count" stepper, clamped like runClaude()'s own copy. */
|
||||
/**
|
||||
* Reads the "Instance count" stepper, clamped to 1..20. Single source for every
|
||||
* run*(), runClaude() included. Optional-chained because callers read it BEFORE
|
||||
* their try block, to put the count in the opening banner: `#tabCount` ships
|
||||
* unconditionally today, but a throw here would escape the launch-error path.
|
||||
*/
|
||||
_readTabCount() {
|
||||
return Math.min(20, Math.max(1, parseInt(document.getElementById('tabCount').value) || 1));
|
||||
return Math.min(20, Math.max(1, parseInt(document.getElementById('tabCount')?.value) || 1));
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -1274,9 +1279,6 @@ Object.assign(CodemanApp.prototype, {
|
||||
async _launchQuickStartInstances(caseName, tabCount, label, buildBody, ownsLaunchTerminal) {
|
||||
const startNumber = this._nextCaseSessionStartNumber(caseName);
|
||||
let firstSessionId = null;
|
||||
if (tabCount > 1) {
|
||||
this._appendSessionLaunchStatus(ownsLaunchTerminal, `Starting ${tabCount} ${label} session(s) in ${caseName}...`);
|
||||
}
|
||||
for (let i = 0; i < tabCount; i++) {
|
||||
const sessionName = `w${startNumber + i}-${caseName}`;
|
||||
const res = await fetch('/api/quick-start', {
|
||||
@@ -1302,7 +1304,10 @@ Object.assign(CodemanApp.prototype, {
|
||||
const _runLoc = (this.cases || []).find(c => c.name === caseName)?.location;
|
||||
const isRemote = _runLoc === 'remote' || _runLoc === 'docker';
|
||||
|
||||
const ownsLaunchTerminal = this._beginSessionLaunchStatus(`Starting OpenCode session in ${caseName}...`);
|
||||
const tabCount = this._readTabCount();
|
||||
const ownsLaunchTerminal = this._beginSessionLaunchStatus(
|
||||
`Starting ${tabCount} OpenCode session(s) in ${caseName}...`
|
||||
);
|
||||
// Focus in sync gesture context (see runClaude comment)
|
||||
this.terminal.focus();
|
||||
|
||||
@@ -1323,7 +1328,6 @@ Object.assign(CodemanApp.prototype, {
|
||||
// Quick-start with opencode mode (auto-allow tools by default).
|
||||
// No `effort` field — it's Claude-specific (OpenCode has no /effort).
|
||||
const envOverrides = this.buildEnvOverrides(this.getCaseSettings(caseName), this.loadAppSettingsFromStorage());
|
||||
const tabCount = this._readTabCount();
|
||||
const firstSessionId = await this._launchQuickStartInstances(
|
||||
caseName,
|
||||
tabCount,
|
||||
@@ -1359,7 +1363,10 @@ Object.assign(CodemanApp.prototype, {
|
||||
const _runLoc = (this.cases || []).find(c => c.name === caseName)?.location;
|
||||
const isRemote = _runLoc === 'remote' || _runLoc === 'docker';
|
||||
|
||||
const ownsLaunchTerminal = this._beginSessionLaunchStatus(`Starting Codex session in ${caseName}...`);
|
||||
const tabCount = this._readTabCount();
|
||||
const ownsLaunchTerminal = this._beginSessionLaunchStatus(
|
||||
`Starting ${tabCount} Codex session(s) in ${caseName}...`
|
||||
);
|
||||
this.terminal.focus();
|
||||
|
||||
try {
|
||||
@@ -1377,7 +1384,6 @@ Object.assign(CodemanApp.prototype, {
|
||||
|
||||
const globalSettings = this.loadAppSettingsFromStorage();
|
||||
const envOverrides = this.buildEnvOverrides(this.getCaseSettings(caseName), globalSettings);
|
||||
const tabCount = this._readTabCount();
|
||||
const firstSessionId = await this._launchQuickStartInstances(
|
||||
caseName,
|
||||
tabCount,
|
||||
@@ -1417,7 +1423,10 @@ Object.assign(CodemanApp.prototype, {
|
||||
const _runLoc = (this.cases || []).find(c => c.name === caseName)?.location;
|
||||
const isRemote = _runLoc === 'remote' || _runLoc === 'docker';
|
||||
|
||||
const ownsLaunchTerminal = this._beginSessionLaunchStatus(`Starting Gemini session in ${caseName}...`);
|
||||
const tabCount = this._readTabCount();
|
||||
const ownsLaunchTerminal = this._beginSessionLaunchStatus(
|
||||
`Starting ${tabCount} Gemini session(s) in ${caseName}...`
|
||||
);
|
||||
this.terminal.focus();
|
||||
|
||||
try {
|
||||
@@ -1434,7 +1443,6 @@ Object.assign(CodemanApp.prototype, {
|
||||
}
|
||||
|
||||
const envOverrides = this.buildEnvOverrides(this.getCaseSettings(caseName), this.loadAppSettingsFromStorage());
|
||||
const tabCount = this._readTabCount();
|
||||
const firstSessionId = await this._launchQuickStartInstances(
|
||||
caseName,
|
||||
tabCount,
|
||||
@@ -1468,7 +1476,10 @@ Object.assign(CodemanApp.prototype, {
|
||||
const _runLoc = (this.cases || []).find(c => c.name === caseName)?.location;
|
||||
const isRemote = _runLoc === 'remote' || _runLoc === 'docker';
|
||||
|
||||
const ownsLaunchTerminal = this._beginSessionLaunchStatus(`Starting Antigravity session in ${caseName}...`);
|
||||
const tabCount = this._readTabCount();
|
||||
const ownsLaunchTerminal = this._beginSessionLaunchStatus(
|
||||
`Starting ${tabCount} Antigravity session(s) in ${caseName}...`
|
||||
);
|
||||
this.terminal.focus();
|
||||
|
||||
try {
|
||||
@@ -1485,7 +1496,6 @@ Object.assign(CodemanApp.prototype, {
|
||||
}
|
||||
|
||||
const envOverrides = this.buildEnvOverrides(this.getCaseSettings(caseName), this.loadAppSettingsFromStorage());
|
||||
const tabCount = this._readTabCount();
|
||||
const firstSessionId = await this._launchQuickStartInstances(
|
||||
caseName,
|
||||
tabCount,
|
||||
@@ -1528,7 +1538,10 @@ Object.assign(CodemanApp.prototype, {
|
||||
const _runLoc = (this.cases || []).find(c => c.name === caseName)?.location;
|
||||
const isRemote = _runLoc === 'remote' || _runLoc === 'docker';
|
||||
|
||||
const ownsLaunchTerminal = this._beginSessionLaunchStatus(`Starting Pi session in ${caseName}...`);
|
||||
const tabCount = this._readTabCount();
|
||||
const ownsLaunchTerminal = this._beginSessionLaunchStatus(
|
||||
`Starting ${tabCount} Pi session(s) in ${caseName}...`
|
||||
);
|
||||
this.terminal.focus();
|
||||
|
||||
try {
|
||||
@@ -1545,7 +1558,6 @@ Object.assign(CodemanApp.prototype, {
|
||||
}
|
||||
|
||||
const envOverrides = this.buildEnvOverrides(this.getCaseSettings(caseName), this.loadAppSettingsFromStorage());
|
||||
const tabCount = this._readTabCount();
|
||||
const firstSessionId = await this._launchQuickStartInstances(
|
||||
caseName,
|
||||
tabCount,
|
||||
@@ -1576,7 +1588,10 @@ Object.assign(CodemanApp.prototype, {
|
||||
const _runLoc = (this.cases || []).find(c => c.name === caseName)?.location;
|
||||
const isRemote = _runLoc === 'remote' || _runLoc === 'docker';
|
||||
|
||||
const ownsLaunchTerminal = this._beginSessionLaunchStatus(`Starting OMP session in ${caseName}...`);
|
||||
const tabCount = this._readTabCount();
|
||||
const ownsLaunchTerminal = this._beginSessionLaunchStatus(
|
||||
`Starting ${tabCount} OMP session(s) in ${caseName}...`
|
||||
);
|
||||
this.terminal.focus();
|
||||
|
||||
try {
|
||||
@@ -1593,7 +1608,6 @@ Object.assign(CodemanApp.prototype, {
|
||||
}
|
||||
|
||||
const envOverrides = this.buildEnvOverrides(this.getCaseSettings(caseName), this.loadAppSettingsFromStorage());
|
||||
const tabCount = this._readTabCount();
|
||||
const firstSessionId = await this._launchQuickStartInstances(
|
||||
caseName,
|
||||
tabCount,
|
||||
@@ -1635,7 +1649,10 @@ Object.assign(CodemanApp.prototype, {
|
||||
const _runLoc = (this.cases || []).find(c => c.name === caseName)?.location;
|
||||
const isRemote = _runLoc === 'remote' || _runLoc === 'docker';
|
||||
|
||||
const ownsLaunchTerminal = this._beginSessionLaunchStatus(`Starting Grok session in ${caseName}...`);
|
||||
const tabCount = this._readTabCount();
|
||||
const ownsLaunchTerminal = this._beginSessionLaunchStatus(
|
||||
`Starting ${tabCount} Grok session(s) in ${caseName}...`
|
||||
);
|
||||
this.terminal.focus();
|
||||
|
||||
try {
|
||||
@@ -1652,7 +1669,6 @@ Object.assign(CodemanApp.prototype, {
|
||||
}
|
||||
|
||||
const envOverrides = this.buildEnvOverrides(this.getCaseSettings(caseName), this.loadAppSettingsFromStorage());
|
||||
const tabCount = this._readTabCount();
|
||||
const firstSessionId = await this._launchQuickStartInstances(
|
||||
caseName,
|
||||
tabCount,
|
||||
@@ -1704,7 +1720,10 @@ Object.assign(CodemanApp.prototype, {
|
||||
const _runLoc = (this.cases || []).find(c => c.name === caseName)?.location;
|
||||
const isRemote = _runLoc === 'remote' || _runLoc === 'docker';
|
||||
|
||||
const ownsLaunchTerminal = this._beginSessionLaunchStatus(`Starting DeepSeek session in ${caseName}...`);
|
||||
const tabCount = this._readTabCount();
|
||||
const ownsLaunchTerminal = this._beginSessionLaunchStatus(
|
||||
`Starting ${tabCount} DeepSeek session(s) in ${caseName}...`
|
||||
);
|
||||
this.terminal.focus();
|
||||
|
||||
try {
|
||||
@@ -1730,7 +1749,6 @@ Object.assign(CodemanApp.prototype, {
|
||||
}
|
||||
|
||||
const envOverrides = this.buildEnvOverrides(this.getCaseSettings(caseName), this.loadAppSettingsFromStorage());
|
||||
const tabCount = this._readTabCount();
|
||||
const firstSessionId = await this._launchQuickStartInstances(
|
||||
caseName,
|
||||
tabCount,
|
||||
|
||||
Reference in New Issue
Block a user