fix: tighten decision center query paths
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
import { readFileSync } from "node:fs";
|
||||
|
||||
type JsonRecord = Record<string, unknown>;
|
||||
|
||||
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')",
|
||||
]),
|
||||
"requirements list route must stay on the records model and exclude meetings",
|
||||
);
|
||||
|
||||
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}` : \"\"}`",
|
||||
]),
|
||||
"CLI must expose bounded list opt-in and diary source disambiguation",
|
||||
);
|
||||
|
||||
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-and-diary-source-query",
|
||||
"frontend-exact-diary-row-and-record-edit-body",
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
if (import.meta.main) {
|
||||
process.stdout.write(`${JSON.stringify(runDecisionCenterQueryContract(), null, 2)}\n`);
|
||||
}
|
||||
Reference in New Issue
Block a user