OverviewFilesCommitsIssuesDocsReleasesPeers

P2P-Hiverelay / test / unit / ecosystem-consumers.test.js

1461 lines · javascript ref:
import test from 'brittle'
import fs from 'node:fs'
import { spawnSync } from 'node:child_process'
import os from 'node:os'
import path from 'node:path'
import {
  CURRENT_HIVERELAY_VERSION,
  DEFAULT_DEPENDENCY_MODE,
  EXPECTED_CURRENT_CONSUMERS,
  HIVERELAY_DEPS,
  checkConsumerState,
  formatConsumerReport,
  getExpectedCurrentConsumers,
  scanConsumerSourceChecks,
  scanCurrentConsumerLockChecks,
  scanHiverelayConsumers,
  scanSnapshotVersionChecks,
  scanStaleConsumerSourceChecks,
  verifyNpmLatestDistTags
} from '../../scripts/audit-ecosystem-consumers.mjs'
import { checkEcosystemWorkspace } from '../../scripts/check-ecosystem-workspace.mjs'
import { commitEcosystemConsumers } from '../../scripts/commit-ecosystem-consumers.mjs'
import { syncEcosystemConsumers } from '../../scripts/sync-ecosystem-consumers.mjs'

function npmLatestVersions (version, overrides = {}) {
  return {
    ...Object.fromEntries(HIVERELAY_DEPS.map(dep => [dep, version])),
    ...overrides
  }
}

test('ecosystem consumer audit classifies current, stale, and ignored consumers', (t) => {
  const root = fixtureWorkspace()
  const expectedCurrentPaths = [
    '01-browser/pearbrowser-desktop/package.json',
    '02-apps/pearpaste/package.json',
    '02-apps/pear-pos/package.json',
    '02-apps/pear-tickets/package.json',
    '02-apps/peerit/package.json',
    '03-sites/pearbrowser-publishers/src/p2pbuilders/package.json',
    '04-experiments/Opengit/packages/opengit-relay/package.json',
    '04-experiments/anongpt-native/package.json',
    '04-experiments/hiverelay-test/package.json'
  ]
  for (const consumerPath of expectedCurrentPaths) {
    t.ok(
      EXPECTED_CURRENT_CONSUMERS.some(consumer => consumer.path === consumerPath),
      `${consumerPath} remains in the current-consumer inventory`
    )
  }
  writeExpectedConsumerPackages(root, EXPECTED_CURRENT_CONSUMERS)
  writePackage(root, '00-core/hiverelay/packages/client/package.json', {
    dependencies: {
      'p2p-hiverelay': '^0.20.2'
    }
  })
  writePackage(root, '00-core/hr-fleet/packages/client/package.json', {
    dependencies: {
      'p2p-hiverelay': '^0.19.0'
    }
  })

  const rows = scanHiverelayConsumers({ workspaceRoot: root })
  const summary = checkConsumerState(rows, {
    expectedVersion: '0.20.2',
    expectedCurrent: EXPECTED_CURRENT_CONSUMERS,
    expectedStale: []
  })
  const report = formatConsumerReport(summary)

  t.ok(summary.ok)
  t.is(summary.current.length, expectedCurrentPaths.length)
  t.is(summary.stale.length, 0)
  t.is(summary.ignored.length, 2)
  t.ok(report.includes('02-apps/pear-pos/package.json'))
  t.ok(report.includes('02-apps/pear-tickets/package.json'))
  t.ok(report.includes('02-apps/peerit/package.json'))
  t.ok(report.includes('04-experiments/hiverelay-test/package.json'))
  t.ok(report.includes('Known stale non-bundled consumers:\n- none'))
  t.ok(report.includes('Direct-consumer scan exclusions:'))
})

test('ecosystem consumer audit fails on unclassified Hiverelay pins', (t) => {
  const root = fixtureWorkspace()
  writePackage(root, '02-apps/unknown/package.json', {
    dependencies: {
      'p2p-hiverelay-client': '^0.8.0'
    }
  })

  const rows = scanHiverelayConsumers({ workspaceRoot: root })
  const summary = checkConsumerState(rows, {
    expectedVersion: '0.20.2',
    expectedCurrent: [],
    expectedStale: []
  })

  t.absent(summary.ok)
  t.ok(summary.errors[0].includes('Unclassified Hiverelay package consumer'))
})

test('ecosystem consumer audit skips the workspace research corpus before parsing fixtures', (t) => {
  const root = fixtureWorkspace()
  const fixture = path.join(root, '00-brain', 'compiled-corpus', 'test', 'fixtures', 'package.json')
  fs.mkdirSync(path.dirname(fixture), { recursive: true })
  fs.writeFileSync(fixture, '{ definitely-not-json')
  writePackage(root, '02-apps/unknown/package.json', {
    dependencies: {
      'p2p-hiverelay-client': '^0.8.0'
    }
  })

  const rows = scanHiverelayConsumers({ workspaceRoot: root })
  t.is(rows.length, 1, 'research fixture is excluded but active workspace consumers remain visible')
  t.is(rows[0].path, '02-apps/unknown/package.json')
})

test('ecosystem consumer audit skips unregistered HiveRelay feature worktrees', (t) => {
  const root = fixtureWorkspace()
  writePackage(root, '00-core/hr-feature/package.json', {
    name: 'p2p-hiverelay-monorepo',
    version: '9.9.9'
  })
  writePackage(root, '00-core/hr-feature/packages/client/package.json', {
    name: 'p2p-hiverelay-client',
    dependencies: {
      'p2p-hiverelay': '^9.9.9'
    }
  })
  writePackage(root, '02-apps/unknown/package.json', {
    dependencies: {
      'p2p-hiverelay-client': '^0.8.0'
    }
  })

  const rows = scanHiverelayConsumers({ workspaceRoot: root })
  t.is(rows.length, 1, 'feature worktree packages cannot become false consumer pins')
  t.is(rows[0].path, '02-apps/unknown/package.json')
})

test('ecosystem consumer audit fails when the known stale inventory changes', (t) => {
  const root = fixtureWorkspace()
  writePackage(root, '02-apps/pearpaste/package.json', {
    dependencies: {
      'p2p-hiverelay-client': '^0.20.2'
    }
  })

  const rows = scanHiverelayConsumers({ workspaceRoot: root })
  const summary = checkConsumerState(rows, {
    expectedVersion: '0.20.2',
    expectedCurrent: [],
    expectedStale: [{
      path: '02-apps/pearpaste/package.json',
      deps: {
        'p2p-hiverelay-client': '^0.9.2'
      }
    }]
  })

  t.absent(summary.ok)
  t.ok(summary.errors[0].includes('known stale consumer has p2p-hiverelay-client'))
})

test('ecosystem consumer audit reports source-level migration markers', (t) => {
  const root = fixtureWorkspace()
  writePackage(root, '03-sites/pearbrowser-publishers/src/p2pbuilders/package.json', {
    dependencies: {
      'p2p-hiverelay': '^0.4.2'
    }
  })
  writeFile(root, '03-sites/pearbrowser-publishers/src/p2pbuilders/scripts/publish.js', `
    const { HiveRelayClient } = await import('p2p-hiverelay/client')
  `)

  const rows = scanHiverelayConsumers({ workspaceRoot: root })
  const expectedStale = [{
    path: '03-sites/pearbrowser-publishers/src/p2pbuilders/package.json',
    deps: {
      'p2p-hiverelay': '^0.4.2'
    },
    sourceChecks: [{
      file: '03-sites/pearbrowser-publishers/src/p2pbuilders/scripts/publish.js',
      label: 'legacy client subpath',
      term: 'p2p-hiverelay/client'
    }]
  }]
  const sourceChecks = scanStaleConsumerSourceChecks({ workspaceRoot: root, expectedStale })
  const summary = checkConsumerState(rows, {
    expectedVersion: '0.20.2',
    expectedCurrent: [],
    expectedStale,
    sourceChecks
  })

  t.ok(summary.ok)
  t.is(summary.sourceChecks.length, 1)
  t.ok(summary.sourceChecks[0].present)
  t.ok(formatConsumerReport(summary).includes('Source-level migration checks'))
})

test('ecosystem consumer audit guards PearPaste current Hiverelay docs', (t) => {
  const root = fixtureWorkspace()
  const pearpaste = EXPECTED_CURRENT_CONSUMERS.find(consumer => consumer.path === '02-apps/pearpaste/package.json')
  writeExpectedConsumerPackages(root, [pearpaste])
  writeFile(root, '02-apps/pearpaste/docs/RECOVERY_DESIGN.md', `
    "p2p-hiverelay": "file:../../00-core/hiverelay/packages/core",
    "p2p-hiverelay-client": "file:../../00-core/hiverelay/packages/client"
  `)
  writeFile(root, '02-apps/pearpaste/docs/PEARPASTE_TECHNICAL_SPEC.md', `
    Integrate the HiveRelay \`${CURRENT_HIVERELAY_VERSION}\` local workspace packages by default.
  `)
  writeFile(root, '02-apps/pearpaste/REVIEW.md', `
    current p2p-hiverelay-client@${CURRENT_HIVERELAY_VERSION} split-client package line
  `)
  writeFile(root, '02-apps/pearpaste/scripts/probe-circuit.mjs', `
    // current HiveRelay fleet
  `)
  writeFile(root, '02-apps/pearpaste/test/unit/hiverelay-upgrade.test.js', `
    test('HiveRelay customer path uses npm latest defaults', async (t) => {
      t.is(pkg.optionalDependencies['p2p-hiverelay'], 'latest')
      t.is(pkg.optionalDependencies['p2p-hiverelay-client'], 'latest')
    })
  `)

  const rows = scanHiverelayConsumers({ workspaceRoot: root })
  const sourceChecks = scanConsumerSourceChecks({
    workspaceRoot: root,
    expectedCurrent: [pearpaste],
    expectedStale: []
  })
  const summary = checkConsumerState(rows, {
    expectedVersion: CURRENT_HIVERELAY_VERSION,
    expectedCurrent: [pearpaste],
    expectedStale: [],
    sourceChecks
  })

  t.ok(summary.ok)

  writeFile(root, '02-apps/pearpaste/docs/RECOVERY_DESIGN.md', `
    Blocked on upstream npm publish
    "p2p-hiverelay-client": "^0.9.2"
  `)
  const failedChecks = scanConsumerSourceChecks({
    workspaceRoot: root,
    expectedCurrent: [pearpaste],
    expectedStale: []
  })
  const failedSummary = checkConsumerState(rows, {
    expectedVersion: CURRENT_HIVERELAY_VERSION,
    expectedCurrent: [pearpaste],
    expectedStale: [],
    sourceChecks: failedChecks
  })

  t.absent(failedSummary.ok)
  t.ok(failedSummary.errors.some(error => error.includes('Disallowed source-level migration marker found')))
})

test('ecosystem consumer audit guards PearBrowser and POS current Hiverelay source defaults', (t) => {
  const root = fixtureWorkspace()
  const expectedCurrent = EXPECTED_CURRENT_CONSUMERS.filter(consumer => [
    '01-browser/pearbrowser-desktop/package.json',
    '02-apps/pear-pos/package.json'
  ].includes(consumer.path))
  const pearbrowser = expectedCurrent.find(consumer => consumer.path === '01-browser/pearbrowser-desktop/package.json')
  writeExpectedConsumerPackages(root, expectedCurrent)
  writeSourceCheckTerms(root, pearbrowser, CURRENT_HIVERELAY_VERSION)
  writeFile(root, '02-apps/pear-pos/app/backend/hiverelay-client.js', `
    // ESM -> CJS bridge for p2p-hiverelay-client@${CURRENT_HIVERELAY_VERSION}
  `)
  writeFile(root, '02-apps/pear-pos/app/backend/hiverelay-sync.js', `
    // Seed lifecycle (v${CURRENT_HIVERELAY_VERSION})
  `)
  writeFile(root, '02-apps/pear-pos/ARCHITECTURE.md', `
    p2p-hiverelay-client@${CURRENT_HIVERELAY_VERSION}
  `)
  writeFile(root, '02-apps/pear-pos/PLAN.md', `
    current HiveRelay ${CURRENT_HIVERELAY_VERSION} split packages
  `)
  writeFile(root, '02-apps/pear-pos/RESEARCH.md', `
    SDK: \`p2p-hiverelay-client@${CURRENT_HIVERELAY_VERSION}\`
  `)
  writeFile(root, '02-apps/pear-pos/scripts/validate-publish-surface.mjs', `
    failures.push('package.json optionalDependencies.p2p-hiverelay should default to npm latest')
    failures.push('package.json optionalDependencies.p2p-hiverelay-client should default to npm latest')
  `)

  const rows = scanHiverelayConsumers({ workspaceRoot: root })
  const sourceChecks = scanConsumerSourceChecks({
    workspaceRoot: root,
    expectedCurrent,
    expectedStale: []
  })
  const summary = checkConsumerState(rows, {
    expectedVersion: CURRENT_HIVERELAY_VERSION,
    expectedCurrent,
    expectedStale: [],
    sourceChecks
  })

  t.ok(summary.ok)

  writeSourceCheckTerms(root, pearbrowser, '0.16.3')
  const failedChecks = scanConsumerSourceChecks({
    workspaceRoot: root,
    expectedCurrent,
    expectedStale: []
  })
  const failedSummary = checkConsumerState(rows, {
    expectedVersion: '0.20.2',
    expectedCurrent,
    expectedStale: [],
    sourceChecks: failedChecks
  })

  t.absent(failedSummary.ok)
  t.ok(failedSummary.errors.some(error => error.includes('PearBrowser bundled catalog seed')))
})

test('ecosystem consumer audit guards anonGPT live relay consumer source contract', (t) => {
  const root = fixtureWorkspace()
  const anongpt = EXPECTED_CURRENT_CONSUMERS.find(consumer => consumer.path === '04-experiments/anongpt-native/package.json')
  writeExpectedConsumerPackages(root, [anongpt])
  writeFile(root, '04-experiments/anongpt-native/docs/ARCHITECTURE.md', 'Network privacy (hide your IP) rides on **HiveRelay**\nrelays — relay (1 hop) or onion (2 hops).\n')
  writeFile(root, '04-experiments/anongpt-native/backend/forward-transport.js', `
    // routes inference through the production HiveRelay relays' forward service
  `)
  writeFile(root, '04-experiments/anongpt-native/test/hiverelay-upgrade.test.cjs', `
    test('HiveRelay customer transport path uses npm latest by default', async () => {
      assert.equal(pkg.dependencies['p2p-hiverelay'], 'latest')
    })
  `)

  const rows = scanHiverelayConsumers({ workspaceRoot: root })
  const sourceChecks = scanConsumerSourceChecks({
    workspaceRoot: root,
    expectedCurrent: [anongpt],
    expectedStale: []
  })
  const summary = checkConsumerState(rows, {
    expectedVersion: '0.20.2',
    expectedCurrent: [anongpt],
    expectedStale: [],
    sourceChecks
  })

  t.ok(summary.ok)

  writeFile(root, '04-experiments/anongpt-native/backend/forward-transport.js', `
    // local standalone forwarder only
  `)
  const failedChecks = scanConsumerSourceChecks({
    workspaceRoot: root,
    expectedCurrent: [anongpt],
    expectedStale: []
  })
  const failedSummary = checkConsumerState(rows, {
    expectedVersion: '0.20.2',
    expectedCurrent: [anongpt],
    expectedStale: [],
    sourceChecks: failedChecks
  })

  t.absent(failedSummary.ok)
  t.ok(failedSummary.errors.some(error => error.includes('anonGPT forward transport targets production HiveRelay forward service')))
})

test('ecosystem consumer audit guards Peerit source-only publish client compatibility', (t) => {
  const root = fixtureWorkspace()
  const peerit = EXPECTED_CURRENT_CONSUMERS.find(consumer => consumer.path === '02-apps/peerit/package.json')
  writeExpectedConsumerPackages(root, [peerit])
  writeFile(root, '02-apps/peerit/publish.mjs', `
    const HIVERELAY_CLIENT_PACKAGE = 'p2p-hiverelay-client'
    const HIVERELAY_CLIENT_PATHS = [
      ['HIVERELAY_ROOT', process.env.HIVERELAY_ROOT]
    ]
    throw new Error(\`\${source} does not export HiveRelayClient\`)
  `)
  writeFile(root, '02-apps/peerit/README.md', [
    'The app itself has no install step and no runtime npm dependencies. Publishing is',
    'the only workflow that needs the HiveRelay client.',
    '',
    'The publisher loads `p2p-hiverelay-client` from an installed package, an explicit',
    'HiveRelay env path, or a discoverable sibling/workspace checkout.'
  ].join('\n'))

  const rows = scanHiverelayConsumers({ workspaceRoot: root })
  const sourceChecks = scanConsumerSourceChecks({
    workspaceRoot: root,
    expectedCurrent: [peerit],
    expectedStale: []
  })
  const lockChecks = scanCurrentConsumerLockChecks({
    workspaceRoot: root,
    expectedVersion: '0.20.2',
    expectedCurrent: [peerit]
  })
  const summary = checkConsumerState(rows, {
    expectedVersion: '0.20.2',
    expectedCurrent: [peerit],
    expectedStale: [],
    sourceChecks,
    lockChecks
  })

  t.ok(summary.ok)
  t.is(rows.length, 0, 'source-only peerit has no package-dependency scan row')
  t.is(lockChecks.length, 0, 'source-only peerit does not require a lockfile')
  t.ok(formatConsumerReport(summary).includes('no package deps; source-only compatibility guard'))

  writePackage(root, '02-apps/peerit/package.json', {
    dependencies: {
      'p2p-hiverelay-client': '^0.9.2'
    }
  })
  const pinnedSummary = checkConsumerState(scanHiverelayConsumers({ workspaceRoot: root }), {
    expectedVersion: '0.20.2',
    expectedCurrent: [peerit],
    expectedStale: [],
    sourceChecks
  })
  t.absent(pinnedSummary.ok)
  t.ok(pinnedSummary.errors.some(error => error.includes('expected (none)')))

  writePackage(root, '02-apps/peerit/package.json', {})
  writeFile(root, '02-apps/peerit/publish.mjs', `
    const legacy = 'p2p-hiverelay/client'
  `)
  const failedChecks = scanConsumerSourceChecks({
    workspaceRoot: root,
    expectedCurrent: [peerit],
    expectedStale: []
  })
  const failedSummary = checkConsumerState(scanHiverelayConsumers({ workspaceRoot: root }), {
    expectedVersion: '0.20.2',
    expectedCurrent: [peerit],
    expectedStale: [],
    sourceChecks: failedChecks
  })
  t.absent(failedSummary.ok)
  t.ok(failedSummary.errors.some(error => error.includes('Peerit publish path loads the split HiveRelay client package')))
  t.ok(failedSummary.errors.some(error => error.includes('Disallowed source-level migration marker found')))
})

test('ecosystem sync keeps Peerit source-only manifest dependency-free', (t) => {
  const root = fixtureWorkspace()
  const peerit = getExpectedCurrentConsumers({ dependencyMode: 'npm-latest' })
    .find(consumer => consumer.path === '02-apps/peerit/package.json')
  writePackage(root, '02-apps/peerit/package.json', {
    optionalDependencies: {
      'p2p-hiverelay-client': '^0.9.2'
    }
  })
  writeFile(root, '02-apps/peerit/publish.mjs', `
    const HIVERELAY_CLIENT_PACKAGE = 'p2p-hiverelay-client'
    const HIVERELAY_CLIENT_PATHS = [
      ['HIVERELAY_ROOT', process.env.HIVERELAY_ROOT]
    ]
    throw new Error(\`\${source} does not export HiveRelayClient\`)
  `)
  writeFile(root, '02-apps/peerit/README.md', [
    'The app itself has no install step and no runtime npm dependencies. Publishing is',
    'the only workflow that needs the HiveRelay client.',
    '',
    'The publisher loads `p2p-hiverelay-client` from an installed package, an explicit',
    'HiveRelay env path, or a discoverable sibling/workspace checkout.'
  ].join('\n'))

  const result = syncEcosystemConsumers({
    workspaceRoot: root,
    expectedVersion: '0.20.2',
    expectedCurrent: [peerit],
    dependencyMode: 'npm-latest',
    npmLatestVersions: npmLatestVersions('0.20.2'),
    snapshotChecks: false
  })

  t.ok(result.ok)
  t.ok(result.changes.some(change => change.includes('02-apps/peerit/package.json: p2p-hiverelay-client removed')))
  t.is(result.summary.lockChecks.length, 0)
  t.alike(readPackage(root, '02-apps/peerit/package.json').optionalDependencies, undefined)
})

test('ecosystem consumer audit fails when source-level migration markers move', (t) => {
  const root = fixtureWorkspace()
  writePackage(root, '04-experiments/Opengit/packages/opengit-relay/package.json', {
    optionalDependencies: {
      'p2p-hiverelay-client': '^0.8.13'
    }
  })
  writeFile(root, '04-experiments/Opengit/packages/opengit-relay/lib/relay.js', `
    async function loadClient () { return import('p2p-hiverelay-client') }
  `)

  const rows = scanHiverelayConsumers({ workspaceRoot: root })
  const expectedStale = [{
    path: '04-experiments/Opengit/packages/opengit-relay/package.json',
    deps: {
      'p2p-hiverelay-client': '^0.8.13'
    },
    sourceChecks: [{
      file: '04-experiments/Opengit/packages/opengit-relay/lib/relay.js',
      label: 'CommonJS client load',
      term: "require('p2p-hiverelay-client')"
    }]
  }]
  const sourceChecks = scanStaleConsumerSourceChecks({ workspaceRoot: root, expectedStale })
  const summary = checkConsumerState(rows, {
    expectedVersion: '0.20.2',
    expectedCurrent: [],
    expectedStale,
    sourceChecks
  })

  t.absent(summary.ok)
  t.ok(summary.errors[0].includes('Missing expected source-level migration marker'))
})

test('ecosystem consumer audit verifies lockfile local package metadata', (t) => {
  const root = fixtureWorkspace()
  const deps = {
    'p2p-hiverelay': 'file:../../00-core/hiverelay/packages/core',
    'p2p-hiverelay-client': 'file:../../00-core/hiverelay/packages/client'
  }
  writePackage(root, '02-apps/pear-pos/package.json', {
    optionalDependencies: deps
  })
  writePackageLock(root, '02-apps/pear-pos/package-lock.json', {
    packages: {
      '': {
        optionalDependencies: deps
      },
      '../../00-core/hiverelay/packages/client': {
        name: 'p2p-hiverelay-client',
        version: '0.20.2',
        dependencies: {
          'p2p-hiverelay': '^0.20.2'
        }
      },
      '../../00-core/hiverelay/packages/core': {
        name: 'p2p-hiverelay',
        version: '0.20.2'
      },
      'node_modules/p2p-hiverelay': {
        resolved: '../../00-core/hiverelay/packages/core',
        link: true
      },
      'node_modules/p2p-hiverelay-client': {
        resolved: '../../00-core/hiverelay/packages/client',
        link: true
      }
    }
  })

  const rows = scanHiverelayConsumers({ workspaceRoot: root })
  const expectedCurrent = [{
    path: '02-apps/pear-pos/package.json',
    deps
  }]
  const lockChecks = scanCurrentConsumerLockChecks({
    workspaceRoot: root,
    expectedVersion: '0.20.2',
    expectedCurrent
  })
  const summary = checkConsumerState(rows, {
    expectedVersion: '0.20.2',
    expectedCurrent,
    expectedStale: [],
    lockChecks
  })

  t.ok(summary.ok)
  t.ok(formatConsumerReport(summary).includes('Lockfile migration checks:'))
})

test('ecosystem consumer audit rejects stale lockfile Hiverelay entries', (t) => {
  const root = fixtureWorkspace()
  const deps = {
    'p2p-hiverelay': 'file:../../00-core/hiverelay/packages/core',
    'p2p-hiverelay-client': 'file:../../00-core/hiverelay/packages/client'
  }
  writePackage(root, '02-apps/pear-pos/package.json', {
    optionalDependencies: deps
  })
  writePackageLock(root, '02-apps/pear-pos/package-lock.json', {
    packages: {
      '': {
        optionalDependencies: deps
      },
      '../../00-core/hiverelay': {
        name: 'p2p-hiverelay-monorepo',
        version: '0.16.3'
      },
      '../../00-core/hiverelay/packages/client': {
        name: 'p2p-hiverelay-client',
        version: '0.20.2',
        dependencies: {
          'p2p-hiverelay': '^0.16.3'
        }
      },
      '../../00-core/hiverelay/packages/core': {
        name: 'p2p-hiverelay',
        version: '0.20.2'
      }
    }
  })

  const rows = scanHiverelayConsumers({ workspaceRoot: root })
  const expectedCurrent = [{
    path: '02-apps/pear-pos/package.json',
    deps
  }]
  const lockChecks = scanCurrentConsumerLockChecks({
    workspaceRoot: root,
    expectedVersion: '0.20.2',
    expectedCurrent
  })
  const summary = checkConsumerState(rows, {
    expectedVersion: '0.20.2',
    expectedCurrent,
    expectedStale: [],
    lockChecks
  })

  t.absent(summary.ok)
  t.ok(summary.errors.some(error => error.includes('stale monorepo-root Hiverelay lock entry')))
  t.ok(summary.errors.some(error => error.includes('p2p-hiverelay') && error.includes('^0.16.3')))
})

test('ecosystem consumer audit finds monorepo package lockfiles', (t) => {
  const root = fixtureWorkspace()
  const deps = {
    'p2p-hiverelay': 'file:../../../../00-core/hiverelay/packages/core',
    'p2p-hiverelay-client': 'file:../../../../00-core/hiverelay/packages/client'
  }
  writePackage(root, '04-experiments/Opengit/packages/opengit-relay/package.json', {
    optionalDependencies: deps
  })
  writePackageLock(root, '04-experiments/Opengit/package-lock.json', {
    packages: {
      'packages/opengit-relay': {
        optionalDependencies: deps
      },
      '../../00-core/hiverelay/packages/client': {
        name: 'p2p-hiverelay-client',
        version: '0.20.2',
        dependencies: {
          'p2p-hiverelay': '^0.20.2'
        }
      },
      '../../00-core/hiverelay/packages/core': {
        name: 'p2p-hiverelay',
        version: '0.20.2'
      }
    }
  })

  const expectedCurrent = [{
    path: '04-experiments/Opengit/packages/opengit-relay/package.json',
    deps
  }]
  const lockChecks = scanCurrentConsumerLockChecks({
    workspaceRoot: root,
    expectedVersion: '0.20.2',
    expectedCurrent
  })

  t.ok(lockChecks.every(check => check.ok))
})

test('ecosystem sync updates app defaults and linked package lock metadata', (t) => {
  const root = fixtureWorkspace()
  const consumerPath = '02-apps/pearpaste/package.json'
  const deps = {
    'p2p-hiverelay': 'file:../../00-core/hiverelay/packages/core',
    'p2p-hiverelay-client': 'file:../../00-core/hiverelay/packages/client'
  }
  const expectedCurrent = [{
    path: consumerPath,
    deps
  }]

  writePackage(root, consumerPath, {
    optionalDependencies: {
      'p2p-hiverelay': '^0.16.3',
      'p2p-hiverelay-client': '^0.16.3'
    }
  })
  writePackageLock(root, '02-apps/pearpaste/package-lock.json', {
    packages: {
      '': {
        optionalDependencies: {
          'p2p-hiverelay': '^0.16.3',
          'p2p-hiverelay-client': '^0.16.3'
        }
      },
      '../../00-core/hiverelay/packages/client': {
        name: 'p2p-hiverelay-client',
        version: '0.16.3',
        dependencies: {
          'p2p-hiverelay': '^0.16.3'
        }
      },
      '../../00-core/hiverelay/packages/core': {
        name: 'p2p-hiverelay',
        version: '0.16.3'
      }
    }
  })

  const result = syncEcosystemConsumers({
    workspaceRoot: root,
    expectedVersion: '0.20.2',
    expectedCurrent,
    dependencyMode: 'local',
    snapshotChecks: false
  })

  t.ok(result.ok)
  t.ok(result.changes.some(change => change.includes('02-apps/pearpaste/package.json')))
  t.ok(result.changes.some(change => change.includes('version -> 0.20.2')))

  const pkg = readPackage(root, consumerPath)
  t.is(pkg.optionalDependencies['p2p-hiverelay'], deps['p2p-hiverelay'])
  t.is(pkg.optionalDependencies['p2p-hiverelay-client'], deps['p2p-hiverelay-client'])

  const lock = readPackage(root, '02-apps/pearpaste/package-lock.json')
  t.is(lock.packages[''].optionalDependencies['p2p-hiverelay'], deps['p2p-hiverelay'])
  t.is(lock.packages[''].optionalDependencies['p2p-hiverelay-client'], deps['p2p-hiverelay-client'])
  t.is(lock.packages['../../00-core/hiverelay/packages/core'].version, '0.20.2')
  t.is(lock.packages['../../00-core/hiverelay/packages/client'].version, '0.20.2')
  t.is(lock.packages['../../00-core/hiverelay/packages/client'].dependencies['p2p-hiverelay'], '^0.20.2')

  const check = syncEcosystemConsumers({
    workspaceRoot: root,
    expectedVersion: '0.20.2',
    expectedCurrent,
    dependencyMode: 'local',
    snapshotChecks: false,
    check: true
  })
  t.ok(check.ok)
  t.is(check.changes.length, 0)
})

test('ecosystem consumer helpers default published apps to npm latest', (t) => {
  const consumers = getExpectedCurrentConsumers()
  const pearpaste = consumers.find(consumer => consumer.path === '02-apps/pearpaste/package.json')
  const anongpt = consumers.find(consumer => consumer.path === '04-experiments/anongpt-native/package.json')
  const peerit = consumers.find(consumer => consumer.path === '02-apps/peerit/package.json')

  t.is(DEFAULT_DEPENDENCY_MODE, 'npm-latest')
  t.ok(pearpaste)
  t.is(pearpaste.dependencyMode, 'npm-latest')
  t.is(pearpaste.deps['p2p-hiverelay'], 'latest')
  t.is(pearpaste.deps['p2p-hiverelay-client'], 'latest')
  t.ok(pearpaste.sourceChecks.some(check => check.termTemplate === 'HiveRelay `{version}` packages through npm `latest` by default'))
  t.ok(pearpaste.sourceChecks.some(check => check.term === '"p2p-hiverelay": "latest"'))
  t.ok(pearpaste.sourceChecks.some(check => check.label === 'PearPaste customer test asserts npm latest Hiverelay defaults'))
  t.ok(anongpt)
  t.is(anongpt.dependencyMode, 'npm-latest')
  t.is(anongpt.deps['p2p-hiverelay'], 'latest')
  t.ok(anongpt.sourceChecks.some(check => check.label === 'anonGPT architecture documents HiveRelay relay/onion transport'))
  t.ok(anongpt.sourceChecks.some(check => check.label === 'anonGPT forward transport targets production HiveRelay forward service'))
  t.ok(anongpt.sourceChecks.some(check => check.label === 'anonGPT customer test asserts npm latest Hiverelay defaults'))
  t.ok(peerit)
  t.ok(peerit.sourceOnly)
  t.alike(peerit.deps, {})
  t.ok(peerit.sourceChecks.some(check => check.label === 'Peerit publish path loads the split HiveRelay client package'))

  const local = getExpectedCurrentConsumers({ dependencyMode: 'local' })
    .find(consumer => consumer.path === '02-apps/pearpaste/package.json')
  t.is(local.dependencyMode, 'local')
  t.ok(local.deps['p2p-hiverelay'].startsWith('file:'))
  t.ok(local.sourceChecks.some(check => check.termTemplate === 'HiveRelay `{version}` local workspace packages by default'))
  t.ok(local.sourceChecks.some(check => check.term === '"p2p-hiverelay": "file:../../00-core/hiverelay/packages/core"'))
})

test('ecosystem consumer release scope includes only package-default release repos', (t) => {
  const consumers = getExpectedCurrentConsumers({ consumerScope: 'release' })
  const paths = consumers.map(consumer => consumer.path).sort()

  t.alike(paths, [
    '01-browser/pearbrowser-desktop/package.json',
    '02-apps/pearpaste/package.json',
    '03-sites/pearbrowser-publishers/src/p2pbuilders/package.json',
    '04-experiments/Opengit/packages/opengit-relay/package.json',
    '04-experiments/anongpt-native/package.json'
  ].sort())
  t.ok(consumers.every(consumer => consumer.release?.repository))
  t.absent(paths.includes('02-apps/pear-pos/package.json'))
  t.absent(paths.includes('02-apps/pear-tickets/package.json'))
  t.absent(paths.includes('02-apps/peerit/package.json'))
  t.absent(paths.includes('04-experiments/hiverelay-test/package.json'))
})

test('ecosystem consumer release scope still classifies local-only app consumers', (t) => {
  const root = fixtureWorkspace()
  const releaseConsumers = getExpectedCurrentConsumers({ dependencyMode: 'local', consumerScope: 'release' })
  const allConsumers = getExpectedCurrentConsumers({ dependencyMode: 'local', consumerScope: 'all' })
  writeExpectedConsumerPackages(root, allConsumers)

  const rows = scanHiverelayConsumers({ workspaceRoot: root })
  const summary = checkConsumerState(rows, {
    expectedVersion: '0.20.2',
    expectedCurrent: releaseConsumers,
    expectedClassifiedCurrent: allConsumers,
    consumerScope: 'release',
    expectedStale: []
  })

  t.ok(summary.ok)
  t.is(summary.current.length, releaseConsumers.length)
  t.absent(summary.current.some(row => row.path === '02-apps/pear-pos/package.json'))
  t.ok(formatConsumerReport(summary).includes('scope release'))
})

test('ecosystem workspace check accepts all release-critical app consumers', (t) => {
  const root = fixtureWorkspace()
  writeExpectedConsumerPackages(root, EXPECTED_CURRENT_CONSUMERS)

  const result = checkEcosystemWorkspace({
    workspaceRoot: root,
    expectedCurrent: EXPECTED_CURRENT_CONSUMERS
  })

  t.ok(result.ok)
  t.is(result.present.length, EXPECTED_CURRENT_CONSUMERS.length)
  t.is(result.missing.length, 0)
})

test('ecosystem workspace check fails when full sibling workspace is absent', (t) => {
  const root = fixtureWorkspace()

  const result = checkEcosystemWorkspace({
    workspaceRoot: root,
    expectedCurrent: EXPECTED_CURRENT_CONSUMERS
  })

  t.absent(result.ok)
  t.is(result.present.length, 0)
  t.is(result.missing.length, EXPECTED_CURRENT_CONSUMERS.length)
  t.ok(result.errors.some(error => error.includes('full sibling workspace not found')))
})

test('ecosystem workspace check reports partial app workspaces', (t) => {
  const root = fixtureWorkspace()
  const [present, missing] = EXPECTED_CURRENT_CONSUMERS
  writeExpectedConsumerPackages(root, [present])

  const result = checkEcosystemWorkspace({
    workspaceRoot: root,
    expectedCurrent: [present, missing]
  })

  t.absent(result.ok)
  t.is(result.present.length, 1)
  t.is(result.missing.length, 1)
  t.ok(result.errors.some(error => error.includes(missing.path)))
})

test('ecosystem workspace check accepts release-managed consumer scope', (t) => {
  const root = fixtureWorkspace()
  const consumers = getExpectedCurrentConsumers({ dependencyMode: 'local', consumerScope: 'release' })
  writeExpectedConsumerPackages(root, consumers)

  const result = checkEcosystemWorkspace({
    workspaceRoot: root,
    consumerScope: 'release'
  })

  t.ok(result.ok)
  t.is(result.consumerScope, 'release')
  t.is(result.present.length, consumers.length)
})

test('ecosystem consumer commit helper commits changed release repos', (t) => {
  const root = fixtureWorkspace()
  const checkoutPath = '01-browser/pearbrowser-desktop'
  const repo = path.join(root, checkoutPath)
  fs.mkdirSync(repo, { recursive: true })
  git(repo, ['init'])
  git(repo, ['config', 'user.name', 'test'])
  git(repo, ['config', 'user.email', 'test@example.invalid'])
  writePackage(root, `${checkoutPath}/package.json`, {
    dependencies: {
      'p2p-hiverelay': 'latest'
    }
  })
  git(repo, ['add', '-A'])
  git(repo, ['commit', '-m', 'initial'])

  writePackage(root, `${checkoutPath}/package.json`, {
    dependencies: {
      'p2p-hiverelay': 'latest',
      'p2p-hiverelay-client': 'latest'
    }
  })

  const result = commitEcosystemConsumers({
    workspaceRoot: root,
    version: 'v0.20.2',
    push: false,
    expectedCurrent: [{
      path: `${checkoutPath}/package.json`,
      release: {
        repository: 'bigdestiny2/pearbrowser-desktop',
        ref: 'main',
        checkoutPath
      },
      deps: {
        'p2p-hiverelay': 'latest',
        'p2p-hiverelay-client': 'latest'
      }
    }]
  })

  t.ok(result.ok)
  t.is(result.rows[0].status, 'committed')
  t.ok(git(repo, ['status', '--porcelain']).stdout.trim() === '')
  t.ok(git(repo, ['log', '-1', '--pretty=%s']).stdout.includes('chore: sync HiveRelay defaults v0.20.2'))
})

test('ecosystem sync default npm-latest path refuses stale registry latest', (t) => {
  const root = fixtureWorkspace()
  const consumer = {
    ...getExpectedCurrentConsumers().find(consumer => consumer.path === '02-apps/pearpaste/package.json'),
    sourceChecks: []
  }

  const result = syncEcosystemConsumers({
    workspaceRoot: root,
    expectedVersion: '0.20.2',
    expectedCurrent: [consumer],
    npmLatestVersions: npmLatestVersions('0.20.2', {
      'p2p-hiverelay': '0.9.2'
    }),
    snapshotChecks: false
  })

  t.absent(result.ok)
  t.is(result.dependencyMode, 'npm-latest')
  t.ok(result.errors.some(error => error.includes('p2p-hiverelay npm latest dist-tag is 0.9.2; expected 0.20.2')))
  t.is(result.changes.length, 0, 'default npm latest mode blocks before writing app files')
})

test('ecosystem audit CLI defaults to npm-latest mode', (t) => {
  const root = fixtureWorkspace()
  const result = spawnSync(process.execPath, [
    'scripts/audit-ecosystem-consumers.mjs',
    '--workspace-root', root,
    '--expected-version', '0.20.2',
    '--consumer-scope', 'release',
    '--check'
  ], {
    cwd: process.cwd(),
    encoding: 'utf8',
    env: {
      ...process.env,
      HIVERELAY_NPM_LATEST_JSON: JSON.stringify(npmLatestVersions('0.20.2'))
    }
  })

  t.is(result.status, 1)
  t.ok(result.stdout.includes('mode npm-latest'), 'direct CLI uses the published-app default')
  t.ok(result.stdout.includes('Current npm-latest consumers:'))
  t.absent(result.stdout.includes('mode local'))
})

test('ecosystem sync default npm-latest path verifies the full package line', (t) => {
  const root = fixtureWorkspace()
  const consumer = {
    ...getExpectedCurrentConsumers().find(consumer => consumer.path === '02-apps/pearpaste/package.json'),
    sourceChecks: []
  }

  const result = syncEcosystemConsumers({
    workspaceRoot: root,
    expectedVersion: '0.20.2',
    expectedCurrent: [consumer],
    npmLatestVersions: npmLatestVersions('0.20.2', {
      'p2p-hiveservices': '0.9.2'
    }),
    snapshotChecks: false
  })

  t.absent(result.ok)
  t.ok(result.errors.some(error => error.includes('p2p-hiveservices npm latest dist-tag is 0.9.2; expected 0.20.2')))
  t.is(result.changes.length, 0, 'stale services package blocks app default writes even when the app does not list it directly')
})

test('ecosystem sync refuses npm-latest defaults when npm latest would downgrade', (t) => {
  const root = fixtureWorkspace()
  const consumer = getExpectedCurrentConsumers({ dependencyMode: 'npm-latest' })
    .find(consumer => consumer.path === '02-apps/pearpaste/package.json')
  writePackage(root, consumer.path, {
    optionalDependencies: {
      'p2p-hiverelay': 'file:../../00-core/hiverelay/packages/core',
      'p2p-hiverelay-client': 'file:../../00-core/hiverelay/packages/client'
    }
  })

  const result = syncEcosystemConsumers({
    workspaceRoot: root,
    expectedVersion: '0.20.2',
    expectedCurrent: [consumer],
    dependencyMode: 'npm-latest',
    npmLatestVersions: npmLatestVersions('0.20.2', {
      'p2p-hiverelay': '0.9.2'
    }),
    snapshotChecks: false
  })

  t.absent(result.ok)
  t.ok(result.errors.some(error => error.includes('p2p-hiverelay npm latest dist-tag is 0.9.2; expected 0.20.2')))
  t.ok(result.changes.some(change => change.includes('02-apps/pearpaste/package.json: p2p-hiverelay -> latest')))
  t.ok(result.warnings.some(warning => warning.includes('pending manifest/source changes are shown for release planning only')))
  const pkg = readPackage(root, consumer.path)
  t.is(pkg.optionalDependencies['p2p-hiverelay'], 'file:../../00-core/hiverelay/packages/core', 'stale npm latest still blocks writes')
  t.is(pkg.optionalDependencies['p2p-hiverelay-client'], 'file:../../00-core/hiverelay/packages/client')
})

test('ecosystem sync can prepare latest app defaults before npm latest is promoted', (t) => {
  const root = fixtureWorkspace()
  const consumer = {
    ...getExpectedCurrentConsumers({ dependencyMode: 'npm-latest' })
      .find(consumer => consumer.path === '02-apps/pearpaste/package.json'),
    sourceChecks: []
  }

  writePackage(root, consumer.path, {
    optionalDependencies: {
      'p2p-hiverelay': 'file:../../00-core/hiverelay/packages/core',
      'p2p-hiverelay-client': 'file:../../00-core/hiverelay/packages/client'
    }
  })
  writePackageLock(root, '02-apps/pearpaste/package-lock.json', {
    packages: {
      '': {
        optionalDependencies: {
          'p2p-hiverelay': 'file:../../00-core/hiverelay/packages/core',
          'p2p-hiverelay-client': 'file:../../00-core/hiverelay/packages/client'
        }
      },
      '../../00-core/hiverelay/packages/core': {
        name: 'p2p-hiverelay',
        version: '0.20.2'
      },
      '../../00-core/hiverelay/packages/client': {
        name: 'p2p-hiverelay-client',
        version: '0.20.2'
      }
    }
  })

  const result = syncEcosystemConsumers({
    workspaceRoot: root,
    expectedVersion: '0.20.2',
    expectedCurrent: [consumer],
    dependencyMode: 'npm-latest',
    npmLatestVersions: npmLatestVersions('0.20.2', {
      'p2p-hiverelay': '0.9.2'
    }),
    prepareLatestDefaults: true,
    snapshotChecks: false
  })

  t.ok(result.ok)
  t.ok(result.prepareLatestDefaults)
  t.ok(result.changes.some(change => change.includes('02-apps/pearpaste/package.json: p2p-hiverelay -> latest')))
  t.ok(result.warnings.some(warning => warning.includes('prepare-latest-defaults skipped registry proof')))
  t.ok(result.warnings.some(warning => warning.includes('without lockfile refresh')))

  const pkg = readPackage(root, consumer.path)
  const lock = readPackage(root, '02-apps/pearpaste/package-lock.json')
  t.is(pkg.optionalDependencies['p2p-hiverelay'], 'latest')
  t.is(pkg.optionalDependencies['p2p-hiverelay-client'], 'latest')
  t.is(lock.packages[''].optionalDependencies['p2p-hiverelay'], 'file:../../00-core/hiverelay/packages/core')
  t.is(lock.packages[''].optionalDependencies['p2p-hiverelay-client'], 'file:../../00-core/hiverelay/packages/client')
})

test('ecosystem prepare-latest check verifies staged defaults without writing', (t) => {
  const root = fixtureWorkspace()
  const consumer = {
    ...getExpectedCurrentConsumers({ dependencyMode: 'npm-latest' })
      .find(consumer => consumer.path === '02-apps/pearpaste/package.json'),
    sourceChecks: []
  }

  writePackage(root, consumer.path, {
    optionalDependencies: {
      'p2p-hiverelay': 'file:../../00-core/hiverelay/packages/core',
      'p2p-hiverelay-client': 'file:../../00-core/hiverelay/packages/client'
    }
  })
  writePackageLock(root, '02-apps/pearpaste/package-lock.json', {
    packages: {
      '': {
        optionalDependencies: {
          'p2p-hiverelay': 'file:../../00-core/hiverelay/packages/core',
          'p2p-hiverelay-client': 'file:../../00-core/hiverelay/packages/client'
        }
      }
    }
  })

  const pending = syncEcosystemConsumers({
    workspaceRoot: root,
    expectedVersion: '0.20.2',
    expectedCurrent: [consumer],
    dependencyMode: 'npm-latest',
    npmLatestVersions: npmLatestVersions('0.20.2', {
      'p2p-hiverelay': '0.9.2'
    }),
    prepareLatestDefaults: true,
    snapshotChecks: false,
    check: true
  })

  t.absent(pending.ok)
  t.ok(pending.check)
  t.ok(pending.prepareLatestDefaults)
  t.ok(pending.changes.some(change => change.includes('02-apps/pearpaste/package.json: p2p-hiverelay -> latest')))
  t.ok(pending.errors.some(error => error.includes('ecosystem consumer file(s) need default-version sync')))
  t.is(readPackage(root, consumer.path).optionalDependencies['p2p-hiverelay'], 'file:../../00-core/hiverelay/packages/core')

  writePackage(root, consumer.path, {
    optionalDependencies: consumer.deps
  })

  const ready = syncEcosystemConsumers({
    workspaceRoot: root,
    expectedVersion: '0.20.2',
    expectedCurrent: [consumer],
    dependencyMode: 'npm-latest',
    npmLatestVersions: npmLatestVersions('0.20.2', {
      'p2p-hiverelay': '0.9.2'
    }),
    prepareLatestDefaults: true,
    snapshotChecks: false,
    check: true
  })

  t.ok(ready.ok)
  t.ok(ready.check)
  t.is(ready.changes.length, 0)
  t.ok(ready.warnings.some(warning => warning.includes('prepare-latest-defaults skipped registry proof')))
})

test('ecosystem consumer audit accepts npm-latest manifests with current npm lock metadata', (t) => {
  const root = fixtureWorkspace()
  const consumer = {
    ...getExpectedCurrentConsumers({ dependencyMode: 'npm-latest' })
      .find(consumer => consumer.path === '02-apps/pearpaste/package.json'),
    sourceChecks: []
  }

  writePackage(root, consumer.path, {
    optionalDependencies: consumer.deps
  })
  writePackageLock(root, '02-apps/pearpaste/package-lock.json', {
    packages: {
      '': {
        optionalDependencies: consumer.deps
      },
      'node_modules/p2p-hiverelay': {
        version: '0.20.2',
        resolved: 'https://registry.npmjs.org/p2p-hiverelay/-/p2p-hiverelay-0.20.2.tgz',
        integrity: 'sha512-test'
      },
      'node_modules/p2p-hiverelay-client': {
        version: '0.20.2',
        resolved: 'https://registry.npmjs.org/p2p-hiverelay-client/-/p2p-hiverelay-client-0.20.2.tgz',
        integrity: 'sha512-test',
        dependencies: {
          'p2p-hiverelay': '^0.20.2'
        }
      }
    }
  })

  const rows = scanHiverelayConsumers({ workspaceRoot: root })
  const lockChecks = scanCurrentConsumerLockChecks({
    workspaceRoot: root,
    expectedVersion: '0.20.2',
    expectedCurrent: [consumer]
  })
  const summary = checkConsumerState(rows, {
    expectedVersion: '0.20.2',
    expectedCurrent: [consumer],
    expectedStale: [],
    lockChecks
  })
  const syncCheck = syncEcosystemConsumers({
    workspaceRoot: root,
    expectedVersion: '0.20.2',
    expectedCurrent: [consumer],
    dependencyMode: 'npm-latest',
    npmLatestVersions: npmLatestVersions('0.20.2'),
    snapshotChecks: false,
    check: true
  })

  t.ok(summary.ok)
  t.ok(lockChecks.every(check => check.ok))
  t.ok(syncCheck.ok)
  t.is(syncCheck.changes.length, 0)
  t.ok(syncCheck.warnings.some(warning => warning.includes('p2p-hiveservices')))

  const staleLatest = verifyNpmLatestDistTags({
    expectedCurrent: [consumer],
    expectedVersion: '0.20.2',
    npmLatestVersions: npmLatestVersions('0.20.2', {
      'p2p-hiveservices': '0.9.2'
    })
  })
  const latestSummary = checkConsumerState(rows, {
    expectedVersion: '0.20.2',
    expectedCurrent: [consumer],
    expectedStale: [],
    lockChecks,
    npmLatestChecks: staleLatest.checks,
    npmLatestWarnings: staleLatest.warnings
  })

  t.absent(latestSummary.ok)
  t.ok(latestSummary.errors.some(error => error.includes('p2p-hiveservices npm latest dist-tag is 0.9.2; expected 0.20.2')))
  t.ok(formatConsumerReport(latestSummary).includes('NPM latest dist-tag checks:'))
})

test('ecosystem sync updates versioned app source markers', (t) => {
  const root = fixtureWorkspace()
  const consumer = EXPECTED_CURRENT_CONSUMERS.find(consumer => consumer.path === '01-browser/pearbrowser-desktop/package.json')
  writeExpectedConsumerPackages(root, [consumer])
  writePackageLock(root, '01-browser/pearbrowser-desktop/package-lock.json', {
    packages: {
      '': {
        dependencies: consumer.deps
      },
      '../../00-core/hiverelay/packages/client': {
        name: 'p2p-hiverelay-client',
        version: '0.16.3',
        dependencies: {
          'p2p-hiverelay': '^0.16.3'
        }
      },
      '../../00-core/hiverelay/packages/core': {
        name: 'p2p-hiverelay',
        version: '0.16.3'
      },
      '../../00-core/hiverelay/packages/verifier': {
        name: 'p2p-hiverelay-verifier',
        version: '0.16.3'
      }
    }
  })
  writeSourceCheckTerms(root, consumer, '0.16.3')
  writeFile(
    root,
    '01-browser/pearbrowser-desktop/docs/HIVERELAY-BACKBONE-HANDOVER.md',
    '- Relay core: `p2p-hiverelay` (npm v0.8.12) — `core/index.js`\n'
  )

  const result = syncEcosystemConsumers({
    workspaceRoot: root,
    expectedVersion: '0.20.2',
    expectedCurrent: [consumer],
    dependencyMode: 'local',
    snapshotChecks: false
  })

  t.ok(result.ok)
  t.ok(result.changes.some(change => change.includes('catalog-source/pearbrowser-network.catalog.json')))
  t.ok(result.changes.some(change => change.includes('backend/catalogue-seed.js')))
  t.ok(result.changes.some(change => change.includes('docs/HIVERELAY-BACKBONE-HANDOVER.md')))
  t.ok(fs.readFileSync(path.join(root, '01-browser/pearbrowser-desktop/catalog-source/pearbrowser-network.catalog.json'), 'utf8').includes('"version": "0.20.2"'))
  t.ok(fs.readFileSync(path.join(root, '01-browser/pearbrowser-desktop/backend/catalogue-seed.js'), 'utf8').includes('"version": "0.20.2"'))
  const handover = fs.readFileSync(path.join(root, '01-browser/pearbrowser-desktop/docs/HIVERELAY-BACKBONE-HANDOVER.md'), 'utf8')
  t.ok(handover.includes('`p2p-hiverelay` `0.20.2`'))
  t.absent(handover.includes('npm v0.8.12'))

  const sourceChecks = scanConsumerSourceChecks({
    workspaceRoot: root,
    expectedVersion: '0.20.2',
    expectedCurrent: [consumer],
    expectedStale: []
  })
  t.ok(sourceChecks.every(check => check.present))
  t.ok(sourceChecks.every(check => check.rejectedTermsFound.length === 0))
})

test('ecosystem consumer audit guards local release snapshot defaults', (t) => {
  const root = fixtureWorkspace()
  writeSnapshot(root, '00-core/hr-fleet', '0.20.2')

  const snapshotRoots = [{
    path: '00-core/hr-fleet',
    role: 'fleet release snapshot'
  }]
  const snapshotChecks = scanSnapshotVersionChecks({
    workspaceRoot: root,
    expectedVersion: '0.20.2',
    snapshotRoots
  })
  const summary = checkConsumerState([], {
    expectedVersion: '0.20.2',
    expectedCurrent: [],
    expectedStale: [],
    snapshotChecks
  })

  t.ok(summary.ok)
  t.ok(formatConsumerReport(summary).includes('Snapshot/default version checks:'))

  writePackage(root, '00-core/hr-fleet/packages/client/package.json', {
    name: 'p2p-hiverelay-client',
    version: '0.20.2',
    dependencies: {
      'p2p-hiverelay': '^0.20.0'
    }
  })
  const failedChecks = scanSnapshotVersionChecks({
    workspaceRoot: root,
    expectedVersion: '0.20.2',
    snapshotRoots
  })
  const failedSummary = checkConsumerState([], {
    expectedVersion: '0.20.2',
    expectedCurrent: [],
    expectedStale: [],
    snapshotChecks: failedChecks
  })

  t.absent(failedSummary.ok)
  t.ok(failedSummary.errors.some(error => error.includes('p2p-hiverelay') && error.includes('^0.20.2')))
})

function fixtureWorkspace () {
  return fs.mkdtempSync(path.join(os.tmpdir(), 'hiverelay-ecosystem-consumers-'))
}

function writePackage (root, relPath, body) {
  const file = path.join(root, relPath)
  fs.mkdirSync(path.dirname(file), { recursive: true })
  fs.writeFileSync(file, JSON.stringify({ name: path.basename(path.dirname(file)), ...body }, null, 2))
}

function writeExpectedConsumerPackages (root, consumers) {
  for (const consumer of consumers) {
    writePackage(root, consumer.path, {
      dependencies: consumer.deps
    })
  }
}

function writeFile (root, relPath, body) {
  const file = path.join(root, relPath)
  fs.mkdirSync(path.dirname(file), { recursive: true })
  fs.writeFileSync(file, body)
}

function writeSourceCheckTerms (root, consumer, version) {
  for (const spec of consumer.sourceChecks || []) {
    const term = typeof spec.termTemplate === 'string'
      ? spec.termTemplate.replaceAll('{version}', version)
      : spec.term
    writeFile(root, spec.file, `${term}\n`)
  }
}

function writePackageLock (root, relPath, body) {
  const file = path.join(root, relPath)
  fs.mkdirSync(path.dirname(file), { recursive: true })
  fs.writeFileSync(file, JSON.stringify({
    name: path.basename(path.dirname(file)),
    version: '0.0.0',
    lockfileVersion: 3,
    requires: true,
    ...body
  }, null, 2))
}

function readPackage (root, relPath) {
  return JSON.parse(fs.readFileSync(path.join(root, relPath), 'utf8'))
}

function git (cwd, argv) {
  const result = spawnSync('git', argv, {
    cwd,
    encoding: 'utf8'
  })
  if (result.status !== 0) {
    throw new Error(result.stderr || result.stdout || `git ${argv.join(' ')} failed`)
  }
  return result
}

function writeSnapshot (root, snapshotRoot, version) {
  const coreRange = `^${version}`
  writePackage(root, `${snapshotRoot}/package.json`, {
    name: 'p2p-hiverelay-monorepo',
    version
  })
  writePackage(root, `${snapshotRoot}/packages/core/package.json`, {
    name: 'p2p-hiverelay',
    version
  })
  writePackage(root, `${snapshotRoot}/packages/client/package.json`, {
    name: 'p2p-hiverelay-client',
    version,
    dependencies: {
      'p2p-hiverelay': coreRange
    }
  })
  writePackage(root, `${snapshotRoot}/packages/services/package.json`, {
    name: 'p2p-hiveservices',
    version,
    dependencies: {
      'p2p-hiverelay': coreRange
    }
  })
  writePackage(root, `${snapshotRoot}/packages/verifier/package.json`, {
    name: 'p2p-hiverelay-verifier',
    version
  })
  writePackageLock(root, `${snapshotRoot}/package-lock.json`, {
    name: 'p2p-hiverelay-monorepo',
    version,
    packages: {
      '': {
        name: 'p2p-hiverelay-monorepo',
        version
      },
      'packages/client': {
        name: 'p2p-hiverelay-client',
        version,
        dependencies: {
          'p2p-hiverelay': coreRange
        }
      },
      'packages/core': {
        name: 'p2p-hiverelay',
        version
      },
      'packages/services': {
        name: 'p2p-hiveservices',
        version,
        dependencies: {
          'p2p-hiverelay': coreRange
        }
      },
      'packages/verifier': {
        name: 'p2p-hiverelay-verifier',
        version
      }
    }
  })
}