import { readFileSync } from "node:fs"; type JsonRecord = Record; function assertCondition(condition: unknown, message: string, detail: JsonRecord = {}): void { if (!condition) throw new Error(`${message}: ${JSON.stringify(detail)}`); } function source(path: string): string { return readFileSync(path, "utf8"); } function includesAll(text: string, snippets: string[]): boolean { return snippets.every((snippet) => text.includes(snippet)); } export function runDecisionCenterQueryContract(): JsonRecord { const service = source("src/components/microservices/decision-center/src/index.ts"); const cli = source("scripts/src/decision-center.ts"); const frontend = source("src/components/frontend/src/decision-center.tsx"); assertCondition( includesAll(service, [ 'url.pathname === "/api/requirements" && method === "GET"', "listRecords(url, { requirementOnly: true })", "type IN ('decision', 'goal', 'external_goal', 'internal_goal', 'blocker', 'debt', 'experiment')", "doc_no", "doc_type", "doc_priority", "doc_year", "doc_seq", "signer", "issued_at", "effective_scope", "supersedes", "superseded_by", ]), "requirements list route must stay on the records model, exclude meetings, and expose document fields", ); assertCondition( includesAll(service, [ "CASE WHEN ${includeBody}::boolean THEN body ELSE left(body, 4000) END AS body", "return rows.map((row) => recordFromRow(row, { includeBody }));", "body: includeBody ? body : \"\"", ]), "record list must be body-light by default while preserving summaries", ); assertCondition( includesAll(service, [ "sourceFileFilterFromUrl(url)", "url.searchParams.get(\"sourceFile\") ?? url.searchParams.get(\"sourcePath\") ?? url.searchParams.get(\"source\")", "AND (${sourceFile || null}::text IS NULL OR source_file = ${sourceFile || null})", "getDiaryEntry(key, { sourceFile: sourceFileFilterFromUrl(url) })", ]), "diary date lookup must support sourceFile disambiguation for same-day entries", ); assertCondition( service.split("AND (${sourceFile || null}::text IS NULL OR source_file = ${sourceFile || null})").length >= 3, "diary sourceFile filter must cover both read and date-key upsert lookup paths", ); assertCondition( includesAll(service, [ "CASE WHEN ${includeBody}::boolean THEN body ELSE left(body, 4000) END AS body", "return rows.map((row) => diaryEntryFromRow(row, { includeBody }));", ]), "diary list must be body-light by default while preserving summaries", ); assertCondition( includesAll(cli, [ "if (args.includes(\"--include-body\")) params.set(\"includeBody\", \"true\")", "function diaryShowQuery(args: string[]): string", "params.set(\"sourceFile\", sourceFile)", "showDiary(diaryId, args.slice(3))", "`/api/requirements${query ? `?${query}` : \"\"}`", "parseDocumentNo(optionValue(args, [\"--doc-no\", \"--docNo\", \"--document-no\", \"--documentNo\"])", "params.set(\"docNo\", docNo)", "payload.docType = docType", "payload.signer = signer", ]), "CLI must expose bounded list opt-in, diary source disambiguation, and document fields", ); assertCondition( includesAll(frontend, [ "function diaryEntryLookupPath(entry: any): string", "const key = entry?.id || entry?.date", "if (entry?.sourceFile) params.set(\"sourceFile\", String(entry.sourceFile))", "decisionApi(apiBaseUrl, diaryEntryLookupPath(entry))", "if (!record?.id || record?.body) return", "`/api/records/${encodeURIComponent(record.id)}`", ]), "frontend must select exact diary rows and fetch full record bodies before editing list results", ); return { ok: true, checks: [ "requirements-route", "body-light-record-list-query", "body-light-diary-list-query", "diary-source-disambiguation", "cli-bounded-list-diary-source-and-document-query", "frontend-exact-diary-row-and-record-edit-body", ], }; } if (import.meta.main) { process.stdout.write(`${JSON.stringify(runDecisionCenterQueryContract(), null, 2)}\n`); }