Files
deepseek-harness/examples/desktop/scripts/qa-overlap-fix-probe.mjs
T
ZiyaZhang e8f5c0b51b feat(desktop): DSH Electron desktop shell — harness internals visualized
Minimal Electron shell over the DSH JSON-RPC runtime — a first-look at
what a ChatGPT.app-style host on top of the DeepSeek Harness looks
like, with the harness's normally-invisible internals (trace timeline,
context surface, subagent tree, compaction, plugin registry, rubrics)
brought forward as first-class UI surfaces so plugin authors and
researchers can see what the agent is actually doing.

Runs against three keyless-to-live profiles (stdio-echo works on
master out of the box; daemon-echo / daemon-vibe-echo activate once
the daemon-demo lands; stdio-deepseek and daemon-vibe hit the real
DeepSeek API when you supply a key). HARNESS_DEV auto-resolves to the
in-repo runtime when this shell ships under examples/desktop/, so a
fresh clone launches without config; env DSH_DEV_ROOT overrides for
custom layouts, and a sibling deepseek-harness-dev/ checkout is the
original dev workflow.

Cold-clone gate (P0 fixes for first-time-clone usability):
- HARNESS_DEV: 3-candidate resolver (env → walk-up in-repo marker →
  sibling), unit-tested via mock fs so ordering is locked without
  needing either real layout on disk.
- config yml leaves rewritten at assemble time so the sibling-clone
  paths (../../deepseek-harness-dev/examples/echo-agent/…) become
  the in-repo paths (../../echo-agent/…) in the released tree —
  source yml stays usable for local dev, released tree ships a
  working shape.
- pnpm-workspace.yaml allowBuilds.electron = true (was placeholder).
- missing-key card in stdio-deepseek offers a one-click switch to
  stdio-echo (the keyless profile that works on master) rather than
  daemon-echo (blocked on the not-yet-shipped daemon-demo).
- assemble-oss-release.sh rewrites the source-side breadcrumb name
  'dsh-desktop-demo' → 'dsh-desktop' for the released package.json.

FOUC guard on the onboarding gate (41fc5df carried) keeps the
first-launch splash from flashing before the runtime probe finishes.

Test suite (1634 tests in source, 3990 in the runtime repo) covers
resolver ordering, renderer classifiers, trace timeline shape,
compaction diff rendering, rubric parity, and the missing-key
onboarding paths.
2026-07-18 12:59:34 -07:00

279 lines
13 KiB
JavaScript

// payload-controls overlap regression probe (2026-07-18 delta).
//
// Boots a fresh isolated Electron instance, injects tool blocks with a
// long meta suffix ("(content · meta · isError · error · durationMs)"),
// opens the JSON drawer, then asserts via getBoundingClientRect() that
// the button cluster (pretty ⇅ raw · copy · download) does NOT overlap
// the label / meta text at either width.
//
// Four payload-controls mount points get exercised — matches the shipped
// (merged) fix from da779ac (`fix/ui-hotfix-drawer-overlap` -> 9743db1):
// A) inline tool-block args renderer.js:1226 → `.tool-block-label-row`
// B) inline tool-block result renderer.js:1249 → `.tool-block-label-row`
// C) tool-json-drawer sections tool-cards.js:663 → `.tool-json-section-controls[data-drawer-controls]`
// D) trace-detail Render=JSON trace-detail-pane.js:1512 → `.trace-detail-json-panel`
// (no-op verify — mount is flex-column,
// controls right-anchor via margin-left)
//
// Runs the app under a private user-data dir + dedicated CDP port so it
// never collides with the user's own Electron. Kills the child on exit.
//
// Usage: `pnpm exec node scripts/qa-overlap-fix-probe.mjs [outDir]`.
// Requires the electron binary at `node_modules/.bin/electron`. When run
// in a worktree that has never had `pnpm install` executed there, either
// (a) run `pnpm install` locally, or (b) symlink `node_modules` in from
// the primary checkout — see docs/qa-overlap-fix/README.md.
import { spawn } from 'node:child_process'
import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join, resolve } from 'node:path'
const PORT = Number(process.env.OVERLAP_PROBE_PORT || 9247)
const outDir = resolve(process.argv[2] || 'docs/qa-overlap-fix')
mkdirSync(outDir, { recursive: true })
const userData = mkdtempSync(join(tmpdir(), 'dsh-overlap-fix-'))
const electronBin = resolve('node_modules/.bin/electron')
const child = spawn(electronBin, [
'.',
`--remote-debugging-port=${PORT}`,
`--user-data-dir=${userData}`,
'--no-first-run',
], {
env: {
...process.env,
DSH_QA: '1',
DSH_MAXIMIZE: '0',
DSH_ONBOARDING_SKIP: '1',
},
stdio: ['ignore', 'pipe', 'pipe'],
})
let bootLog = ''
child.stdout.on('data', (b) => { bootLog += b.toString() })
child.stderr.on('data', (b) => { bootLog += b.toString() })
async function cleanup(code) {
try { child.kill('SIGKILL') } catch {}
try { rmSync(userData, { recursive: true, force: true }) } catch {}
process.exit(code)
}
process.on('SIGINT', () => cleanup(130))
process.on('SIGTERM', () => cleanup(143))
async function waitForCdp(port, timeoutMs = 15000) {
const deadline = Date.now() + timeoutMs
while (Date.now() < deadline) {
try {
const r = await fetch(`http://localhost:${port}/json/list`)
const list = await r.json()
const page = list.find((t) => t.type === 'page')
if (page && page.webSocketDebuggerUrl) return page
} catch {}
await new Promise((r) => setTimeout(r, 200))
}
throw new Error(`CDP not up on port ${port} after ${timeoutMs}ms; boot log:\n${bootLog}`)
}
async function main() {
const page = await waitForCdp(PORT)
const ws = new WebSocket(page.webSocketDebuggerUrl)
await new Promise((r, x) => { ws.onopen = r; ws.onerror = (e) => x(new Error(String(e))) })
let id = 1
const pending = new Map()
ws.onmessage = (ev) => {
const msg = JSON.parse(typeof ev.data === 'string' ? ev.data : String(ev.data))
if (msg.id != null && pending.has(msg.id)) {
const [ok, err] = pending.get(msg.id)
pending.delete(msg.id)
if (msg.error) err(new Error(msg.error.message))
else ok(msg.result)
}
}
const call = (m, p = {}, timeoutMs = 15000) => new Promise((ok, err) => {
const _id = id++
const timer = setTimeout(() => { pending.delete(_id); err(new Error('cdp timeout: ' + m)) }, timeoutMs)
pending.set(_id, [(v) => { clearTimeout(timer); ok(v) }, (e) => { clearTimeout(timer); err(e) }])
ws.send(JSON.stringify({ id: _id, method: m, params: p }))
})
const evj = async (expr) => {
const r = await call('Runtime.evaluate', {
expression: `(async()=>{try{return (${expr})}catch(e){return {__err: String(e)}}})()`,
returnByValue: true, awaitPromise: true,
})
if (r.exceptionDetails) throw new Error(r.exceptionDetails.exception?.description || r.exceptionDetails.text)
return r.result?.value
}
await call('Page.enable')
await evj(`window.dshQa && window.dshQa.revealWindow ? await window.dshQa.revealWindow() : null`)
const results = []
for (const w of [1440, 800]) {
await call('Emulation.setDeviceMetricsOverride', {
width: w, height: 900, deviceScaleFactor: 2, mobile: false,
})
await new Promise((r) => setTimeout(r, 400))
// Inject a synthetic tool-block that mirrors renderer.js appendToolCall's
// POST-fix shape (`.tool-block-label-row` wrapper containing `.label` and
// the util's controls). We build the DOM directly rather than call
// appendToolCall so we can stress-test with an intentionally long meta
// suffix — that string is what pulled the buttons on top of the label
// in the pre-fix bug.
const injected = await evj(`(function(){
const stream = document.getElementById('stream')
if (!stream) return {ok:false, why:'no stream'}
for (const el of stream.querySelectorAll('.tool-block.qa-overlap-fix')) el.remove()
const details = document.createElement('details')
details.className = 'tool-block qa-overlap-fix'
details.setAttribute('data-tool-name', 'bash')
details.setAttribute('data-tool-card-family', 'bash')
details.setAttribute('open', '')
const summary = document.createElement('summary')
summary.textContent = 'bash — running the full test battery with a really long argument gist that should ellipsis and never overlap'
details.appendChild(summary)
const pc = window.__dshPayloadControls
// (A) args row
const argsRow = document.createElement('div')
argsRow.className = 'tool-block-label-row'
const argLabel = document.createElement('div')
argLabel.className = 'label'
argLabel.textContent = 'args (content · meta · isError · error · durationMs · plus even more meta text to stress-test)'
argsRow.appendChild(argLabel)
const argsBox = document.createElement('div')
argsBox.className = 'args args-with-controls'
if (pc && pc.attachPayloadControls) {
const ret = pc.attachPayloadControls(argsRow, { getRaw: () => ({command: 'pnpm test'}), kind: 'args' })
if (ret && ret.preEl && ret.preEl.parentNode) {
ret.preEl.parentNode.removeChild(ret.preEl)
argsBox.appendChild(ret.preEl)
}
}
// (B) result row
const resultRow = document.createElement('div')
resultRow.className = 'tool-block-label-row'
const resLabel = document.createElement('div')
resLabel.className = 'label'
resLabel.textContent = 'result (content · meta · isError · error · durationMs)'
resultRow.appendChild(resLabel)
const resBox = document.createElement('div')
resBox.className = 'result result-with-controls'
if (pc && pc.attachPayloadControls) {
const ret = pc.attachPayloadControls(resultRow, { getRaw: () => ({ok: true, output: 'PASS'}), kind: 'result' })
if (ret && ret.preEl && ret.preEl.parentNode) {
ret.preEl.parentNode.removeChild(ret.preEl)
resBox.appendChild(ret.preEl)
}
}
details.appendChild(argsRow); details.appendChild(argsBox)
details.appendChild(resultRow); details.appendChild(resBox)
stream.appendChild(details)
details.scrollIntoView({block:'center'})
return {ok:true, id:'qa-overlap-fix'}
})()`)
if (!injected || !injected.ok) throw new Error('inject failed: ' + JSON.stringify(injected))
await new Promise((r) => setTimeout(r, 300))
// (C) open the drawer for the third callsite.
const drawerRes = await evj(`(function(){
const tc = window.__dshToolCards
if (!tc || !tc.openJsonDrawer) return {ok:false, why:'no drawer api'}
tc.openJsonDrawer({
title: 'bash — a very long tool title used for the drawer overlap probe',
call: { arguments: { command: 'pnpm test' } },
result: { content: [{type:'text',text:'PASS'}], meta:{card:'generic'}, isError:false, durationMs:48 }
})
return {ok:true}
})()`)
if (!drawerRes || !drawerRes.ok) throw new Error('drawer open failed: ' + JSON.stringify(drawerRes))
await new Promise((r) => setTimeout(r, 300))
// Measure geometry — post-fix, controls sit inside the same flex row
// as the label, right-anchored. The only failure mode we still guard
// against is horizontal overlap between the label text rect and the
// controls cluster rect (which happens if the flex breaks or someone
// reintroduces float/negative-margin).
const geom = await evj(`(function(){
function rectOf(el){ if(!el) return null; const r=el.getBoundingClientRect(); return {top:r.top,bottom:r.bottom,left:r.left,right:r.right,width:r.width,height:r.height} }
function overlapY(a,b){ if(!a||!b) return null; const top=Math.max(a.top,b.top); const bot=Math.min(a.bottom,b.bottom); return Math.max(0, bot-top) }
function hOverlap(a,b){ if(!a||!b) return null; const left=Math.max(a.left,b.left); const right=Math.min(a.right,b.right); return Math.max(0, right-left) }
const details = document.querySelector('.tool-block.qa-overlap-fix')
if (!details) return {err:'no details'}
const rows = details.querySelectorAll('.tool-block-label-row')
const argRow = rows[0]
const resRow = rows[1]
const argLabel = argRow ? argRow.querySelector('.label') : null
const argCtl = argRow ? argRow.querySelector('.payload-controls') : null
const resLabel = resRow ? resRow.querySelector('.label') : null
const resCtl = resRow ? resRow.querySelector('.payload-controls') : null
const drawer = document.getElementById('tool-json-drawer')
const drawerSummary = drawer ? drawer.querySelector('.tool-json-section summary') : null
const drawerCtlStrip = drawer ? drawer.querySelector('.tool-json-section-controls[data-drawer-controls]') : null
const drawerCtl = drawerCtlStrip ? drawerCtlStrip.querySelector('.payload-controls') : null
return {
argsRow: { label: rectOf(argLabel), controls: rectOf(argCtl), hOverlap: hOverlap(rectOf(argLabel), rectOf(argCtl)) },
resultRow: { label: rectOf(resLabel), controls: rectOf(resCtl), hOverlap: hOverlap(rectOf(resLabel), rectOf(resCtl)) },
drawer: { summary: rectOf(drawerSummary), controls: rectOf(drawerCtl), vOverlap: overlapY(rectOf(drawerSummary), rectOf(drawerCtl)) },
}
})()`)
results.push({ width: w, geom })
// Screenshot for eye verification alongside the geom log.
const shot = await call('Page.captureScreenshot', { format: 'png', captureBeyondViewport: false })
const path = join(outDir, `overlap-w${w}.png`)
writeFileSync(path, Buffer.from(shot.data, 'base64'))
await evj(`window.__dshToolCards && window.__dshToolCards.closeJsonDrawer && window.__dshToolCards.closeJsonDrawer()`)
await new Promise((r) => setTimeout(r, 200))
}
writeFileSync(join(outDir, 'geom-trace.log'), JSON.stringify(results, null, 2))
console.log(JSON.stringify(results, null, 2))
let bad = 0
for (const { width, geom } of results) {
if (!geom || geom.__err) { console.error('geom error at width', width, geom); bad++; continue }
const a = geom.argsRow, r = geom.resultRow, d = geom.drawer
// The flex row wraps at narrow widths (flex-wrap: wrap on
// .tool-block-label-row) — when it wraps, controls drop onto their
// own line under the label, so hOverlap can become non-zero but
// vOverlap between label and controls is 0. Guard against actual
// stack-on-same-line overlap (both non-zero) rather than either alone.
if (a && a.label && a.controls) {
const sameLine = Math.abs((a.label.top + a.label.height/2) - (a.controls.top + a.controls.height/2)) < Math.max(a.label.height, a.controls.height)
if (sameLine && a.hOverlap > 0.5) {
console.error('args row: label ↔ controls overlap on same line =', a.hOverlap, 'px at width', width); bad++
}
}
if (r && r.label && r.controls) {
const sameLine = Math.abs((r.label.top + r.label.height/2) - (r.controls.top + r.controls.height/2)) < Math.max(r.label.height, r.controls.height)
if (sameLine && r.hOverlap > 0.5) {
console.error('result row: label ↔ controls overlap on same line =', r.hOverlap, 'px at width', width); bad++
}
}
if (d && d.vOverlap > 0.5) {
console.error('drawer: summary ↔ controls vertical overlap =', d.vOverlap, 'px at width', width); bad++
}
}
if (bad) {
console.error(`FAIL — ${bad} overlap(s) still present`)
await cleanup(1)
}
console.log(`PASS — no overlaps at widths ${results.map((r) => r.width).join(', ')}`)
await cleanup(0)
}
main().catch(async (e) => { console.error('probe error:', e && e.stack || e); await cleanup(2) })