|
|
@@ -240,6 +240,31 @@ export async function launchWebScaffold(options: LaunchOptions = {}): Promise<We
|
|
|
// paths at load, and an in-process boot must NEVER touch the developer's
|
|
|
// real ~/.dsh document or credential file.
|
|
|
const harnessHome = join(workspaceCwd, '.dsh-home')
|
|
|
+ // Skill discovery is model-visible input, and its roots now resolve inside a
|
|
|
+ // PRESET — a subtree this lane's include patches cannot reach, because the
|
|
|
+ // roster mounts it directly per session rather than as a row of the booted
|
|
|
+ // tree. The row's documented fallback is the environment, so pin that: the
|
|
|
+ // whole scaffold lifetime, not just the boot, since presets mount when a
|
|
|
+ // session is created. Without this a developer's real ~/.dsh/skills silently
|
|
|
+ // enters replay requests and goldens while CI sees none.
|
|
|
+ const skillRootEnvironment = {
|
|
|
+ DSH_HOME: join(workspaceCwd, '.dsh-home'),
|
|
|
+ DSH_AGENTS_HOME: join(workspaceCwd, '.agents-home'),
|
|
|
+ DSH_BUNDLED_SKILL_DIR: join(workspaceCwd, '.bundled-skills'),
|
|
|
+ }
|
|
|
+ const originalSkillRootEnvironment = Object.fromEntries(
|
|
|
+ Object.keys(skillRootEnvironment).map(key => [key, process.env[key]]),
|
|
|
+ )
|
|
|
+ let skillRootEnvironmentRestored = false
|
|
|
+ const restoreSkillRootEnvironment = (): void => {
|
|
|
+ if (skillRootEnvironmentRestored) return
|
|
|
+ skillRootEnvironmentRestored = true
|
|
|
+ for (const [key, value] of Object.entries(originalSkillRootEnvironment)) {
|
|
|
+ if (value === undefined) Reflect.deleteProperty(process.env, key)
|
|
|
+ else process.env[key] = value
|
|
|
+ }
|
|
|
+ }
|
|
|
+ Object.assign(process.env, skillRootEnvironment)
|
|
|
let persistenceRoot: string
|
|
|
try {
|
|
|
persistenceRoot = await mkdtemp(join(tmpdir(), 'dsh-web-e2e-sessions-'))
|
|
|
@@ -415,6 +440,7 @@ export async function launchWebScaffold(options: LaunchOptions = {}): Promise<We
|
|
|
if (process.cwd() !== originalCwd) process.chdir(originalCwd)
|
|
|
const cleanupFailures = await cleanupScaffoldWorld(ctx, workspaceCwd, persistenceRoot)
|
|
|
restoreCredentialEnvironment()
|
|
|
+ restoreSkillRootEnvironment()
|
|
|
if (cleanupFailures.length > 0) {
|
|
|
throw new AggregateError([error, ...cleanupFailures], 'web scaffold setup failed and cleanup was incomplete')
|
|
|
}
|
|
|
@@ -462,6 +488,7 @@ export async function launchWebScaffold(options: LaunchOptions = {}): Promise<We
|
|
|
failures.push(...await cleanupScaffoldWorld(ctx, workspaceCwd, persistenceRoot))
|
|
|
} finally {
|
|
|
restoreCredentialEnvironment()
|
|
|
+ restoreSkillRootEnvironment()
|
|
|
}
|
|
|
if (failures.length > 0) throw new AggregateError(failures, 'web scaffold teardown failed')
|
|
|
},
|