← Files CompText ContextARCHIVED FILE

scripts/build-context.mjs

2.08 KB · Oct 2, 2026 · 00:31 UTC

↓ Download file

import fs from 'node:fs';
import { canonicalText, sha256Canonical } from './canonical-json.mjs';

const [sourcePath, contractPath] = process.argv.slice(2);
if (!sourcePath || !contractPath) {
  console.error('usage: node build-context-ir.mjs <source-summary.json> <contract.json>');
  process.exit(2);
}

const source = JSON.parse(fs.readFileSync(sourcePath, 'utf8'));
const contract = JSON.parse(fs.readFileSync(contractPath, 'utf8'));
const digestPattern = /^sha256:[0-9a-f]{64}$/;
if (!digestPattern.test(source.source_digest ?? '')) throw new Error('invalid source_digest');
if (!Array.isArray(source.field_paths)) throw new Error('field_paths must be an array');
if (!Array.isArray(contract.required_field_paths) || contract.required_field_paths.length === 0) {
  throw new Error('required_field_paths must be non-empty');
}

const uniqSorted = values => [...new Set(values)].sort();
const orderedUnique = values => [...new Set(values)];
const sortEdges = values => [...values].sort((a,b) =>
  `${a.source}\0${a.target}`.localeCompare(`${b.source}\0${b.target}`));
const required = uniqSorted(contract.required_field_paths);
const available = new Set(source.field_paths);
const satisfied = required.filter(path => available.has(path));
const missing = required.filter(path => !available.has(path));
const preimage = {
  schema: 'comptext-context/v1',
  source_digest: source.source_digest,
  strategy: contract.strategy,
  required_field_paths: required,
  satisfied_field_paths: satisfied,
  missing_field_paths: missing,
  constraints: uniqSorted(contract.constraints ?? []),
  required_order: orderedUnique(contract.required_order ?? []),
  dependency_edges: sortEdges(contract.dependency_edges ?? []),
  blockers: sortEdges(contract.blockers ?? []),
  recovery_paths: sortEdges(contract.recovery_paths ?? []),
  validation: {
    valid: missing.length === 0,
    failure_labels: missing.length ? ['MISSING_REQUIRED_FIELD'] : []
  },
  non_claims: uniqSorted(contract.non_claims ?? [])
};

const context = {
  ...preimage,
  context_id: sha256Canonical(preimage)
};

process.stdout.write(`${canonicalText(context)}\n`);

SHA-256: 8f6d506f12bf0a17057e243a7931412b67c7dd9af7d52844264dcd53f08570e9