session-snapshot-corpus.corpus.ts 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. /** Repository-wide ownership and storage invariants for the recorded-session corpus. */
  2. import { existsSync } from 'node:fs'
  3. import { lstat, readFile, readdir, realpath } from 'node:fs/promises'
  4. import { dirname, join, relative, resolve } from 'node:path'
  5. import { expect, it } from 'vitest'
  6. import {
  7. captureExpectedWorkspaceSnapshot,
  8. EMPTY_WORKSPACE_MARKER,
  9. parseSnapshotManifest,
  10. redactSessionSnapshotIds,
  11. scrubSystemPrompts,
  12. scrubToolSchemas,
  13. sessionFixtureNames,
  14. type SnapshotManifest,
  15. } from '@deepseek-ai/dsh-session-snapshot'
  16. const repoRoot = resolve(import.meta.dirname, '..')
  17. const corpusRoot = join(repoRoot, 'snapshots')
  18. const profiles = ['acp', 'sdk', 'session', 'web'] as const
  19. const snapshotAdapters = [
  20. 'apps/web/tests/message-feedback-protocol.snapshot.ts',
  21. 'apps/web/tests/minimal-preset.snapshot.ts',
  22. 'snapshots/acp/acp.snapshot.ts',
  23. 'snapshots/sdk/sdk.snapshot.ts',
  24. 'snapshots/session/headless.snapshot.ts',
  25. ] as const
  26. interface Scenario {
  27. readonly key: string
  28. readonly profile: string
  29. readonly name: string
  30. readonly dir: string
  31. readonly manifest: SnapshotManifest & {
  32. composition: string
  33. recording: 'live' | 'authored'
  34. header: NonNullable<SnapshotManifest['header']>
  35. }
  36. }
  37. async function scenarios(): Promise<Scenario[]> {
  38. const result: Scenario[] = []
  39. for (const profile of profiles) {
  40. const root = join(corpusRoot, profile)
  41. for (const entry of await readdir(root, { withFileTypes: true })) {
  42. if (!entry.isDirectory()) continue
  43. const dir = join(root, entry.name)
  44. const path = join(dir, 'snapshot.yml')
  45. expect(existsSync(path), `${profile}/${entry.name}/snapshot.yml`).toBe(true)
  46. const manifest = parseSnapshotManifest(await readFile(path, 'utf8'), path)
  47. expect(manifest.scenario, `${profile}/${entry.name}: scenario`).toBe(entry.name)
  48. expect(manifest.profile, `${profile}/${entry.name}: profile`).toBe(profile === 'session' ? 'headless' : profile)
  49. expect(manifest.composition, `${profile}/${entry.name}: composition`).toBeTypeOf('string')
  50. expect(manifest.recording, `${profile}/${entry.name}: recording`).toMatch(/^(live|authored)$/)
  51. expect(manifest.header, `${profile}/${entry.name}: header`).toBeDefined()
  52. result.push({
  53. key: `${profile}/${entry.name}`,
  54. profile,
  55. name: entry.name,
  56. dir,
  57. manifest: {
  58. ...manifest,
  59. composition: manifest.composition as string,
  60. recording: manifest.recording as 'live' | 'authored',
  61. header: manifest.header as NonNullable<SnapshotManifest['header']>,
  62. },
  63. })
  64. }
  65. }
  66. return result
  67. }
  68. function referencedScenario(owner: Scenario, source: string): string {
  69. return source.includes('/') ? source : `${owner.profile}/${source}`
  70. }
  71. async function snapshotNamedTests(): Promise<string[]> {
  72. const files: string[] = []
  73. const visit = async (directory: string, relativeDir: string): Promise<void> => {
  74. for (const entry of await readdir(directory, { withFileTypes: true })) {
  75. if (entry.isDirectory()) {
  76. if (['dist', 'lib', 'node_modules'].includes(entry.name)) continue
  77. await visit(join(directory, entry.name), join(relativeDir, entry.name))
  78. } else if (entry.isFile() && /\.snapshot\.tsx?$/u.test(entry.name)) {
  79. files.push(join(relativeDir, entry.name).split(/[/\\]/u).join('/'))
  80. }
  81. }
  82. }
  83. for (const root of ['apps', 'native', 'packages', 'python', 'scripts', 'snapshots', 'website']) {
  84. await visit(join(repoRoot, root), root)
  85. }
  86. return files.sort()
  87. }
  88. it('reserves the snapshot test suffix for recorded-session adapters', async () => {
  89. expect(await snapshotNamedTests()).toEqual([...snapshotAdapters])
  90. })
  91. it('keeps every recorded session owned, pinned, redacted, and header-scrubbed', async () => {
  92. const all = await scenarios()
  93. const byKey = new Map(all.map(scenario => [scenario.key, scenario]))
  94. const pinByClass = new Map<string, Scenario>()
  95. for (const scenario of all) {
  96. if (scenario.manifest.header.pin !== true) continue
  97. const key = `${scenario.manifest.composition}/${scenario.manifest.header.class}`
  98. expect(pinByClass.has(key), `${key}: duplicate header pin`).toBe(false)
  99. pinByClass.set(key, scenario)
  100. }
  101. for (const scenario of all) {
  102. const { manifest, dir, key } = scenario
  103. const classKey = `${manifest.composition}/${manifest.header.class}`
  104. expect(pinByClass.has(classKey), `${key}: missing composition/header pin ${classKey}`).toBe(true)
  105. const localSession = join(dir, 'session.jsonl')
  106. if (manifest.session === undefined) {
  107. expect(existsSync(localSession), `${key}: owner session.jsonl`).toBe(true)
  108. } else {
  109. expect(existsSync(localSession), `${key}: borrower must not own session.jsonl`).toBe(false)
  110. const target = resolve(dir, manifest.session.source)
  111. expect(existsSync(target), `${key}: session source`).toBe(true)
  112. const targetDir = await realpath(dirname(target))
  113. const sourceKey = relative(corpusRoot, targetDir).split(/[/\\]/).join('/')
  114. expect(byKey.has(sourceKey), `${key}: session source must name a corpus owner`).toBe(true)
  115. expect(byKey.get(sourceKey)?.manifest.session, `${key}: session source cannot chain through a borrower`).toBeUndefined()
  116. }
  117. expect(existsSync(join(dir, 'replay.override.json')), `${key}: replay override presence`)
  118. .toBe(manifest.replay?.override === true)
  119. expect(existsSync(join(dir, 'workspace.expected')), `${key}: final workspace presence`)
  120. .toBe(manifest.workspace?.final === true)
  121. if (manifest.workspace?.final === true) {
  122. const expectedRoot = join(dir, 'workspace.expected')
  123. const expectedWorkspace = await captureExpectedWorkspaceSnapshot(expectedRoot)
  124. expect(existsSync(join(expectedRoot, EMPTY_WORKSPACE_MARKER)), `${key}: empty workspace marker`)
  125. .toBe(expectedWorkspace.length === 0)
  126. }
  127. expect(existsSync(join(dir, 'input.json')), `${key}: executable input metadata is ACP-only`)
  128. .toBe(scenario.profile === 'acp')
  129. if (scenario.profile !== 'acp') {
  130. expect(existsSync(join(dir, 'stdout.expected.jsonl')), `${key}: ACP transcript outside ACP`).toBe(false)
  131. }
  132. if (manifest.header.pin === true) {
  133. const promptSource = byKey.get(referencedScenario(scenario, manifest.header.systemPromptSource ?? scenario.name))
  134. const schemaSource = byKey.get(referencedScenario(scenario, manifest.header.toolSchemasSource ?? scenario.name))
  135. expect(promptSource, `${key}: system-prompt source`).toBeDefined()
  136. expect(schemaSource, `${key}: tool-schema source`).toBeDefined()
  137. expect(existsSync(join((promptSource as Scenario).dir, 'system-prompt.expected.md')), `${key}: system-prompt sidecar`).toBe(true)
  138. expect(existsSync(join((schemaSource as Scenario).dir, 'tool-schemas.expected.json')), `${key}: tool-schema sidecar`).toBe(true)
  139. for (const [field, source] of [
  140. ['system-prompt.expected.md', promptSource],
  141. ['tool-schemas.expected.json', schemaSource],
  142. ] as const) {
  143. const local = join(dir, field)
  144. if (!existsSync(local) || !(await lstat(local)).isSymbolicLink()) continue
  145. expect(await realpath(local), `${key}: ${field} symlink follows its manifest source`)
  146. .toBe(await realpath(join((source as Scenario).dir, field)))
  147. }
  148. }
  149. if (manifest.session !== undefined) continue
  150. const names = sessionFixtureNames(await readdir(dir))
  151. const fixtures = await Promise.all(names.map(name => readFile(join(dir, name), 'utf8')))
  152. expect(redactSessionSnapshotIds(fixtures), `${key}: typed identity fixed point`).toEqual(fixtures)
  153. for (const [index, fixture] of fixtures.entries()) {
  154. expect(scrubSystemPrompts(fixture), `${key}/${names[index]}: system prompt must be a sidecar`).toBe(fixture)
  155. expect(scrubToolSchemas(fixture), `${key}/${names[index]}: tool schemas must be a sidecar`).toBe(fixture)
  156. }
  157. for (const index of manifest.header.childSystemPrompts ?? []) {
  158. expect(names[index], `${key}: child prompt index ${index}`).toBeDefined()
  159. expect(existsSync(join(dir, `system-prompt.${index}.expected.md`)), `${key}: child prompt sidecar ${index}`).toBe(true)
  160. }
  161. for (const index of manifest.header.childToolSchemas ?? []) {
  162. expect(names[index], `${key}: child schema index ${index}`).toBeDefined()
  163. expect(existsSync(join(dir, `tool-schemas.${index}.expected.json`)), `${key}: child schema sidecar ${index}`).toBe(true)
  164. }
  165. }
  166. })