import { type ArtifactDescriptor, CONTRACT_PATHS, type ResearchMode } from "@autoscholar/schemas"; import { MODE_PROFILES } from "./profiles.js"; export interface ContractCheck { mode: ResearchMode; missing: string[]; present: string[]; ok: boolean; } /** Deterministic check that the artifact set contains every mode-required path. */ export function checkArtifactContract( mode: ResearchMode, artifacts: Pick[], ): ContractCheck { const paths = new Set(artifacts.map((a) => a.path)); const required = MODE_PROFILES[mode].requiredArtifacts; const missing = required.filter((p) => !paths.has(p)); const present = required.filter((p) => paths.has(p)); return { mode, missing, present, ok: missing.length === 0 }; } /** Artifacts that are promoted to the public bundle at publication (private provenance is redacted separately). */ export const PUBLIC_PROMOTION_RULES: Array<{ match: RegExp; kind: string; contentType?: string }> = [ { match: /^manuscript\/main\.pdf$/, kind: "pdf", contentType: "application/pdf" }, { match: /^manuscript\/(main\.tex|references\.bib|.*\.(tex|bib|cls|sty|bst))$/, kind: "manuscript" }, { match: /^manuscript\/abstract\.md$/, kind: "manuscript" }, { match: /^manuscript\/metadata\.json$/, kind: "manuscript" }, { match: /^results\/figures\/.*\.(pdf|png|svg)$/, kind: "figure" }, { match: /^results\/tables\/.*\.(tex|csv|md)$/, kind: "table" }, { match: /^results\/(metrics\.json|robustness\.md)$/, kind: "result" }, { match: /^code\/(?!.*\.(env|pem|key)$).*/, kind: "code" }, { match: /^data\/(README\.md|manifest\.json)$/, kind: "data" }, { match: /^data\/.*\.(csv|parquet|json|txt|zip)$/, kind: "data" }, { match: /^sources\/(references\.bib|sources\.json|source-map\.json)$/, kind: "source" }, { match: /^review\/(review-1\.md|revisions\.md|qa-report\.json)$/, kind: "review" }, { match: /^plan\/(research-plan\.md|feasibility\.json)$/, kind: "plan" }, { match: /^provenance\/(run-summary\.json|checksums\.sha256)$/, kind: "provenance" }, ]; /** Paths that are never promoted (private). */ export const NEVER_PUBLIC = [ /^input\//, /^provenance\/events\.jsonl$/, /\.env($|\.)/, /(^|\/)\.(ssh|aws|claude)(\/|$)/, /\.(pem|key|p12)$/, /^logs?\//, ]; export function isPromotable(path: string): { promote: boolean; kind?: string; contentType?: string } { if (NEVER_PUBLIC.some((r) => r.test(path))) return { promote: false }; for (const r of PUBLIC_PROMOTION_RULES) if (r.match.test(path)) return { promote: true, kind: r.kind, contentType: r.contentType }; return { promote: false }; } export { CONTRACT_PATHS };