OverviewFilesCommitsIssuesDocsReleasesPeers

P2P-Hiverelay / scripts / plan-blind-capacity.mjs

844 lines · javascript ref:
#!/usr/bin/env node

import { createHash } from 'node:crypto'
import { readFile, writeFile } from 'node:fs/promises'
import { resolve } from 'node:path'
import { fileURLToPath } from 'node:url'
import {
  BLIND_SCENARIO_MANIFEST_SCHEMA,
  CAPACITY_LAB_CONFIG_SCHEMA,
  normalizeCapacityLabConfig,
  runCapacityLab
} from './blind-capacity-lab.mjs'
import {
  runBlindFleetSimulation,
  stableStringify,
  verifyBlindFleetEvidenceDigest
} from './simulate-blind-fleet.mjs'
import {
  BLIND_RELEASE_LAB_PROFILES,
  buildFleetAlignedCapacityConfig
} from './run-blind-release-lab.mjs'

export const CAPACITY_SIZING_PLAN_SCHEMA = 'hiverelay/blind-capacity-sizing-plan/v1'
export const CAPACITY_SIZING_SEARCH_SCHEMA = 'hiverelay/blind-capacity-sizing-search/v1'

const SCALE_KEYS = Object.freeze([
  'storageBytes',
  'diskWrite',
  'diskRead',
  'diskIops',
  'walFsyncThroughput',
  'networkIngress',
  'networkEgress',
  'cpu',
  'streamBuffer',
  'streamSlots',
  'inboxWaiterMemory',
  'inboxWaiterSlots'
])

const BASELINE_SCALES = Object.freeze(Object.fromEntries(SCALE_KEYS.map(key => [key, 1])))

function scaledProfile (id, label, performanceScale, storageScale = performanceScale) {
  return Object.freeze({
    id,
    label,
    scales: Object.freeze({
      storageBytes: storageScale,
      diskWrite: performanceScale,
      diskRead: performanceScale,
      diskIops: performanceScale,
      walFsyncThroughput: performanceScale,
      networkIngress: performanceScale,
      networkEgress: performanceScale,
      cpu: performanceScale,
      streamBuffer: performanceScale,
      streamSlots: performanceScale,
      inboxWaiterMemory: performanceScale,
      inboxWaiterSlots: performanceScale
    })
  })
}

export const DEFAULT_CAPACITY_SIZING_SEARCH = deepFreeze({
  schema: CAPACITY_SIZING_SEARCH_SCHEMA,
  relayCounts: [24, 48, 72, 96, 144],
  hardwareProfiles: [
    { id: 'source-envelope', label: 'Source model envelope', scales: BASELINE_SCALES },
    {
      id: 'storage-64x',
      label: 'Storage-only 64x target',
      scales: { ...BASELINE_SCALES, storageBytes: 64 }
    },
    scaledProfile('balanced-8x-storage-64x', 'Balanced 8x target with 64x storage', 8, 64),
    scaledProfile('balanced-16x-storage-64x', 'Balanced 16x target with 64x storage', 16, 64),
    scaledProfile('balanced-32x-storage-64x', 'Balanced 32x target with 64x storage', 32, 64),
    scaledProfile('balanced-64x-storage-64x', 'Balanced 64x target with 64x storage', 64, 64),
    scaledProfile('balanced-128x', 'Balanced 128x target', 128)
  ],
  minimumModeledLogicalObjects: 0,
  minimumModeledLogicalPayloadBytes: 0
})

export class CapacitySizingError extends Error {
  constructor (violations) {
    super(`invalid blind capacity sizing input: ${violations.join('; ')}`)
    this.name = 'CapacitySizingError'
    this.code = 'INVALID_CAPACITY_SIZING_INPUT'
    this.violations = violations
  }
}

/**
 * Construct the externally-bound input used by the CLI. The source is the exact
 * fleet-to-capacity mapping already owned by the release lab. It remains
 * deterministic model evidence; neither the fleet nor the target hardware is
 * represented as observed production hardware.
 */
export function buildReleaseCapacitySizingInput (profileName = 'release', search = {}) {
  const profile = BLIND_RELEASE_LAB_PROFILES[profileName]
  if (!profile) throw new CapacitySizingError([`unknown release profile ${profileName}`])
  const fleet = runBlindFleetSimulation(profile.fleet)
  if (!verifyBlindFleetEvidenceDigest(fleet)) {
    throw new CapacitySizingError(['source fleet evidence checksum does not verify'])
  }
  const capacityConfig = normalizeCapacityLabConfig(buildFleetAlignedCapacityConfig(fleet))
  return {
    capacityConfig,
    sourceAuthority: {
      kind: fleet.kind,
      schemaVersion: fleet.schemaVersion,
      scenarioSchema: fleet.scenarioManifest.schema,
      scenarioDigest: fleet.scenarioDigest,
      evidenceDigest: fleet.evidenceDigest,
      capacityConfigDigest: sha256(stableStringify(capacityConfig)),
      sourceProfile: profileName,
      evidenceClass: 'deterministic-simulation-not-observed',
      authenticityProven: false
    },
    search
  }
}

export function runCapacitySizingPlan (input) {
  const normalized = normalizeSizingInput(input)
  const source = normalized.capacityConfig
  const authority = buildWorkloadAuthority(source, normalized.sourceAuthority)
  const sourceBaseline = runCapacityLab(source)
  const candidates = []

  for (let relayIndex = 0; relayIndex < normalized.search.relayCounts.length; relayIndex++) {
    const relayCount = normalized.search.relayCounts[relayIndex]
    for (let profileIndex = 0; profileIndex < normalized.search.hardwareProfiles.length; profileIndex++) {
      const profile = normalized.search.hardwareProfiles[profileIndex]
      const config = buildCandidateConfig(source, relayCount, profile)
      const capacity = runCapacityLab(config)
      candidates.push(buildCandidate({
        capacity,
        config,
        authority,
        relayCount,
        relayIndex,
        profile,
        profileIndex,
        search: normalized.search
      }))
    }
  }

  const answers = buildSizingAnswers(candidates, normalized.search)
  const transitions = buildBottleneckTransitions(candidates, normalized.search)
  const sourceRelayCount = source.fleet.relayCount
  const conservativeSourceMinCandidate = candidates.find(candidate =>
    candidate.relayCount === sourceRelayCount && candidate.profileIndex === 0)
  if (!conservativeSourceMinCandidate) throw new CapacitySizingError(['internal source-envelope candidate is missing'])

  const body = {
    schema: CAPACITY_SIZING_PLAN_SCHEMA,
    status: 'complete',
    planningStatus: answers.modeledTargetFoundInSearch
      ? 'modeled-fit-found-in-enumerated-search'
      : 'no-modeled-fit-in-enumerated-search',
    evidenceClass: 'modeled-target-hardware-not-observed',
    claimBoundary: 'This deterministic sizing sweep compares enumerated target assumptions. It is not a hardware benchmark, observation of any relay, production capacity evidence, release authorization, or a universal minimum-hardware proof.',
    authority,
    search: normalized.search,
    searchDigest: sha256(stableStringify(normalized.search)),
    sourceRelayCountInjectedIntoSearch: normalized.sourceRelayCountInjectedIntoSearch,
    baselineReference: {
      sourceCapacityConfigDigest: sourceBaseline.configDigest,
      sourceCapacityScenarioDigest: sourceBaseline.scenarioDigest,
      relayCount: source.fleet.relayCount,
      storageBytesByRelay: sourceBaseline.scenarioManifest.storageBytesByRelay,
      capacityModelStatus: sourceBaseline.status,
      modeledSustainableLogicalOpsPerSecond: sourceBaseline.throughput.modeledSustainableLogicalOpsPerSecond,
      bottleneck: sourceBaseline.throughput.bottleneck,
      conservativeHomogeneousSourceMinimumCandidateId: conservativeSourceMinCandidate.id,
      releaseGateStatus: 'unchanged-not-evaluated'
    },
    releaseAuthority: {
      authorizesRelease: false,
      changesBaselineReleaseGate: false,
      productionHardwareObserved: false,
      signedByOperator: false,
      instruction: 'Treat a modeled fit as a procurement/benchmark target only. The existing composite release and mainnet gates must consume separately verified observed evidence.'
    },
    answers,
    bottleneckAnalysis: transitions,
    candidates,
    limitations: [
      'Minimum means the first fit in the explicitly ordered, component-wise nondecreasing search lattice; it is not an optimizer over every possible hardware combination.',
      'Every candidate preserves the exact source workload, operation weights, offered logical rate, quorum and durability contract. Only relay count and enumerated per-relay resources change.',
      'Target resource values are assumptions. Operators must benchmark the exact storage engine, filesystem, kernel, transport, host contention and network path.',
      'The analytical model treats relay performance as homogeneous and reports the worst modeled relay; independent failure domains, correlated outages and adversarial routing require the fleet simulation and multi-host drills.',
      'Content capacity is the configured retained CELL mix. INBOX and CORE dedicated projections remain mutually exclusive surfaces in the underlying capacity report.',
      'A report checksum detects byte drift only when the expected checksum is trusted. Authenticity requires a separate verified signature and trust root.'
    ]
  }
  return { ...body, reportDigest: sha256(stableStringify(body)) }
}

export function verifyCapacitySizingPlanDigest (report) {
  if (!isPlainObject(report) || typeof report.reportDigest !== 'string') return false
  const { reportDigest, ...body } = report
  return reportDigest === sha256(stableStringify(body))
}

/**
 * Owning verification requires the expected input from outside the report and
 * reruns the complete deterministic search. Rehashing fields taken from the
 * report itself would be a self-sealing checksum, not an authority check.
 */
export function verifyCapacitySizingPlan (report, expectedInput) {
  const checks = []
  checks.push(check('REPORT_DIGEST', verifyCapacitySizingPlanDigest(report), report?.reportDigest, 'digest(recomputed report body)'))
  let expected = null
  try {
    expected = runCapacitySizingPlan(expectedInput)
    checks.push(check('EXTERNAL_SOURCE_SCENARIO',
      report?.authority?.source?.scenarioDigest === expected.authority.source.scenarioDigest,
      report?.authority?.source?.scenarioDigest,
      expected.authority.source.scenarioDigest))
    checks.push(check('EXACT_WORKLOAD_AUTHORITY',
      report?.authority?.workloadDigest === expected.authority.workloadDigest,
      report?.authority?.workloadDigest,
      expected.authority.workloadDigest))
    checks.push(check('EXACT_DETERMINISTIC_PLAN', stableStringify(report) === stableStringify(expected),
      report?.reportDigest, expected.reportDigest))
  } catch (error) {
    checks.push(check('EXPECTED_INPUT_VALID', false, error.message, 'valid externally supplied sizing input'))
  }
  const blockers = checks.filter(item => !item.passed).map(item => item.id)
  return {
    evidenceClass: 'externally-bound-deterministic-rerun',
    authenticReleaseEvidence: false,
    authorizesRelease: false,
    passed: blockers.length === 0,
    blockers,
    checks
  }
}

function normalizeSizingInput (input) {
  const violations = []
  if (!isPlainObject(input)) throw new CapacitySizingError(['input must be an object'])
  rejectUnknownFields(input, ['capacityConfig', 'sourceAuthority', 'search'], 'input', violations)
  let capacityConfig = null
  try {
    capacityConfig = normalizeCapacityLabConfig(input.capacityConfig)
  } catch (error) {
    violations.push(error.message)
  }
  validateSourceAuthority(input.sourceAuthority, violations)
  const search = normalizeSearch(input.search, capacityConfig, violations)
  if (capacityConfig?.schema !== CAPACITY_LAB_CONFIG_SCHEMA) {
    violations.push(`capacityConfig.schema must be ${CAPACITY_LAB_CONFIG_SCHEMA}`)
  }
  if (!(capacityConfig?.workload?.offeredLogicalOpsPerSecond > 0)) {
    violations.push('capacityConfig.workload.offeredLogicalOpsPerSecond must be explicit and positive')
  }
  if (capacityConfig && input.sourceAuthority?.capacityConfigDigest !== sha256(stableStringify(capacityConfig))) {
    violations.push('sourceAuthority.capacityConfigDigest must bind the exact normalized capacityConfig')
  }
  if (violations.length) throw new CapacitySizingError(violations)
  const requestedRelayCounts = Array.isArray(input.search?.relayCounts)
    ? input.search.relayCounts
    : DEFAULT_CAPACITY_SIZING_SEARCH.relayCounts
  const injected = !requestedRelayCounts.includes(capacityConfig.fleet.relayCount)
  return {
    capacityConfig,
    sourceAuthority: clone(input.sourceAuthority),
    search,
    sourceRelayCountInjectedIntoSearch: injected
  }
}

function normalizeSearch (input, capacityConfig, violations) {
  if (input !== undefined && !isPlainObject(input)) {
    violations.push('search must be an object')
    input = {}
  }
  const supplied = input || {}
  rejectUnknownFields(supplied, [
    'schema',
    'relayCounts',
    'hardwareProfiles',
    'minimumModeledLogicalObjects',
    'minimumModeledLogicalPayloadBytes'
  ], 'search', violations)
  const search = deepMerge(DEFAULT_CAPACITY_SIZING_SEARCH, supplied)
  if (search.schema !== CAPACITY_SIZING_SEARCH_SCHEMA) {
    violations.push(`search.schema must be ${CAPACITY_SIZING_SEARCH_SCHEMA}`)
  }

  if (!Array.isArray(search.relayCounts) || search.relayCounts.length === 0) {
    violations.push('search.relayCounts must be a non-empty array')
  } else {
    const minimum = Math.max(
      capacityConfig?.fleet?.replicationFactor || 1,
      capacityConfig?.workload?.readFanout || 1,
      capacityConfig?.workload?.writeAcks || 1
    )
    for (let i = 0; i < search.relayCounts.length; i++) {
      integer(search.relayCounts[i], `search.relayCounts[${i}]`, minimum, 10_000, violations)
      if (i > 0 && search.relayCounts[i] <= search.relayCounts[i - 1]) {
        violations.push('search.relayCounts must be strictly increasing')
      }
    }
  }

  const sourceRelayCount = capacityConfig?.fleet?.relayCount
  if (Number.isSafeInteger(sourceRelayCount) && Array.isArray(search.relayCounts)) {
    search.relayCounts = [...new Set([...search.relayCounts, sourceRelayCount])].sort((a, b) => a - b)
  }
  validateHardwareProfiles(search.hardwareProfiles, violations)
  nonNegativeInteger(search.minimumModeledLogicalObjects, 'search.minimumModeledLogicalObjects', violations)
  nonNegativeInteger(search.minimumModeledLogicalPayloadBytes, 'search.minimumModeledLogicalPayloadBytes', violations)
  return search
}

function validateSourceAuthority (authority, violations) {
  if (!isPlainObject(authority)) {
    violations.push('sourceAuthority must be an object')
    return
  }
  rejectUnknownFields(authority, [
    'kind',
    'schemaVersion',
    'scenarioSchema',
    'scenarioDigest',
    'evidenceDigest',
    'capacityConfigDigest',
    'sourceProfile',
    'evidenceClass',
    'authenticityProven'
  ], 'sourceAuthority', violations)
  for (const field of ['kind', 'scenarioSchema', 'sourceProfile']) {
    if (typeof authority[field] !== 'string' || authority[field].length === 0) {
      violations.push(`sourceAuthority.${field} must be a non-empty string`)
    }
  }
  integer(authority.schemaVersion, 'sourceAuthority.schemaVersion', 1, 1000, violations)
  if (authority.kind !== 'hiverelay-blind-fleet-simulation-evidence') {
    violations.push('sourceAuthority.kind must be hiverelay-blind-fleet-simulation-evidence')
  }
  if (authority.schemaVersion !== 3) {
    violations.push('sourceAuthority.schemaVersion must be the current fleet evidence schema 3')
  }
  if (authority.scenarioSchema !== BLIND_SCENARIO_MANIFEST_SCHEMA) {
    violations.push(`sourceAuthority.scenarioSchema must be ${BLIND_SCENARIO_MANIFEST_SCHEMA}`)
  }
  for (const field of ['scenarioDigest', 'evidenceDigest', 'capacityConfigDigest']) {
    if (typeof authority[field] !== 'string' || !/^[0-9a-f]{64}$/.test(authority[field])) {
      violations.push(`sourceAuthority.${field} must be a lowercase SHA-256 digest`)
    }
  }
  if (authority.evidenceClass !== 'deterministic-simulation-not-observed') {
    violations.push('sourceAuthority.evidenceClass must be deterministic-simulation-not-observed')
  }
  if (authority.authenticityProven !== false) {
    violations.push('sourceAuthority.authenticityProven must be false for the modeled source')
  }
}

function validateHardwareProfiles (profiles, violations) {
  if (!Array.isArray(profiles) || profiles.length === 0) {
    violations.push('search.hardwareProfiles must be a non-empty array')
    return
  }
  const ids = new Set()
  let previous = null
  for (let index = 0; index < profiles.length; index++) {
    const profile = profiles[index]
    const path = `search.hardwareProfiles[${index}]`
    if (!isPlainObject(profile)) {
      violations.push(`${path} must be an object`)
      continue
    }
    rejectUnknownFields(profile, ['id', 'label', 'scales'], path, violations)
    if (typeof profile.id !== 'string' || !/^[a-z0-9][a-z0-9-]{0,63}$/.test(profile.id) || ids.has(profile.id)) {
      violations.push(`${path}.id must be a unique lowercase slug`)
    } else ids.add(profile.id)
    if (typeof profile.label !== 'string' || profile.label.length === 0 || profile.label.length > 120) {
      violations.push(`${path}.label must be a non-empty string no longer than 120 characters`)
    }
    if (!isPlainObject(profile.scales)) {
      violations.push(`${path}.scales must be an object`)
      continue
    }
    rejectUnknownFields(profile.scales, SCALE_KEYS, `${path}.scales`, violations)
    for (const key of SCALE_KEYS) {
      positive(profile.scales[key], `${path}.scales.${key}`, violations)
      if (index === 0 && profile.scales[key] !== 1) {
        violations.push(`the first hardware profile must be the unmodified source envelope (${key}=1)`)
      }
      if (previous && Number.isFinite(profile.scales[key]) && profile.scales[key] < previous[key]) {
        violations.push(`${path}.scales.${key} must not decrease from the previous profile`)
      }
    }
    previous = profile.scales
  }
}

function buildWorkloadAuthority (config, sourceAuthority) {
  const workload = clone(config.workload)
  const operationMix = workload.operations.map(operation => clone(operation))
  const durability = {
    replicationFactor: config.fleet.replicationFactor,
    readFanout: workload.readFanout,
    readQuorum: workload.readQuorum,
    writeAcks: workload.writeAcks
  }
  const storageSemantics = {
    objects: clone(config.objects),
    checkpoint: clone(config.checkpoint),
    repair: clone(config.repair),
    walFormatAllowances: {
      recordOverheadBytes: config.wal.recordOverheadBytes,
      alignmentBytes: config.wal.alignmentBytes,
      effectiveGroupCommitRecords: config.wal.effectiveGroupCommitRecords,
      reserveFractionOfDisk: config.wal.reserveFractionOfDisk,
      retentionSeconds: config.wal.retentionSeconds
    }
  }
  return {
    source: clone(sourceAuthority),
    capacityConfigSchema: config.schema,
    sourceCapacityConfigDigest: sha256(stableStringify(config)),
    offeredLogicalOpsPerSecond: workload.offeredLogicalOpsPerSecond,
    operationKinds: operationMix.map(operation => `${operation.family}:${operation.operation}`),
    operationMixDigest: sha256(stableStringify(operationMix)),
    workloadDigest: sha256(stableStringify(workload)),
    durability,
    durabilityDigest: sha256(stableStringify(durability)),
    storageSemanticsDigest: sha256(stableStringify(storageSemantics)),
    binding: 'Every candidate is regenerated from this normalized capacity config and must preserve the exact workload and durability digests.'
  }
}

function buildCandidateConfig (source, relayCount, profile) {
  const baseDiskBytes = source.fleet.diskBytesByRelay === null
    ? source.fleet.diskBytesPerRelay
    : Math.min(...source.fleet.diskBytesByRelay)
  const scales = profile.scales
  const diskBytesPerRelay = Math.ceil(baseDiskBytes * scales.storageBytes)
  const aggregateDiskBytes = diskBytesPerRelay * relayCount
  if (!Number.isSafeInteger(diskBytesPerRelay) || !Number.isSafeInteger(aggregateDiskBytes)) {
    throw new CapacitySizingError([`candidate ${relayCount}:${profile.id} disk sizing exceeds safe integer report bounds`])
  }
  return normalizeCapacityLabConfig(deepMerge(source, {
    seed: `${source.seed}:sizing:${relayCount}:${profile.id}`,
    simulation: {
      sampleObjects: Math.max(source.simulation.sampleObjects, relayCount * 100),
      scaleRelayCounts: []
    },
    fleet: {
      relayCount,
      diskBytesPerRelay,
      diskBytesByRelay: null,
      unavailableRelays: Math.min(source.fleet.unavailableRelays, relayCount - 1)
    },
    disk: {
      sequentialWriteBytesPerSecond: source.disk.sequentialWriteBytesPerSecond * scales.diskWrite,
      sequentialReadBytesPerSecond: source.disk.sequentialReadBytesPerSecond * scales.diskRead,
      randomIopsPerSecond: source.disk.randomIopsPerSecond * scales.diskIops
    },
    wal: {
      fsyncLatencyMs: source.wal.fsyncLatencyMs / scales.walFsyncThroughput
    },
    network: {
      ingressBitsPerSecond: source.network.ingressBitsPerSecond * scales.networkIngress,
      egressBitsPerSecond: source.network.egressBitsPerSecond * scales.networkEgress
    },
    cpu: {
      coresPerRelay: source.cpu.coresPerRelay * scales.cpu
    },
    streams: {
      maxBufferedBytesPerRelay: Math.ceil(source.streams.maxBufferedBytesPerRelay * scales.streamBuffer),
      maxConcurrentStreamsPerRelay: Math.ceil(source.streams.maxConcurrentStreamsPerRelay * scales.streamSlots)
    },
    surfaces: {
      inboxWaiterMemoryBytesPerRelay: Math.ceil(source.surfaces.inboxWaiterMemoryBytesPerRelay * scales.inboxWaiterMemory),
      inboxMaxWaitersPerRelay: Math.ceil(source.surfaces.inboxMaxWaitersPerRelay * scales.inboxWaiterSlots)
    }
  }))
}

function buildCandidate (context) {
  const { capacity, config, authority, relayCount, relayIndex, profile, profileIndex, search } = context
  const sustainable = capacity.throughput.modeledSustainableLogicalOpsPerSecond
  const target = authority.offeredLogicalOpsPerSecond
  const contentChecks = [
    {
      id: 'minimum-modeled-logical-objects',
      basis: 'planned-fill-retained-cell-mix',
      passed: capacity.storage.plannedLogicalObjects >= search.minimumModeledLogicalObjects,
      observed: capacity.storage.plannedLogicalObjects,
      required: search.minimumModeledLogicalObjects
    },
    {
      id: 'minimum-modeled-logical-payload-bytes',
      basis: 'planned-fill-retained-cell-mix',
      passed: capacity.storage.plannedLogicalPayloadBytes >= search.minimumModeledLogicalPayloadBytes,
      observed: capacity.storage.plannedLogicalPayloadBytes,
      required: search.minimumModeledLogicalPayloadBytes
    }
  ]
  const planningFit = capacity.status === 'pass' && contentChecks.every(item => item.passed)
  const orderedResources = [...capacity.throughput.resources]
    .filter(resource => resource.modeledLogicalOpsPerSecondLimit !== null)
    .sort((left, right) => left.modeledLogicalOpsPerSecondLimit - right.modeledLogicalOpsPerSecondLimit ||
      left.resource.localeCompare(right.resource))
  const constraints = orderedResources.slice(0, 4).map(resource => ({
    resource: resource.resource,
    modeledLogicalOpsPerSecondLimit: resource.modeledLogicalOpsPerSecondLimit,
    modeledUtilizationAtTarget: resource.modeledUtilizationAtOfferedLoad,
    targetShortfallFraction: fixed(Math.max(0, target / resource.modeledLogicalOpsPerSecondLimit - 1))
  }))
  const workloadDigest = sha256(stableStringify(config.workload))
  const durability = {
    replicationFactor: config.fleet.replicationFactor,
    readFanout: config.workload.readFanout,
    readQuorum: config.workload.readQuorum,
    writeAcks: config.workload.writeAcks
  }
  const durabilityDigest = sha256(stableStringify(durability))
  const operationMixDigest = sha256(stableStringify(config.workload.operations))
  if (workloadDigest !== authority.workloadDigest ||
      durabilityDigest !== authority.durabilityDigest ||
      operationMixDigest !== authority.operationMixDigest) {
    throw new CapacitySizingError([`candidate ${relayCount}:${profile.id} drifted from the exact workload authority`])
  }
  return {
    id: `r${relayCount}-${profile.id}`,
    relayCount,
    relayIndex,
    hardwareProfileId: profile.id,
    hardwareProfileLabel: profile.label,
    profileIndex,
    hardware: resolvedHardware(config),
    hardwareEvidenceClass: 'enumerated-modeled-target-not-observed',
    targetLogicalOpsPerSecond: target,
    capacityModelStatus: capacity.status,
    planningFit,
    authorizesRelease: false,
    modeledSustainableLogicalOpsPerSecond: sustainable,
    modeledHeadroomFractionAtTarget: fixed(sustainable / target - 1),
    bottleneck: capacity.throughput.bottleneck,
    nearestConstraints: constraints,
    modeledContentCapacity: {
      scope: capacity.storage.capacityScope,
      maximumModelCeiling: {
        logicalObjects: capacity.storage.modeledLogicalObjectCapacity,
        logicalPayloadBytes: capacity.storage.modeledLogicalPayloadCapacityBytes,
        physicalResidentBytes: capacity.storage.modeledPhysicalResidentBytesAtCapacity,
        limitingRelayIndex: capacity.storage.limitingRelayIndex
      },
      plannedFillCapacity: {
        plannedFillFraction: capacity.storage.plannedFillFraction,
        logicalObjects: capacity.storage.plannedLogicalObjects,
        logicalPayloadBytes: capacity.storage.plannedLogicalPayloadBytes,
        physicalResidentBytes: Math.floor(
          capacity.storage.modeledPhysicalResidentBytesAtCapacity * capacity.storage.plannedFillFraction
        ),
        relayPhysicalResidentBytes: capacity.storage.relayAtPlannedFill.physicalResidentBytes
      }
    },
    contentChecks,
    rejectionCodes: capacity.rejectionReasons.map(reason => reason.code),
    binding: {
      sourceScenarioDigest: authority.source.scenarioDigest,
      workloadDigest,
      operationMixDigest,
      durabilityDigest,
      capacityConfigDigest: capacity.configDigest,
      capacityScenarioDigest: capacity.scenarioDigest
    }
  }
}

function resolvedHardware (config) {
  return {
    provenance: 'enumerated-target-assumption',
    diskBytesPerRelay: config.fleet.diskBytesPerRelay,
    diskSequentialWriteBytesPerSecond: config.disk.sequentialWriteBytesPerSecond,
    diskSequentialReadBytesPerSecond: config.disk.sequentialReadBytesPerSecond,
    diskRandomIopsPerSecond: config.disk.randomIopsPerSecond,
    walFsyncLatencyMs: config.wal.fsyncLatencyMs,
    walFsyncsPerSecond: fixed(1000 / config.wal.fsyncLatencyMs),
    networkIngressBitsPerSecond: config.network.ingressBitsPerSecond,
    networkEgressBitsPerSecond: config.network.egressBitsPerSecond,
    cpuCoresPerRelay: config.cpu.coresPerRelay,
    streamBufferedBytesPerRelay: config.streams.maxBufferedBytesPerRelay,
    streamSlotsPerRelay: config.streams.maxConcurrentStreamsPerRelay,
    inboxWaiterBytesPerRelay: config.surfaces.inboxWaiterMemoryBytesPerRelay,
    inboxWaiterSlotsPerRelay: config.surfaces.inboxMaxWaitersPerRelay
  }
}

function buildSizingAnswers (candidates, search) {
  const byRelayCount = search.relayCounts.map(relayCount => {
    const options = candidates.filter(candidate => candidate.relayCount === relayCount)
    const fit = options.find(candidate => candidate.planningFit)
    return {
      relayCount,
      found: Boolean(fit),
      candidateId: fit?.id || null,
      minimumEnumeratedHardwareProfileId: fit?.hardwareProfileId || null,
      hardware: fit?.hardware || null,
      modeledSustainableLogicalOpsPerSecond: fit?.modeledSustainableLogicalOpsPerSecond || null,
      bottleneck: fit?.bottleneck || options.at(-1)?.bottleneck || null,
      modeledContentCapacity: fit?.modeledContentCapacity || null
    }
  })
  const byHardwareProfile = search.hardwareProfiles.map(profile => {
    const options = candidates.filter(candidate => candidate.hardwareProfileId === profile.id)
    const fit = options.find(candidate => candidate.planningFit)
    return {
      hardwareProfileId: profile.id,
      found: Boolean(fit),
      candidateId: fit?.id || null,
      minimumEnumeratedRelayCount: fit?.relayCount || null,
      hardware: fit?.hardware || options[0]?.hardware || null,
      modeledSustainableLogicalOpsPerSecond: fit?.modeledSustainableLogicalOpsPerSecond || null,
      bottleneck: fit?.bottleneck || options.at(-1)?.bottleneck || null,
      modeledContentCapacity: fit?.modeledContentCapacity || null
    }
  })
  const fits = candidates.filter(candidate => candidate.planningFit)
  const pareto = fits.filter(candidate => !fits.some(other =>
    other.id !== candidate.id &&
    other.relayIndex <= candidate.relayIndex &&
    other.profileIndex <= candidate.profileIndex &&
    (other.relayIndex < candidate.relayIndex || other.profileIndex < candidate.profileIndex)
  ))
  return {
    definitionOfMinimum: 'first modeled fit in the ordered relay-count and component-wise nondecreasing hardware-profile search lattice',
    modeledTargetFoundInSearch: fits.length > 0,
    minimumEnumeratedHardwareByRelayCount: byRelayCount,
    minimumEnumeratedRelayCountByHardwareProfile: byHardwareProfile,
    paretoFrontierCandidateIds: pareto.map(candidate => candidate.id),
    searchCeiling: {
      relayCount: search.relayCounts.at(-1),
      hardwareProfileId: search.hardwareProfiles.at(-1).id,
      candidateId: candidates.at(-1).id,
      planningFit: candidates.at(-1).planningFit,
      bottleneck: candidates.at(-1).bottleneck,
      modeledSustainableLogicalOpsPerSecond: candidates.at(-1).modeledSustainableLogicalOpsPerSecond,
      modeledContentCapacity: candidates.at(-1).modeledContentCapacity
    }
  }
}

function buildBottleneckTransitions (candidates, search) {
  const transitions = []
  for (const relayCount of search.relayCounts) {
    const points = candidates.filter(candidate => candidate.relayCount === relayCount)
    addTransitions(transitions, 'hardware-profile-sweep', `relay:${relayCount}`, points)
  }
  for (const profile of search.hardwareProfiles) {
    const points = candidates.filter(candidate => candidate.hardwareProfileId === profile.id)
    addTransitions(transitions, 'relay-count-sweep', `profile:${profile.id}`, points)
  }
  const regions = {}
  for (const candidate of candidates) {
    if (!regions[candidate.bottleneck]) regions[candidate.bottleneck] = []
    regions[candidate.bottleneck].push(candidate.id)
  }
  return {
    transitionDefinition: 'Adjacent points are recorded when the limiting modeled resource changes or the point crosses the modeled-fit threshold.',
    noTransitionsObserved: transitions.length === 0,
    observedBottlenecks: Object.keys(regions).sort(),
    regions,
    transitions
  }
}

function addTransitions (target, dimension, series, points) {
  for (let index = 1; index < points.length; index++) {
    const from = points[index - 1]
    const to = points[index]
    const changes = []
    if (from.bottleneck !== to.bottleneck) changes.push('bottleneck')
    if (from.planningFit !== to.planningFit) changes.push('modeled-fit-threshold')
    if (changes.length === 0) continue
    target.push({
      dimension,
      series,
      fromCandidateId: from.id,
      toCandidateId: to.id,
      changes,
      fromBottleneck: from.bottleneck,
      toBottleneck: to.bottleneck,
      fromPlanningFit: from.planningFit,
      toPlanningFit: to.planningFit
    })
  }
}

function check (id, passed, observed, expected) {
  return { id, passed: passed === true, observed, expected }
}

function sha256 (value) {
  return createHash('sha256').update(value).digest('hex')
}

function fixed (value) {
  if (!Number.isFinite(value)) return value
  return Math.round(value * 1_000_000) / 1_000_000
}

function clone (value) {
  if (Array.isArray(value)) return value.map(clone)
  if (isPlainObject(value)) return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, clone(item)]))
  return value
}

function deepMerge (base, override) {
  if (Array.isArray(override)) return override.map(clone)
  if (!isPlainObject(override)) return override === undefined ? clone(base) : override
  const result = {}
  for (const key of new Set([...Object.keys(base || {}), ...Object.keys(override)])) {
    if (override[key] === undefined) result[key] = clone(base?.[key])
    else if (isPlainObject(base?.[key]) && isPlainObject(override[key])) result[key] = deepMerge(base[key], override[key])
    else result[key] = clone(override[key])
  }
  return result
}

function deepFreeze (value) {
  if (value && typeof value === 'object' && !Object.isFrozen(value)) {
    Object.freeze(value)
    for (const item of Object.values(value)) deepFreeze(item)
  }
  return value
}

function isPlainObject (value) {
  return value !== null && typeof value === 'object' && !Array.isArray(value)
}

function rejectUnknownFields (value, allowed, path, violations) {
  if (!isPlainObject(value)) return
  const set = new Set(allowed)
  for (const key of Object.keys(value)) {
    if (!set.has(key)) violations.push(`unknown field ${path}.${key}`)
  }
}

function positive (value, path, violations) {
  if (typeof value !== 'number' || !Number.isFinite(value) || value <= 0) {
    violations.push(`${path} must be a positive finite number`)
  }
}

function integer (value, path, minimum, maximum, violations) {
  if (!Number.isSafeInteger(value) || value < minimum || value > maximum) {
    violations.push(`${path} must be an integer between ${minimum} and ${maximum}`)
  }
}

function nonNegativeInteger (value, path, violations) {
  integer(value, path, 0, Number.MAX_SAFE_INTEGER, violations)
}

async function readStdin () {
  const chunks = []
  for await (const chunk of process.stdin) chunks.push(chunk)
  return Buffer.concat(chunks).toString('utf8')
}

function usage () {
  return [
    'Usage: node scripts/plan-blind-capacity.mjs [options]',
    '',
    'Options:',
    '  --release-profile NAME  Source exact workload from smoke or release (default: release)',
    '  --search FILE|-         Override the enumerated sizing search JSON',
    '  --out FILE              Write the report to FILE instead of stdout',
    '  --pretty | --compact    Select JSON formatting',
    '  --assert-fit            Exit 2 when no enumerated target is a modeled fit',
    '',
    'The result is modeled target sizing, not observed hardware or release evidence.'
  ].join('\n')
}

async function main (argv) {
  let profileName = 'release'
  let searchPath = null
  let outPath = null
  let pretty = true
  let assertFit = false
  for (let index = 0; index < argv.length; index++) {
    const arg = argv[index]
    if (arg === '--release-profile') {
      profileName = argv[++index]
      if (!profileName) throw new CapacitySizingError(['--release-profile requires a name'])
    } else if (arg === '--search') {
      searchPath = argv[++index]
      if (!searchPath) throw new CapacitySizingError(['--search requires a file path or -'])
    } else if (arg === '--out') {
      outPath = argv[++index]
      if (!outPath) throw new CapacitySizingError(['--out requires a file path'])
    } else if (arg === '--pretty') pretty = true
    else if (arg === '--compact') pretty = false
    else if (arg === '--assert-fit') assertFit = true
    else if (arg === '--help' || arg === '-h') {
      process.stdout.write(`${usage()}\n`)
      return
    } else throw new CapacitySizingError([`unknown argument ${arg}`])
  }
  let search = {}
  if (searchPath) {
    const source = searchPath === '-' ? await readStdin() : await readFile(resolve(searchPath), 'utf8')
    search = JSON.parse(source)
  }
  const input = buildReleaseCapacitySizingInput(profileName, search)
  const report = runCapacitySizingPlan(input)
  const output = `${JSON.stringify(report, null, pretty ? 2 : 0)}\n`
  if (outPath) await writeFile(resolve(outPath), output)
  else process.stdout.write(output)
  if (assertFit && !report.answers.modeledTargetFoundInSearch) process.exitCode = 2
}

if (process.argv[1] && fileURLToPath(import.meta.url) === resolve(process.argv[1])) {
  main(process.argv.slice(2)).catch(error => {
    const known = error instanceof CapacitySizingError
    const output = {
      schema: CAPACITY_SIZING_PLAN_SCHEMA,
      status: 'error',
      evidenceClass: 'modeled-target-hardware-not-observed',
      error: {
        code: known ? error.code : 'CAPACITY_SIZING_FAILED',
        message: error.message,
        violations: known ? error.violations : []
      },
      authorizesRelease: false
    }
    process.stdout.write(`${JSON.stringify(output)}\n`)
    process.exitCode = 1
  })
}