Merge remote-tracking branch 'origin/master' into codex/basic-session-search

Conflict resolutions:

- `session.list`: master's projection columns fold into the PR's cancellable,
  batched `listVisibleSessionSummaries`, which `session.search` shares as its
  visibility baseline; master's goal helpers stay beside it.
- Client sessions face: master narrowed `ctx.sessions` to `ISessions`, so the
  search verb and its protocol-constant bound are declared there and the
  test-runtime double implements them (recorded, empty page unless a scenario
  stubs hits).
- `WorkspaceBrowser`: master's per-row Rename wiring rides the PR's search
  results view; the tree keeps the PR's query-free derivations.
- `dsh web` bin: the PR's shutdown-handlers-before-readiness order with
  master's boot-time LAN address snapshot.
- `session-query-sqlite`: master's `SCHEMA_VERSION` 7 stands; the PR's bump
  carried no schema change.
- Specs: master wraps assistant/steering message payloads and requires an
  `application/json` carrier request, so the search fixtures and tests follow.
- Web aria goldens keep master's recording plus the PR's search placeholder;
  the navigation-panes inventory keeps master's terminal-card golden next to
  the PR's search-results golden.
This commit is contained in:
Hypatia May
2026-07-30 09:40:38 +08:00
1904 changed files with 80499 additions and 17610 deletions

View File

@@ -262,6 +262,30 @@ describe('worktree-local Lefthook installer', () => {
expect(readFileSync(legacyHook, 'utf8')).toBe('#!/bin/sh\n# legacy hook\n')
})
it('replaces the owned hook path Git copies into a newly added worktree', async () => {
const fixture = createFixture()
const mainInstall = await runInstaller(fixture, fixture.main)
expect(mainInstall.status, mainInstall.stderr).toBe(0)
const mainHooks = hooksPath(fixture, fixture.main)
const mainHookBefore = readFileSync(join(mainHooks, 'pre-commit'), 'utf8')
const lateLinked = join(fixture.container, 'late-linked')
git(fixture, fixture.main, ['worktree', 'add', '-b', 'late-linked', lateLinked])
write(join(lateLinked, 'lefthook.yml'), 'late-linked-worktree-config\n')
installFakeLefthook(lateLinked)
expect(git(fixture, lateLinked, ['config', '--worktree', '--get', 'core.hooksPath'])).toBe(mainHooks)
const linkedInstall = await runInstaller(fixture, lateLinked)
expect(linkedInstall.status, linkedInstall.stderr).toBe(0)
const linkedHooks = hooksPath(fixture, lateLinked)
expect(linkedHooks).not.toBe(mainHooks)
expect(git(fixture, lateLinked, ['config', '--worktree', '--get', 'core.hooksPath'])).toBe(linkedHooks)
expect(readFileSync(join(linkedHooks, 'pre-commit'), 'utf8')).toContain(
'# config=late-linked-worktree-config',
)
expect(readFileSync(join(mainHooks, 'pre-commit'), 'utf8')).toBe(mainHookBefore)
})
it('serializes concurrent installs and keeps repeated output stable', async () => {
const fixture = createFixture()
const delayed = { DSH_TEST_LEFTHOOK_DELAY_MS: '150' }
@@ -509,6 +533,30 @@ describe('worktree-local Lefthook installer', () => {
expect(git(fixture, fixture.linked, ['config', '--worktree', '--get', 'core.hooksPath'])).toBe('linked-custom-hooks')
})
it('does not trust an ownership marker outside a registered worktree hook path', async () => {
const fixture = createFixture()
const mainInstall = await runInstaller(fixture, fixture.main)
expect(mainInstall.status, mainInstall.stderr).toBe(0)
const externalHooks = join(fixture.container, 'external-owned-hooks')
write(
join(externalHooks, '.dsh-lefthook-owned'),
`${JSON.stringify({
version: 1,
owner: 'deepseek-harness worktree-local lefthook hooks',
hooksPath: externalHooks,
})}\n`,
0o600,
)
git(fixture, fixture.linked, ['config', '--worktree', 'core.hooksPath', externalHooks])
const result = await runInstaller(fixture, fixture.linked)
expect(result.status).toBe(1)
expect(result.stderr).toContain('worktree-scoped core.hooksPath')
expect(git(fixture, fixture.linked, ['config', '--worktree', '--get', 'core.hooksPath'])).toBe(externalHooks)
expect(existsSync(hooksPath(fixture, fixture.linked))).toBe(false)
})
it('refuses to activate a sibling worktree dormant hook path', async () => {
const fixture = createFixture()
const linkedConfig = join(gitDirectory(fixture, fixture.linked), 'config.worktree')