{
  "schemaVersion": 1,
  "rawCaptureSha256": "a14dc449ba1f9f888332dbb3722057362ceaa8a6c427a43ff8f12935cc2fd8da",
  "publicRecordSha256": "e8f5fdf6d4b8c995dc2c774c48417131f64a70b710292a85f1b8dfdaf3b05852",
  "runId": "pnpm-changed-project-move-r1-packet",
  "identicalCompletedDuplicates": 0,
  "normalization": {
    "rule": "Longest explicit absolute prefixes first, applied to all JSON keys and string values after original payload binding. Uncovered private machine paths fail closed.",
    "prefixes": [
      {
        "prefixSha256": "ef172c26fe96c186811dcdc58dd6470c0f724ee726c7e05e1356722fe8f4e130",
        "replacement": "$PROVIDER_STATE"
      },
      {
        "prefixSha256": "9709cbc5d1b6787a5deb13e77b7afee5a4cec261257312f12dd9975101b591d6",
        "replacement": "$WORKSPACE"
      },
      {
        "prefixSha256": "514d562510e28d6e6253e4bbeafa3f412d21fa2d0b985c8c8b00f0a4e994b3f4",
        "replacement": "$STUDY"
      },
      {
        "prefixSha256": "768a63684ee857a90d0c5e1aac2d644942946e9d0e89296a385f90a539686b00",
        "replacement": "$REPOSITORY"
      },
      {
        "prefixSha256": "25595d2cdeb1f5f36533058dd527c5d63be24ac139bd3226307a6d8eb4fb391f",
        "replacement": "$TEMP_ROOT"
      },
      {
        "prefixSha256": "ebfbce7eba786938bcd88fce3ee47df82501feb7171536e7afe200d36647bbb7",
        "replacement": "$TEMP_ROOT"
      },
      {
        "prefixSha256": "bb83c0ccb509c1931abaeb39d1a86a485d224480125b8ad357e10e9457d10aaf",
        "replacement": "$TEMP_ROOT"
      },
      {
        "prefixSha256": "76d509b275fdc76041a48f4ed3789b73a1588b1b6c472dff3507750705b8256f",
        "replacement": "$TEMP_ROOT"
      },
      {
        "prefixSha256": "e4693ec98d2762b9a5b34fc9dd8c540384a2a5fb719f88d14d760230b3990fc9",
        "replacement": "$USER_HOME"
      }
    ]
  },
  "actions": [
    {
      "idSha256": "6a9722cdf589a2a068832c955a8253b9df87983501c03dd92e95debae1f409da",
      "type": "command_execution",
      "status": "failed",
      "originalLine": 6,
      "outputSha256": "ee9e9dec78b47869a8648fee91989c7c3cad1d023144e22b1fe905a9ab43e896",
      "outputBytes": 1142,
      "exitCode": 1,
      "stdout": "\n> test:typelatch-benchmark\n> TZ=UTC TSX_DISABLE_CACHE=1 node --import '$REPOSITORY/node_modules/tsx/dist/loader.mjs' '$REPOSITORY/artifacts/research/large-monorepo/maintenance/public-check.mjs'\n\n{\"passed\":false,\"cases\":3,\"failed\":1,\"results\":[{\"id\":\"move-between-projects\",\"passed\":false,\"expected\":[[\"packages/alpha\",\"packages/beta\"],[]],\"actual\":[[\"packages/beta\"],[]],\"renameDetected\":true,\"diffStatus\":\"R100\\tpackages/alpha/src/value.ts\\tpackages/beta/src/value.ts\",\"gitFixture\":\"$TEMP_ROOTtypelatch-pnpm-maintenance-dvBreQ\"},{\"id\":\"ordinary-edit\",\"passed\":true,\"expected\":[[\"packages/alpha\"],[]],\"actual\":[[\"packages/alpha\"],[]],\"renameDetected\":false,\"diffStatus\":\"M\\tpackages/alpha/src/value.ts\",\"gitFixture\":\"$TEMP_ROOTtypelatch-pnpm-maintenance-oyQsTE\"},{\"id\":\"unchanged\",\"passed\":true,\"expected\":[[],[]],\"actual\":[[],[]],\"renameDetected\":false,\"diffStatus\":\"\",\"gitFixture\":\"$TEMP_ROOTtypelatch-pnpm-maintenance-Yfgb9E\"}]}\n"
    },
    {
      "idSha256": "4a317b6972e2f1834050485420bd71fe6fc2d87cd25d572cb4b8b327b37bc030",
      "type": "mcp_tool_call",
      "status": "completed",
      "originalLine": 8,
      "server": "typelatch_packet",
      "tool": "workspace_evidence",
      "resultSha256": "a79edd84cd4161c0990cf87a0a12412a3662845f7b9215e0a2be850df49cf901",
      "resultBytes": 16060,
      "errorSha256": null,
      "result": {
        "content": [
          {
            "type": "text",
            "text": "EXPERIMENTAL SOURCE EVIDENCE v2. Verbatim modules or enclosing declarations when budget permits; lexical candidates; not resolved definitions. Absence is unproven. Multiple candidates remain alternatives. APIs, compilation and tests are not run by this wrapper.\n\nSTATUS partial inventory=observed ambiguity=unresolved\n\nCOVERAGE {\"complete\":false,\"files\":2715,\"excluded\":231,\"projects\":394,\"configs\":503,\"issues\":4,\"dependencyErrors\":0,\"missingIndexes\":0,\"exhaustive\":false,\"candidateLimitReached\":true}\n\nSEARCH CHECKS retrieved=pass (Lexical candidates only; coverage and ranking limits remain explicit); resolved=not-run (Use workspace_context for compiler resolution); typechecked=not-run (Use workspace_validate for compilation); tested=not-run (No assertion command executed); stable=unknown (Search records files as read; it does not establish an atomic workspace snapshot); artifactMatch=unknown (Installed dependency contents were not compared with registry artifacts) omittedChecks=0\n\nISSUES File exceeds 1 MB: reviewing/list/test/__fixtures__/many-deps/pnpm-lock.yaml; File chunk limit reached: $STUDY/runs/pnpm-changed-proj [omittedBytes=69 sha256=8568e0452e57fe961967deba00acb795256b0b52706410a18c9535b6d53065fd]; File chunk limit reached: $STUDY/runs/pnpm-changed-proj [omittedBytes=58 sha256=6a023c24a3d5a53ffd4ae45e7c530f1453d64f1ea424b203af2ec453d0c9a572] omittedIssues=1\n\nPROVENANCE searchSnapshot=2bd5e8d455e50afd2bb955df40157b21dc18d028cfaa9aaf90ff0151fe770a65 fullResponseSha256=b64813db81fcac2293a32cbcff8b6f625fbe9fa652aa9c3a0958e67d5dcfa117 sourceImportResolution=not-run\n\nOMISSIONS {\"inputSourceFiles\":5,\"uniqueCandidates\":5,\"returnedCandidates\":3,\"budgetCandidates\":2,\"duplicateSourceRecords\":0,\"invalidSourceRecords\":0,\"upstreamSourceFilesOmitted\":3,\"unavailableImports\":0,\"ambiguousImports\":0,\"syntaxErrorFiles\":0,\"sourceVerification\":\"full hashes checked at expansion and again before return; not an atomic snapshot\",\"metadata\":\"project/config lists and duplicate envelopes omitted; complete response retained only in coordinator diagnostics\"}\n\nCANDIDATE 1 {\"path\":\"workspace/filter-workspace-packages/src/getChangedPackages.ts\",\"lines\":[1,103],\"columns\":[1,3],\"offsets\":[0,3584],\"lexicalScore\":86,\"fileSha256\":\"d9457500e9be42f72ef83fde68890ba08617bcd4489478615d6c2e95421f408e\",\"excerptSha256\":\"d9457500e9be42f72ef83fde68890ba08617bcd4489478615d6c2e95421f408e\",\"complete\":true,\"declarationComplete\":false,\"selection\":\"complete-module\",\"omittedBytes\":0}\nimport assert from 'assert'\nimport path from 'path'\nimport util from 'util'\nimport { PnpmError } from '@pnpm/error'\nimport * as micromatch from 'micromatch'\nimport execa from 'execa'\nimport findUp from 'find-up'\nimport { type ProjectRootDir } from '@pnpm/types'\n\ntype ChangeType = 'source' | 'test'\n\ninterface ChangedDir { dir: string, changeType: ChangeType }\n\nexport async function getChangedPackages (\n  packageDirs: ProjectRootDir[],\n  commit: string,\n  opts: { workspaceDir: string, testPattern?: string[], changedFilesIgnorePattern?: string[] }\n): Promise<[ProjectRootDir[], ProjectRootDir[]]> {\n  // .git is a directory in regular repos, but a file in worktrees\n  const gitPath = await findUp('.git', { cwd: opts.workspaceDir, type: 'directory' }) ??\n                  await findUp('.git', { cwd: opts.workspaceDir, type: 'file' })\n\n  const repoRoot = path.resolve(gitPath ?? opts.workspaceDir, '..')\n\n  const changedDirs = (await getChangedDirsSinceCommit(commit, opts.workspaceDir, opts.testPattern ?? [], opts.changedFilesIgnorePattern ?? []))\n    .map(changedDir => ({ ...changedDir, dir: path.join(repoRoot, changedDir.dir) }))\n  const pkgChangeTypes = new Map<ProjectRootDir, ChangeType | undefined>()\n  for (const pkgDir of packageDirs) {\n    pkgChangeTypes.set(pkgDir, undefined)\n  }\n  for (const changedDir of changedDirs) {\n    let currentDir = changedDir.dir\n    while (!pkgChangeTypes.has(currentDir as ProjectRootDir)) {\n      const nextDir = path.dirname(currentDir)\n      if (nextDir === currentDir) break\n      currentDir = nextDir\n    }\n    if (pkgChangeTypes.get(currentDir as ProjectRootDir) === 'source') continue\n    pkgChangeTypes.set(currentDir as ProjectRootDir, changedDir.changeType)\n  }\n\n  const changedPkgs = [] as ProjectRootDir[]\n  const ignoreDependentForPkgs = [] as ProjectRootDir[]\n  for (const [changedDir, changeType] of pkgChangeTypes.entries()) {\n    switch (changeType) {\n    case 'source':\n      changedPkgs.push(changedDir)\n      break\n    case 'test':\n      ignoreDependentForPkgs.push(changedDir)\n      break\n    }\n  }\n  return [changedPkgs, ignoreDependentForPkgs]\n}\n\nasync function getChangedDirsSinceCommit (commit: string, workingDir: string, testPattern: string[], changedFilesIgnorePattern: string[]): Promise<ChangedDir[]> {\n  let diff!: string\n  try {\n    diff = (\n      await execa('git', [\n        'diff',\n        '--name-only',\n        commit,\n        '--',\n        workingDir,\n      ], { cwd: workingDir })\n    ).stdout\n  } catch (err: unknown) {\n    assert(util.types.isNativeError(err))\n    throw new PnpmError('FILTER_CHANGED', `Filtering by changed packages failed. ${'stderr' in err ? err.stderr as string : ''}`)\n  }\n  const changedDirs = new Map<string, ChangeType>()\n\n  if (!diff) {\n    return []\n  }\n\n  const allChangedFiles = diff.split('\\n')\n    // The prefix and suffix '\"' are appended to the Korean path\n    .map(line => line.replace(/^\"/, '').replace(/\"$/, ''))\n  const patterns = changedFilesIgnorePattern.filter(\n    (pattern) => pattern.length\n  )\n  const changedFiles = (patterns.length > 0)\n    ? micromatch.not(allChangedFiles, patterns, {\n      dot: true,\n    })\n    : allChangedFiles\n\n  for (const changedFile of changedFiles) {\n    const dir = path.dirname(changedFile)\n\n    if (changedDirs.get(dir) === 'source') continue\n\n    const changeType: ChangeType = testPattern.some(pattern => micromatch.isMatch(changedFile, pattern))\n      ? 'test'\n      : 'source'\n    changedDirs.set(dir, changeType)\n  }\n\n  return Array.from(changedDirs.entries()).map(([dir, changeType]) => ({ dir, changeType }))\n}\n\n\nCANDIDATE 2 {\"path\":\"pkg-manager/core/test/install/misc.ts\",\"lines\":[53,53],\"columns\":[1,69],\"offsets\":[1538,1606],\"lexicalScore\":74,\"fileSha256\":\"ea713a978f5e6e30efd94451ee61a371024bd9d0ddd1d82aacc5f4269446a737\",\"excerptSha256\":\"1fdce1480c64fb6babd61a47e514eae63e19281f22ff3dac800957c84f139aaa\",\"complete\":false,\"declarationComplete\":true,\"selection\":\"enclosing-declaration\",\"omittedBytes\":41016}\n  const ignoreFile = (filename: string) => filename === 'readme.md'\n\n\nCANDIDATE 3 {\"path\":\"pkg-manager/core/test/install/gitBranchLockfile.test.ts\",\"lines\":[134,152],\"columns\":[1,8],\"offsets\":[3550,4099],\"lexicalScore\":67,\"fileSha256\":\"9ccc9ff9fbc6acd6d81673ac0bcf526f092130a9901212e37f355da0b3dae394\",\"excerptSha256\":\"71d60c144b5bc7c6bb9cb90b5441fd96569537a76572ff916ce985857ff681d3\",\"complete\":false,\"declarationComplete\":false,\"selection\":\"partial-line-window\",\"omittedBytes\":6200}\n  const branchName: string = 'main-branch'\n  jest.mocked(getCurrentBranch).mockReturnValue(Promise.resolve(branchName))\n\n  const otherLockfilePath: string = path.resolve('pnpm-lock.other.yaml')\n  writeYamlFile(otherLockfilePath, {\n    whatever: 'whatever',\n  })\n\n  expect(fs.existsSync(otherLockfilePath)).toBe(true)\n  expect(fs.existsSync(WANTED_LOCKFILE)).toBe(false)\n\n  const opts = testDefaults({\n    useGitBranchLockfile: true,\n    mergeGitBranchLockfiles: true,\n  })\n  await install({\n    dependencies: {\n      'is-positive': '^3.0.0',\n    },\n"
          }
        ],
        "structured_content": {
          "text": "EXPERIMENTAL SOURCE EVIDENCE v2. Verbatim modules or enclosing declarations when budget permits; lexical candidates; not resolved definitions. Absence is unproven. Multiple candidates remain alternatives. APIs, compilation and tests are not run by this wrapper.\n\nSTATUS partial inventory=observed ambiguity=unresolved\n\nCOVERAGE {\"complete\":false,\"files\":2715,\"excluded\":231,\"projects\":394,\"configs\":503,\"issues\":4,\"dependencyErrors\":0,\"missingIndexes\":0,\"exhaustive\":false,\"candidateLimitReached\":true}\n\nSEARCH CHECKS retrieved=pass (Lexical candidates only; coverage and ranking limits remain explicit); resolved=not-run (Use workspace_context for compiler resolution); typechecked=not-run (Use workspace_validate for compilation); tested=not-run (No assertion command executed); stable=unknown (Search records files as read; it does not establish an atomic workspace snapshot); artifactMatch=unknown (Installed dependency contents were not compared with registry artifacts) omittedChecks=0\n\nISSUES File exceeds 1 MB: reviewing/list/test/__fixtures__/many-deps/pnpm-lock.yaml; File chunk limit reached: $STUDY/runs/pnpm-changed-proj [omittedBytes=69 sha256=8568e0452e57fe961967deba00acb795256b0b52706410a18c9535b6d53065fd]; File chunk limit reached: $STUDY/runs/pnpm-changed-proj [omittedBytes=58 sha256=6a023c24a3d5a53ffd4ae45e7c530f1453d64f1ea424b203af2ec453d0c9a572] omittedIssues=1\n\nPROVENANCE searchSnapshot=2bd5e8d455e50afd2bb955df40157b21dc18d028cfaa9aaf90ff0151fe770a65 fullResponseSha256=b64813db81fcac2293a32cbcff8b6f625fbe9fa652aa9c3a0958e67d5dcfa117 sourceImportResolution=not-run\n\nOMISSIONS {\"inputSourceFiles\":5,\"uniqueCandidates\":5,\"returnedCandidates\":3,\"budgetCandidates\":2,\"duplicateSourceRecords\":0,\"invalidSourceRecords\":0,\"upstreamSourceFilesOmitted\":3,\"unavailableImports\":0,\"ambiguousImports\":0,\"syntaxErrorFiles\":0,\"sourceVerification\":\"full hashes checked at expansion and again before return; not an atomic snapshot\",\"metadata\":\"project/config lists and duplicate envelopes omitted; complete response retained only in coordinator diagnostics\"}\n\nCANDIDATE 1 {\"path\":\"workspace/filter-workspace-packages/src/getChangedPackages.ts\",\"lines\":[1,103],\"columns\":[1,3],\"offsets\":[0,3584],\"lexicalScore\":86,\"fileSha256\":\"d9457500e9be42f72ef83fde68890ba08617bcd4489478615d6c2e95421f408e\",\"excerptSha256\":\"d9457500e9be42f72ef83fde68890ba08617bcd4489478615d6c2e95421f408e\",\"complete\":true,\"declarationComplete\":false,\"selection\":\"complete-module\",\"omittedBytes\":0}\nimport assert from 'assert'\nimport path from 'path'\nimport util from 'util'\nimport { PnpmError } from '@pnpm/error'\nimport * as micromatch from 'micromatch'\nimport execa from 'execa'\nimport findUp from 'find-up'\nimport { type ProjectRootDir } from '@pnpm/types'\n\ntype ChangeType = 'source' | 'test'\n\ninterface ChangedDir { dir: string, changeType: ChangeType }\n\nexport async function getChangedPackages (\n  packageDirs: ProjectRootDir[],\n  commit: string,\n  opts: { workspaceDir: string, testPattern?: string[], changedFilesIgnorePattern?: string[] }\n): Promise<[ProjectRootDir[], ProjectRootDir[]]> {\n  // .git is a directory in regular repos, but a file in worktrees\n  const gitPath = await findUp('.git', { cwd: opts.workspaceDir, type: 'directory' }) ??\n                  await findUp('.git', { cwd: opts.workspaceDir, type: 'file' })\n\n  const repoRoot = path.resolve(gitPath ?? opts.workspaceDir, '..')\n\n  const changedDirs = (await getChangedDirsSinceCommit(commit, opts.workspaceDir, opts.testPattern ?? [], opts.changedFilesIgnorePattern ?? []))\n    .map(changedDir => ({ ...changedDir, dir: path.join(repoRoot, changedDir.dir) }))\n  const pkgChangeTypes = new Map<ProjectRootDir, ChangeType | undefined>()\n  for (const pkgDir of packageDirs) {\n    pkgChangeTypes.set(pkgDir, undefined)\n  }\n  for (const changedDir of changedDirs) {\n    let currentDir = changedDir.dir\n    while (!pkgChangeTypes.has(currentDir as ProjectRootDir)) {\n      const nextDir = path.dirname(currentDir)\n      if (nextDir === currentDir) break\n      currentDir = nextDir\n    }\n    if (pkgChangeTypes.get(currentDir as ProjectRootDir) === 'source') continue\n    pkgChangeTypes.set(currentDir as ProjectRootDir, changedDir.changeType)\n  }\n\n  const changedPkgs = [] as ProjectRootDir[]\n  const ignoreDependentForPkgs = [] as ProjectRootDir[]\n  for (const [changedDir, changeType] of pkgChangeTypes.entries()) {\n    switch (changeType) {\n    case 'source':\n      changedPkgs.push(changedDir)\n      break\n    case 'test':\n      ignoreDependentForPkgs.push(changedDir)\n      break\n    }\n  }\n  return [changedPkgs, ignoreDependentForPkgs]\n}\n\nasync function getChangedDirsSinceCommit (commit: string, workingDir: string, testPattern: string[], changedFilesIgnorePattern: string[]): Promise<ChangedDir[]> {\n  let diff!: string\n  try {\n    diff = (\n      await execa('git', [\n        'diff',\n        '--name-only',\n        commit,\n        '--',\n        workingDir,\n      ], { cwd: workingDir })\n    ).stdout\n  } catch (err: unknown) {\n    assert(util.types.isNativeError(err))\n    throw new PnpmError('FILTER_CHANGED', `Filtering by changed packages failed. ${'stderr' in err ? err.stderr as string : ''}`)\n  }\n  const changedDirs = new Map<string, ChangeType>()\n\n  if (!diff) {\n    return []\n  }\n\n  const allChangedFiles = diff.split('\\n')\n    // The prefix and suffix '\"' are appended to the Korean path\n    .map(line => line.replace(/^\"/, '').replace(/\"$/, ''))\n  const patterns = changedFilesIgnorePattern.filter(\n    (pattern) => pattern.length\n  )\n  const changedFiles = (patterns.length > 0)\n    ? micromatch.not(allChangedFiles, patterns, {\n      dot: true,\n    })\n    : allChangedFiles\n\n  for (const changedFile of changedFiles) {\n    const dir = path.dirname(changedFile)\n\n    if (changedDirs.get(dir) === 'source') continue\n\n    const changeType: ChangeType = testPattern.some(pattern => micromatch.isMatch(changedFile, pattern))\n      ? 'test'\n      : 'source'\n    changedDirs.set(dir, changeType)\n  }\n\n  return Array.from(changedDirs.entries()).map(([dir, changeType]) => ({ dir, changeType }))\n}\n\n\nCANDIDATE 2 {\"path\":\"pkg-manager/core/test/install/misc.ts\",\"lines\":[53,53],\"columns\":[1,69],\"offsets\":[1538,1606],\"lexicalScore\":74,\"fileSha256\":\"ea713a978f5e6e30efd94451ee61a371024bd9d0ddd1d82aacc5f4269446a737\",\"excerptSha256\":\"1fdce1480c64fb6babd61a47e514eae63e19281f22ff3dac800957c84f139aaa\",\"complete\":false,\"declarationComplete\":true,\"selection\":\"enclosing-declaration\",\"omittedBytes\":41016}\n  const ignoreFile = (filename: string) => filename === 'readme.md'\n\n\nCANDIDATE 3 {\"path\":\"pkg-manager/core/test/install/gitBranchLockfile.test.ts\",\"lines\":[134,152],\"columns\":[1,8],\"offsets\":[3550,4099],\"lexicalScore\":67,\"fileSha256\":\"9ccc9ff9fbc6acd6d81673ac0bcf526f092130a9901212e37f355da0b3dae394\",\"excerptSha256\":\"71d60c144b5bc7c6bb9cb90b5441fd96569537a76572ff916ce985857ff681d3\",\"complete\":false,\"declarationComplete\":false,\"selection\":\"partial-line-window\",\"omittedBytes\":6200}\n  const branchName: string = 'main-branch'\n  jest.mocked(getCurrentBranch).mockReturnValue(Promise.resolve(branchName))\n\n  const otherLockfilePath: string = path.resolve('pnpm-lock.other.yaml')\n  writeYamlFile(otherLockfilePath, {\n    whatever: 'whatever',\n  })\n\n  expect(fs.existsSync(otherLockfilePath)).toBe(true)\n  expect(fs.existsSync(WANTED_LOCKFILE)).toBe(false)\n\n  const opts = testDefaults({\n    useGitBranchLockfile: true,\n    mergeGitBranchLockfiles: true,\n  })\n  await install({\n    dependencies: {\n      'is-positive': '^3.0.0',\n    },\n"
        }
      },
      "error": null
    },
    {
      "idSha256": "57dbfc296665f072254d15345fb3a2bbf457ae03cbb6ee8f6020e8f11d4b056b",
      "type": "command_execution",
      "status": "completed",
      "originalLine": 10,
      "outputSha256": "954ec5dad7eaf581ad4bfa090369b244ac0120b43722ef2efff5ba89fc52046f",
      "outputBytes": 10,
      "exitCode": 0,
      "stdout": "AGENTS.md\n"
    },
    {
      "idSha256": "9462965f2002ffb6e5ebf84d633936910b584441932a9dee26f8497eb9d82c75",
      "type": "command_execution",
      "status": "completed",
      "originalLine": 12,
      "outputSha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
      "outputBytes": 0,
      "exitCode": 0,
      "stdout": ""
    },
    {
      "idSha256": "919299b8b8860441036a08b8ce4a6486ee1712366afaec2286c8579387359f27",
      "type": "command_execution",
      "status": "completed",
      "originalLine": 14,
      "outputSha256": "d9457500e9be42f72ef83fde68890ba08617bcd4489478615d6c2e95421f408e",
      "outputBytes": 3584,
      "exitCode": 0,
      "stdout": "import assert from 'assert'\nimport path from 'path'\nimport util from 'util'\nimport { PnpmError } from '@pnpm/error'\nimport * as micromatch from 'micromatch'\nimport execa from 'execa'\nimport findUp from 'find-up'\nimport { type ProjectRootDir } from '@pnpm/types'\n\ntype ChangeType = 'source' | 'test'\n\ninterface ChangedDir { dir: string, changeType: ChangeType }\n\nexport async function getChangedPackages (\n  packageDirs: ProjectRootDir[],\n  commit: string,\n  opts: { workspaceDir: string, testPattern?: string[], changedFilesIgnorePattern?: string[] }\n): Promise<[ProjectRootDir[], ProjectRootDir[]]> {\n  // .git is a directory in regular repos, but a file in worktrees\n  const gitPath = await findUp('.git', { cwd: opts.workspaceDir, type: 'directory' }) ??\n                  await findUp('.git', { cwd: opts.workspaceDir, type: 'file' })\n\n  const repoRoot = path.resolve(gitPath ?? opts.workspaceDir, '..')\n\n  const changedDirs = (await getChangedDirsSinceCommit(commit, opts.workspaceDir, opts.testPattern ?? [], opts.changedFilesIgnorePattern ?? []))\n    .map(changedDir => ({ ...changedDir, dir: path.join(repoRoot, changedDir.dir) }))\n  const pkgChangeTypes = new Map<ProjectRootDir, ChangeType | undefined>()\n  for (const pkgDir of packageDirs) {\n    pkgChangeTypes.set(pkgDir, undefined)\n  }\n  for (const changedDir of changedDirs) {\n    let currentDir = changedDir.dir\n    while (!pkgChangeTypes.has(currentDir as ProjectRootDir)) {\n      const nextDir = path.dirname(currentDir)\n      if (nextDir === currentDir) break\n      currentDir = nextDir\n    }\n    if (pkgChangeTypes.get(currentDir as ProjectRootDir) === 'source') continue\n    pkgChangeTypes.set(currentDir as ProjectRootDir, changedDir.changeType)\n  }\n\n  const changedPkgs = [] as ProjectRootDir[]\n  const ignoreDependentForPkgs = [] as ProjectRootDir[]\n  for (const [changedDir, changeType] of pkgChangeTypes.entries()) {\n    switch (changeType) {\n    case 'source':\n      changedPkgs.push(changedDir)\n      break\n    case 'test':\n      ignoreDependentForPkgs.push(changedDir)\n      break\n    }\n  }\n  return [changedPkgs, ignoreDependentForPkgs]\n}\n\nasync function getChangedDirsSinceCommit (commit: string, workingDir: string, testPattern: string[], changedFilesIgnorePattern: string[]): Promise<ChangedDir[]> {\n  let diff!: string\n  try {\n    diff = (\n      await execa('git', [\n        'diff',\n        '--name-only',\n        commit,\n        '--',\n        workingDir,\n      ], { cwd: workingDir })\n    ).stdout\n  } catch (err: unknown) {\n    assert(util.types.isNativeError(err))\n    throw new PnpmError('FILTER_CHANGED', `Filtering by changed packages failed. ${'stderr' in err ? err.stderr as string : ''}`)\n  }\n  const changedDirs = new Map<string, ChangeType>()\n\n  if (!diff) {\n    return []\n  }\n\n  const allChangedFiles = diff.split('\\n')\n    // The prefix and suffix '\"' are appended to the Korean path\n    .map(line => line.replace(/^\"/, '').replace(/\"$/, ''))\n  const patterns = changedFilesIgnorePattern.filter(\n    (pattern) => pattern.length\n  )\n  const changedFiles = (patterns.length > 0)\n    ? micromatch.not(allChangedFiles, patterns, {\n      dot: true,\n    })\n    : allChangedFiles\n\n  for (const changedFile of changedFiles) {\n    const dir = path.dirname(changedFile)\n\n    if (changedDirs.get(dir) === 'source') continue\n\n    const changeType: ChangeType = testPattern.some(pattern => micromatch.isMatch(changedFile, pattern))\n      ? 'test'\n      : 'source'\n    changedDirs.set(dir, changeType)\n  }\n\n  return Array.from(changedDirs.entries()).map(([dir, changeType]) => ({ dir, changeType }))\n}\n"
    },
    {
      "idSha256": "3f21b4ea2fbe67cd553dbcfae108e6632bdb6c655218a1e4ad5a543cc24800f4",
      "type": "command_execution",
      "status": "completed",
      "originalLine": 16,
      "outputSha256": "c5e2413f308b24ccab3edd157ff03daa80b9db3e040c6d57f6c496b99c66c405",
      "outputBytes": 5296,
      "exitCode": 0,
      "stdout": "# Agent Guide to pnpm Repository\n\nThis document provides context and instructions for AI agents working on the pnpm codebase.\n\n## Repository Structure\n\nThe pnpm codebase is a monorepo managed by pnpm itself. The root contains functional directories organized by domain:\n\n### Core Directories\n\n-   `pnpm/`: The CLI entry point and main package.\n-   `pkg-manager/`: Core package management logic (installation, linking, etc.).\n-   `resolving/`: Dependency resolution logic (resolvers for npm, tarballs, git, etc.).\n-   `fetching/`: Package fetching logic.\n-   `store/`: Store management logic (content-addressable storage).\n-   `lockfile/`: Lockfile handling, parsing, and utilities.\n\n### CLI & Configuration\n\n-   `cli/`: CLI command implementations and infrastructure.\n-   `config/`: Configuration management and parsing.\n-   `hooks/`: pnpm hooks (readPackage, etc.).\n-   `completion/`: Shell completion support.\n\n### Other Functional Directories\n\n-   `network/`: Network-related utilities (proxy, fetch, auth).\n-   `workspace/`: Workspace-related utilities.\n-   `exec/`: Execution-related commands (run, exec, dlx).\n-   `env/`: Node.js environment management.\n-   `cache/`: Cache-related commands and utilities.\n-   `patching/`: Package patching functionality.\n-   `reviewing/`: License and dependency review tools.\n-   `releasing/`: Release and publishing utilities.\n\n### Shared Utilities\n\n-   `packages/`: Shared utility packages (constants, error handling, logger, types, etc.).\n-   `fs/`: Filesystem utilities.\n-   `crypto/`: Cryptographic utilities.\n-   `text/`: Text processing utilities.\n\n## Setup & Build\n\nTo set up the environment and build the project:\n\n```bash\npnpm install\npnpm run compile\n```\n\n## Testing\n\nNever run all tests in the repository as it takes a lot of time.\n\nRun tests for a specific project instead:\n\n```bash\n# From the project directory\npnpm test\n\n# From the root, filtering by package name\npnpm --filter <package_name> test\n```\n\nOr better yet, run tests for a specific file:\n\n```bash\npnpm --filter <package_name> test <file_path>\n```\n\nOr a specific test case in a specific file:\n\n```bash\npnpm --filter <package_name> test <file_path> -t <test_name_pattern>\n```\n\n## Linting\n\nTo run all linting checks:\n\n```bash\npnpm run lint\n```\n\n## Contribution Workflow\n\n### Changesets\n\nIf your changes affect published packages, you MUST create a changeset file in the `.changeset` directory. The changeset file should describe the change and specify the packages that are affected with the pending version bump types: patch, minor, or major.\n\n**IMPORTANT: Always explicitly include `\"pnpm\"` in the changeset** with the appropriate version bump (patch, minor, or major). The pnpm CLI will only receive automatic patch bumps from its dependencies, so if your change warrants a minor or major version bump for the CLI, you must specify it explicitly. The changeset description will appear on the release notes page.\n\nExample:\n\n```\n---\n\"@pnpm/core\": minor\n\"pnpm\": minor\n---\n\nAdded a new setting `blockExoticSubdeps` that prevents the resolution of exotic protocols in transitive dependencies [#10352](https://github.com/pnpm/pnpm/issues/10352).\n```\n\n**Versioning Guidelines for pnpm CLI:**\n- **patch**: Bug fixes, internal refactors, and changes that don't require documentation updates\n- **minor**: New features, settings, or commands that should be documented (anything users should know about)\n- **major**: Breaking changes\n\n### Commit Messages\n\nFollow the [Conventional Commits](https://www.conventionalcommits.org/) specification.\n    -   `feat`: a new feature\n    -   `fix`: a bug fix\n    -   `docs`: documentation only changes\n    -   `style`: formatting, missing semi-colons, etc.\n    -   `refactor`: code change that neither fixes a bug nor adds a feature\n    -   `perf`: a code change that improves performance\n    -   `test`: adding missing tests\n    -   `chore`: changes to build process or auxiliary tools\n\n## Code Style\n\nThis repository uses [Standard Style](https://github.com/standard/standard) with a few modifications:\n-   **Trailing commas** are used.\n-   **Functions are preferred** over classes.\n-   **Functions are declared after they are used** (hoisting is relied upon).\n-   **Import Order**:\n    1.  Standard libraries (e.g., `fs`, `path`).\n    2.  External dependencies (sorted alphabetically).\n    3.  Relative imports.\n\nTo ensure your code adheres to the style guide, run:\n\n```bash\npnpm run lint\n```\n\n## Common Gotchas\n\n### Error Type Checking in Jest\n\nWhen checking if a caught error is an `Error` object, **do not use `instanceof Error`**. Jest runs tests in a VM context where `instanceof` checks can fail across realms.\n\nInstead, use `util.types.isNativeError()`:\n\n```typescript\nimport util from 'util'\n\ntry {\n  // ... some operation\n} catch (err: unknown) {\n  // ❌ Wrong - may fail in Jest\n  if (err instanceof Error && 'code' in err && err.code === 'ENOENT') {\n    return null\n  }\n  \n  // ✅ Correct - works across realms\n  if (util.types.isNativeError(err) && 'code' in err && err.code === 'ENOENT') {\n    return null\n  }\n  throw err\n}\n```\n\n## Key Configuration Files\n\n-   `pnpm-workspace.yaml`: Defines the workspace structure.\n-   `package.json` (root): Root scripts and devDependencies.\n-   `CONTRIBUTING.md`: Detailed contribution guidelines.\n"
    },
    {
      "idSha256": "a26c6de8780e4e792e8ad6df3fb6ac31fe229b7b2f8bc2aa29c602e8d647918c",
      "type": "command_execution",
      "status": "completed",
      "originalLine": 21,
      "outputSha256": "658e5d88454242def8d723b99391cdbcb669d20530a75f915825b7494de8ceca",
      "outputBytes": 1157,
      "exitCode": 0,
      "stdout": "\n> test:typelatch-benchmark\n> TZ=UTC TSX_DISABLE_CACHE=1 node --import '$REPOSITORY/node_modules/tsx/dist/loader.mjs' '$REPOSITORY/artifacts/research/large-monorepo/maintenance/public-check.mjs'\n\n{\"passed\":true,\"cases\":3,\"failed\":0,\"results\":[{\"id\":\"move-between-projects\",\"passed\":true,\"expected\":[[\"packages/alpha\",\"packages/beta\"],[]],\"actual\":[[\"packages/alpha\",\"packages/beta\"],[]],\"renameDetected\":true,\"diffStatus\":\"R100\\tpackages/alpha/src/value.ts\\tpackages/beta/src/value.ts\",\"gitFixture\":\"$TEMP_ROOTtypelatch-pnpm-maintenance-T6slTC\"},{\"id\":\"ordinary-edit\",\"passed\":true,\"expected\":[[\"packages/alpha\"],[]],\"actual\":[[\"packages/alpha\"],[]],\"renameDetected\":false,\"diffStatus\":\"M\\tpackages/alpha/src/value.ts\",\"gitFixture\":\"$TEMP_ROOTtypelatch-pnpm-maintenance-NdQCZY\"},{\"id\":\"unchanged\",\"passed\":true,\"expected\":[[],[]],\"actual\":[[],[]],\"renameDetected\":false,\"diffStatus\":\"\",\"gitFixture\":\"$TEMP_ROOTtypelatch-pnpm-maintenance-E67n4q\"}]}\n"
    },
    {
      "idSha256": "00bf463c9d57c76acacd039e798555d6101dbddf9708e68d232d71a4c30e0633",
      "type": "command_execution",
      "status": "completed",
      "originalLine": 23,
      "outputSha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
      "outputBytes": 0,
      "exitCode": 0,
      "stdout": ""
    },
    {
      "idSha256": "fcf995ce3ce15e435101e7a49adbafdc79fcf9203eefbab40b5d124cae39b03f",
      "type": "command_execution",
      "status": "completed",
      "originalLine": 25,
      "outputSha256": "f8b846ed03e0fdad376ccb78405ace5ac4f54d5a45bba2f7ba3129bcde5023b2",
      "outputBytes": 101,
      "exitCode": 0,
      "stdout": " workspace/filter-workspace-packages/src/getChangedPackages.ts | 1 +\n 1 file changed, 1 insertion(+)\n"
    }
  ],
  "evidenceBoundary": "Only item.completed command_execution stdout and mcp_tool_call result/error payloads. No agent reasoning, assistant messages, command input, unrelated item fields or file edit bodies. Tool-returned message fields remain tool evidence."
}
