spb/autoscholar
Public
1import { type ArtifactDescriptor, CONTRACT_PATHS, type ResearchMode } from "@autoscholar/schemas";2import { MODE_PROFILES } from "./profiles.js";34export interface ContractCheck {5 mode: ResearchMode;6 missing: string[];7 present: string[];8 ok: boolean;9}1011/** Deterministic check that the artifact set contains every mode-required path. */12export function checkArtifactContract(13 mode: ResearchMode,14 artifacts: Pick<ArtifactDescriptor, "path">[],15): ContractCheck {16 const paths = new Set(artifacts.map((a) => a.path));17 const required = MODE_PROFILES[mode].requiredArtifacts;18 const missing = required.filter((p) => !paths.has(p));19 const present = required.filter((p) => paths.has(p));20 return { mode, missing, present, ok: missing.length === 0 };21}2223/** Artifacts that are promoted to the public bundle at publication (private provenance is redacted separately). */24export const PUBLIC_PROMOTION_RULES: Array<{ match: RegExp; kind: string; contentType?: string }> = [25 { match: /^manuscript\/main\.pdf$/, kind: "pdf", contentType: "application/pdf" },26 { match: /^manuscript\/(main\.tex|references\.bib|.*\.(tex|bib|cls|sty|bst))$/, kind: "manuscript" },27 { match: /^manuscript\/abstract\.md$/, kind: "manuscript" },28 { match: /^manuscript\/metadata\.json$/, kind: "manuscript" },29 { match: /^results\/figures\/.*\.(pdf|png|svg)$/, kind: "figure" },30 { match: /^results\/tables\/.*\.(tex|csv|md)$/, kind: "table" },31 { match: /^results\/(metrics\.json|robustness\.md)$/, kind: "result" },32 { match: /^code\/(?!.*\.(env|pem|key)$).*/, kind: "code" },33 { match: /^data\/(README\.md|manifest\.json)$/, kind: "data" },34 { match: /^data\/.*\.(csv|parquet|json|txt|zip)$/, kind: "data" },35 { match: /^sources\/(references\.bib|sources\.json|source-map\.json)$/, kind: "source" },36 { match: /^review\/(review-1\.md|revisions\.md|qa-report\.json)$/, kind: "review" },37 { match: /^plan\/(research-plan\.md|feasibility\.json)$/, kind: "plan" },38 { match: /^provenance\/(run-summary\.json|checksums\.sha256)$/, kind: "provenance" },39];4041/** Paths that are never promoted (private). */42export const NEVER_PUBLIC = [43 /^input\//,44 /^provenance\/events\.jsonl$/,45 /\.env($|\.)/,46 /(^|\/)\.(ssh|aws|claude)(\/|$)/,47 /\.(pem|key|p12)$/,48 /^logs?\//,49];5051export function isPromotable(path: string): { promote: boolean; kind?: string; contentType?: string } {52 if (NEVER_PUBLIC.some((r) => r.test(path))) return { promote: false };53 for (const r of PUBLIC_PROMOTION_RULES)54 if (r.match.test(path)) return { promote: true, kind: r.kind, contentType: r.contentType };55 return { promote: false };56}5758export { CONTRACT_PATHS };59