Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 86 additions & 0 deletions core/dev/bench-preflight.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// Bench preflight (dev/bench.sh): refuse before the profile is written when the bench cannot
// start, and say what to do about it. Both failures otherwise surface much later as smoke
// failures that look like plugin bugs — no pnpm leaves the profile without node_modules, so
// `dsh` never resolves the plugin bundle; no prebuilt native addon boots the web server but
// fails every Bot turn. A deepseek-harness source checkout carries no native `bin/`, so it
// needs the prebuilt copy from the installed `@deepseek-ai/node-addon-system-<platform>` package.
//
// node dev/bench-preflight.mjs <harness-dir>
//
// Exit 0 when the bench can start, 1 with the remedy on stderr when it cannot, 2 on bad usage.
import { spawnSync } from 'node:child_process'
import { existsSync } from 'node:fs'
import { dirname, join } from 'node:path'
import { pathToFileURL } from 'node:url'

// The addon loader picks the directory from Node's report, not from the ELF interpreter, so the
// preflight must read the same signal to name the file the loader would try.
export function libcName(platform = process.platform, report = process.report?.getReport()) {
if (platform !== 'linux') return undefined
return report?.header?.glibcVersionRuntime ? 'glibc' : 'musl'
}

export function addonSubpath({ platform = process.platform, libc = libcName(platform) } = {}) {
return join('bin', ...(libc ? [libc] : []), 'system.node')
}

export function nativeAddonPath(harnessDir, { platform = process.platform, arch = process.arch, libc = libcName(platform) } = {}) {
return join(harnessDir, 'native', 'system', 'packages', `${platform}-${arch}`, addonSubpath({ platform, libc }))
}

export function commandExists(command) {
const result = spawnSync(command, ['--version'], { stdio: 'ignore' })
return !result.error && result.status === 0
}

export function checkBench({
harnessDir,
platform = process.platform,
arch = process.arch,
libc = libcName(platform),
hasPnpm = commandExists('pnpm'),
exists = existsSync,
} = {}) {
const addon = harnessDir ? nativeAddonPath(harnessDir, { platform, arch, libc }) : undefined
const result = {
platform,
arch,
libc,
harnessDir,
addon,
package: `@deepseek-ai/node-addon-system-${platform}-${arch}`,
subpath: addonSubpath({ platform, libc }),
}
if (!hasPnpm) return { ...result, ok: false, state: 'missing-pnpm' }
// An installed harness resolves the addon from its own node_modules; only a source checkout
// has native/system/packages, and only that tree needs the prebuilt bin/ copied into it.
const sourceTree = harnessDir && exists(join(harnessDir, 'native', 'system', 'packages'))
if (sourceTree && !exists(addon)) return { ...result, ok: false, state: 'missing-native-addon' }
return { ...result, ok: true, state: 'pass' }
}

export function problemLine(result) {
if (result.state === 'missing-pnpm') {
return 'bench preflight: pnpm is not on PATH — the profile install needs it; `corepack pnpm` supplies pnpm (put a `pnpm` shim that execs it on PATH, e.g. tools/bin/pnpm).'
}
if (result.state === 'missing-native-addon') {
return `bench preflight: no native addon at ${result.addon} — copy ${result.package}/${result.subpath} (installed under core/dev/harness by \`npm run harness\`) into ${dirname(result.addon)}.`
}
return undefined
}

export function main(argv = process.argv.slice(2)) {
const harnessDir = argv[0] ?? process.env.DSH_HARNESS
if (!harnessDir) {
console.error('usage: node dev/bench-preflight.mjs <harness-dir>')
return 2
}
const result = checkBench({ harnessDir })
if (result.ok) return 0
console.error(problemLine(result))
return 1
}

if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
process.exit(main())
}
10 changes: 9 additions & 1 deletion core/dev/bench.sh
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,12 @@ ensure_profile() {
}
EOF
printf 'packages:\n - .\n\nnodeLinker: hoisted\nautoInstallPeers: false\n' > "$profile/pnpm-workspace.yaml"
(cd "$profile" && DSH_HOME="$HOME_DIR" pnpm install >/dev/null)
if ! install_output=$(cd "$profile" && DSH_HOME="$HOME_DIR" pnpm install 2>&1); then
printf '%s\n' "$install_output" >&2
rm -rf "$profile"
echo "pnpm install failed; the bench profile was not created" >&2
exit 1
fi
}

# Bench-only plugin config, layered over the profile with --patch.
Expand All @@ -80,6 +85,9 @@ write_overlay() {
}

start() {
# Refuse before the profile is written: a missing pnpm or native addon otherwise surfaces
# as smoke failures that look like plugin bugs (#2576).
node "$PLUGIN/dev/bench-preflight.mjs" "$DSH_HARNESS" || exit 1
mkdir -p "$HOME_DIR"
ensure_profile
write_overlay
Expand Down
130 changes: 130 additions & 0 deletions core/test/bench-preflight.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import assert from 'node:assert/strict'
import { spawnSync } from 'node:child_process'
import { chmodSync, copyFileSync, existsSync, mkdirSync, mkdtempSync, rmSync, symlinkSync, writeFileSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { dirname, join } from 'node:path'
import { after, test } from 'node:test'
import { fileURLToPath } from 'node:url'
import { addonSubpath, checkBench, libcName, nativeAddonPath, problemLine } from '../dev/bench-preflight.mjs'

const here = dirname(fileURLToPath(import.meta.url))

const roots = []

after(() => {
for (const root of roots) rmSync(root, { recursive: true, force: true })
})

function tree(base = tmpdir()) {
mkdirSync(base, { recursive: true })
const root = mkdtempSync(join(base, 'flybot-bench-preflight-'))
roots.push(root)
return { root, harness: join(root, 'harness') }
}

// A shell fixture has to execute `pnpm`, and a tmpdir can sit on a noexec mount (it does on
// the shared box). node_modules is inside the checkout, ignored by git, and always executable.
function execTree() {
return tree(join(here, '..', 'node_modules', '.cache'))
}

function writeAddon(harness, { platform = 'linux', arch = 'arm64', libc = 'glibc' } = {}) {
const file = nativeAddonPath(harness, { platform, arch, libc })
mkdirSync(dirname(file), { recursive: true })
writeFileSync(file, '')
return file
}

test('passes when pnpm and the addon the loader would require are there', () => {
const { harness } = tree()
const file = writeAddon(harness)
const result = checkBench({ harnessDir: harness, platform: 'linux', arch: 'arm64', libc: 'glibc', hasPnpm: true })
assert.equal(result.ok, true)
assert.equal(result.state, 'pass')
assert.equal(result.addon, file)
})

test('refuses with missing-pnpm before it looks at the addon', () => {
const { harness } = tree()
const result = checkBench({ harnessDir: harness, platform: 'linux', arch: 'arm64', libc: 'glibc', hasPnpm: false })
assert.equal(result.state, 'missing-pnpm')
assert.match(problemLine(result), /corepack pnpm/)
})

test('refuses with missing-native-addon and names the exact path', () => {
const { harness } = tree()
mkdirSync(join(harness, 'native', 'system', 'packages'), { recursive: true })
const file = nativeAddonPath(harness, { platform: 'linux', arch: 'arm64', libc: 'glibc' })
const result = checkBench({ harnessDir: harness, platform: 'linux', arch: 'arm64', libc: 'glibc', hasPnpm: true })
assert.equal(result.state, 'missing-native-addon')
assert.equal(result.addon, file)
const line = problemLine(result)
assert.ok(line.includes(file), line)
assert.match(line, /node-addon-system-linux-arm64/)
})

test('does not demand a source-tree addon from an installed harness', () => {
const { harness } = tree()
const result = checkBench({ harnessDir: harness, platform: 'linux', arch: 'arm64', libc: 'glibc', hasPnpm: true })
assert.equal(result.ok, true)
assert.equal(result.state, 'pass')
})

test('follows the loader on libc and platform', () => {
assert.equal(addonSubpath({ platform: 'linux', libc: 'glibc' }), join('bin', 'glibc', 'system.node'))
assert.equal(addonSubpath({ platform: 'linux', libc: 'musl' }), join('bin', 'musl', 'system.node'))
assert.equal(addonSubpath({ platform: 'darwin' }), join('bin', 'system.node'))
assert.equal(libcName('darwin'), undefined)
})

test('bench.sh refuses before writing the profile when pnpm is missing', { skip: process.platform === 'win32' }, () => {
const { root, harness, path, home } = benchTree()
const result = runBench({ root, path, harness }, 'start')
assert.notEqual(result.status, 0)
assert.match(result.stderr, /pnpm is not on PATH/)
assert.equal(existsSync(join(home, 'profiles')), false)
})

test('bench.sh shows a failed pnpm install and leaves no half-built profile', { skip: process.platform === 'win32' }, () => {
const script = '#!/bin/sh\n[ "$1" = "--version" ] && exit 0\necho "install failed: boom" >&2\nexit 1\n'
const { root, harness, path, home } = benchTree({ pnpm: script })
const result = runBench({ root, path, harness }, 'start')
assert.notEqual(result.status, 0)
assert.match(result.stderr, /install failed: boom/)
assert.equal(existsSync(join(home, 'profiles', 'web')), false)
})

// A checkout-shaped tree: core/dev/{bench.sh,bench-preflight.mjs}, a harness with the host's
// addon, and a bin directory that holds only what bench.sh needs before the preflight.
function benchTree({ pnpm } = {}) {
const { root, harness } = execTree()
const dev = join(root, 'core', 'dev')
mkdirSync(dev, { recursive: true })
copyFileSync(join(here, '../dev/bench.sh'), join(dev, 'bench.sh'))
copyFileSync(join(here, '../dev/bench-preflight.mjs'), join(dev, 'bench-preflight.mjs'))
writeAddon(harness, { platform: process.platform, arch: process.arch, libc: libcName() })
const bin = join(root, 'bin')
mkdirSync(bin)
symlinkSync(process.execPath, join(bin, 'node'))
// bench.sh's only external tool before the preflight is `dirname "$0"`, and $0 here is an
// absolute path; a shell-only stand-in keeps the fixture independent of the host's bin dirs.
writeFileSync(join(bin, 'dirname'), '#!/bin/sh\nprintf "%s\\n" "${1%/*}"\n')
chmodSync(join(bin, 'dirname'), 0o755)
if (pnpm) {
writeFileSync(join(bin, 'pnpm'), pnpm)
chmodSync(join(bin, 'pnpm'), 0o755)
}
// The rest of the box, minus any directory that holds a real pnpm: these tests decide that
// answer themselves, and a runner with pnpm installed must not leak one in.
const rest = (process.env.PATH ?? '').split(':').filter((dir) => dir && !existsSync(join(dir, 'pnpm')))
return { root, harness, bin, home: join(root, '.dsh-test-smoke'), path: [bin, ...rest].join(':') }
}

function runBench({ root, path, harness }, action) {
const result = spawnSync('/bin/sh', [join(root, 'core', 'dev', 'bench.sh'), 'smoke', '3097', '8797', action], {
encoding: 'utf8',
env: { PATH: path, DSH_HARNESS: harness, HOME: root },
})
if (result.error) assert.fail(`could not run bench.sh: ${result.error.message}`)
return result
}
3 changes: 2 additions & 1 deletion docs/subsystems/ds-bot-core.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,15 @@ holds. Task bookkeeping does not imply runtime execution or tool permission.
| `core/dev/token-report.mjs` | Reads the usage ledger into per-Bot, per-Session and per-day token numbers and each Session's prompt growth, keeping fresh input, cache read and cache write apart (#799). Read-only; `--db`, `--from`, `--to`, `--tz`, `--bot`, `--top`, `--growth`, `--json`. |
| `core/dev/turn-split.mjs` | Reads Session logs into the split of one turn's prompt: the fixed block (system prompt plus tool schemas) measured as each Session's fit intercept, then history, tool results and task by character share (#796). Read-only; `--sessions`, `--session`, `--turn`, `--tz`, `--days`, `--top`, `--stats`, `--room`, `--json`. |
| `core/dev/perf-baseline.mjs` | Measures the four baseline numbers (#356) on the mock bench: turn end-to-end p50/p95, tool-call latency p50/p95, resident RSS peak, plugin load time — read from the Session events the activity feed folds, never from the page. Prints its markdown table and writes the `--out` JSON; no thresholds (see [performance-baseline](../performance-baseline.md)). Tooling only: reads the bench and starts nothing. |
| `core/dev/bench-preflight.mjs` | Bench preflight (#2576): `dev/bench.sh start` calls it before the profile is written and refuses with the remedy when `pnpm` is not on PATH or the harness checkout lacks the host's prebuilt native addon (`native/system/packages/<platform>/bin/<libc>/system.node`). Exit 0 pass, 1 refusal on stderr, 2 usage. Tests: `core/test/bench-preflight.test.js`. |
| `core/dev/experience/` | Offline experience library (#874 Step 2): JSON lessons under `repos/<owner>/<name>/` and `stacks/<techStack>/`. The read API is `core/src/host/experience.js` (see [core-host](core-host.md)). `check.mjs` validates the tree and can add one entry file. Not part of the npm `files` list, and not injected into a harness tier. |
| `core/src/host/hooks.js` (approvals) | Held sends bound to their owner — provenance, owner-bound decisions, stale revalidation. Chief replacement invalidates sender-side holds eagerly on `setMain` (not via `staleReason`). The authority contract lives in [team](team.md) (#52). |

## Entry points

- `core/index.js` — plugin server.
- `core/client.js` — built browser bundle (generated; never hand-edit).
- `dev/bench.sh`, `dev/smoke.mjs` — test bench. The mock reads who leads a group from the system prompt (`Members: … and you (admin).`), which is where the standing section puts that line (#894). A turn's own text no longer carries it, so "admin calls everyone" in the smoke is decided from the section (#917).
- `dev/bench.sh`, `dev/bench-preflight.mjs`, `dev/smoke.mjs` — test bench; `start` runs the preflight first, so a missing `pnpm` or native addon stops it instead of surfacing as smoke failures. The mock reads who leads a group from the system prompt (`Members: … and you (admin).`), which is where the standing section puts that line (#894). A turn's own text no longer carries it, so "admin calls everyone" in the smoke is decided from the section (#917).

## Reviewing a tip without checking it out

Expand Down
27 changes: 27 additions & 0 deletions patches/ds-bot/0007-version-and-tooling.patch
Original file line number Diff line number Diff line change
@@ -1,5 +1,32 @@
FlyBot patch 0007-version-and-tooling on Fei2411/DeepSeek-Bot@2b10c70 (ds-bot/). Generated by scripts/ds-bot-patches.mjs; edits to upstream files only (files FlyBot adds are in files/).

diff --git a/dev/bench.sh b/dev/bench.sh
--- a/dev/bench.sh
+++ b/dev/bench.sh
@@ -59,7 +59,12 @@ ensure_profile() {
}
EOF
printf 'packages:\n - .\n\nnodeLinker: hoisted\nautoInstallPeers: false\n' > "$profile/pnpm-workspace.yaml"
- (cd "$profile" && DSH_HOME="$HOME_DIR" pnpm install >/dev/null)
+ if ! install_output=$(cd "$profile" && DSH_HOME="$HOME_DIR" pnpm install 2>&1); then
+ printf '%s\n' "$install_output" >&2
+ rm -rf "$profile"
+ echo "pnpm install failed; the bench profile was not created" >&2
+ exit 1
+ fi
}

# Bench-only plugin config, layered over the profile with --patch.
@@ -80,6 +80,9 @@ write_overlay() {
}

start() {
+ # Refuse before the profile is written: a missing pnpm or native addon otherwise surfaces
+ # as smoke failures that look like plugin bugs (#2576).
+ node "$PLUGIN/dev/bench-preflight.mjs" "$DSH_HARNESS" || exit 1
mkdir -p "$HOME_DIR"
ensure_profile
write_overlay
diff --git a/dev/harness/package.json b/dev/harness/package.json
--- a/dev/harness/package.json
+++ b/dev/harness/package.json
Expand Down
86 changes: 86 additions & 0 deletions patches/ds-bot/files/dev/bench-preflight.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// Bench preflight (dev/bench.sh): refuse before the profile is written when the bench cannot
// start, and say what to do about it. Both failures otherwise surface much later as smoke
// failures that look like plugin bugs — no pnpm leaves the profile without node_modules, so
// `dsh` never resolves the plugin bundle; no prebuilt native addon boots the web server but
// fails every Bot turn. A deepseek-harness source checkout carries no native `bin/`, so it
// needs the prebuilt copy from the installed `@deepseek-ai/node-addon-system-<platform>` package.
//
// node dev/bench-preflight.mjs <harness-dir>
//
// Exit 0 when the bench can start, 1 with the remedy on stderr when it cannot, 2 on bad usage.
import { spawnSync } from 'node:child_process'
import { existsSync } from 'node:fs'
import { dirname, join } from 'node:path'
import { pathToFileURL } from 'node:url'

// The addon loader picks the directory from Node's report, not from the ELF interpreter, so the
// preflight must read the same signal to name the file the loader would try.
export function libcName(platform = process.platform, report = process.report?.getReport()) {
if (platform !== 'linux') return undefined
return report?.header?.glibcVersionRuntime ? 'glibc' : 'musl'
}

export function addonSubpath({ platform = process.platform, libc = libcName(platform) } = {}) {
return join('bin', ...(libc ? [libc] : []), 'system.node')
}

export function nativeAddonPath(harnessDir, { platform = process.platform, arch = process.arch, libc = libcName(platform) } = {}) {
return join(harnessDir, 'native', 'system', 'packages', `${platform}-${arch}`, addonSubpath({ platform, libc }))
}

export function commandExists(command) {
const result = spawnSync(command, ['--version'], { stdio: 'ignore' })
return !result.error && result.status === 0
}

export function checkBench({
harnessDir,
platform = process.platform,
arch = process.arch,
libc = libcName(platform),
hasPnpm = commandExists('pnpm'),
exists = existsSync,
} = {}) {
const addon = harnessDir ? nativeAddonPath(harnessDir, { platform, arch, libc }) : undefined
const result = {
platform,
arch,
libc,
harnessDir,
addon,
package: `@deepseek-ai/node-addon-system-${platform}-${arch}`,
subpath: addonSubpath({ platform, libc }),
}
if (!hasPnpm) return { ...result, ok: false, state: 'missing-pnpm' }
// An installed harness resolves the addon from its own node_modules; only a source checkout
// has native/system/packages, and only that tree needs the prebuilt bin/ copied into it.
const sourceTree = harnessDir && exists(join(harnessDir, 'native', 'system', 'packages'))
if (sourceTree && !exists(addon)) return { ...result, ok: false, state: 'missing-native-addon' }
return { ...result, ok: true, state: 'pass' }
}

export function problemLine(result) {
if (result.state === 'missing-pnpm') {
return 'bench preflight: pnpm is not on PATH — the profile install needs it; `corepack pnpm` supplies pnpm (put a `pnpm` shim that execs it on PATH, e.g. tools/bin/pnpm).'
}
if (result.state === 'missing-native-addon') {
return `bench preflight: no native addon at ${result.addon} — copy ${result.package}/${result.subpath} (installed under core/dev/harness by \`npm run harness\`) into ${dirname(result.addon)}.`
}
return undefined
}

export function main(argv = process.argv.slice(2)) {
const harnessDir = argv[0] ?? process.env.DSH_HARNESS
if (!harnessDir) {
console.error('usage: node dev/bench-preflight.mjs <harness-dir>')
return 2
}
const result = checkBench({ harnessDir })
if (result.ok) return 0
console.error(problemLine(result))
return 1
}

if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
process.exit(main())
}
Loading
Loading