fix: preserve decision diary summary

Keep diary summary as an independent Decision Center field when body content is supplied from --body-file, and cover the CLI/backend contract.
This commit is contained in:
AgentRun Artificer
2026-06-11 17:35:35 +08:00
parent 49c3145439
commit fc3a095858
5 changed files with 195 additions and 15 deletions
+3
View File
@@ -461,6 +461,7 @@ export async function runChecks(config: UniDeskConfig, options: CheckOptions = d
fileItem("scripts/src/ci.ts"),
fileItem("scripts/src/e2e.ts"),
fileItem("scripts/deploy-artifact-matrix-contract-test.ts"),
fileItem("scripts/decision-center-diary-summary-contract-test.ts"),
fileItem("scripts/decision-center-desired-state-contract-test.ts"),
fileItem("scripts/code-queue-prompt-observation-test.ts"),
fileItem("scripts/check-command-progress-contract-test.ts"),
@@ -523,6 +524,7 @@ export async function runChecks(config: UniDeskConfig, options: CheckOptions = d
items.push(await commandItem("provider:runner-triage-contract", ["bun", "scripts/provider-runner-triage-contract-test.ts"], 30_000, process.env, options.checkHeartbeatMs));
items.push(await commandItem("ssh:argv-guidance-contract", ["bun", "scripts/ssh-argv-guidance-contract-test.ts"], 30_000, process.env, options.checkHeartbeatMs));
items.push(await commandItem("deploy:artifact-matrix-contract", ["bun", "scripts/deploy-artifact-matrix-contract-test.ts"], 90_000, process.env, options.checkHeartbeatMs));
items.push(await commandItem("decision-center:diary-summary-contract", ["bun", "scripts/decision-center-diary-summary-contract-test.ts"], 30_000, process.env, options.checkHeartbeatMs));
items.push(await commandItem("decision-center:desired-state-contract", ["bun", "scripts/decision-center-desired-state-contract-test.ts"], 30_000, process.env, options.checkHeartbeatMs));
items.push(await commandItem("code-queue:active-run-heartbeat-visible", ["bun", "scripts/code-queue-liveness-diagnostics-test.ts", "--only", "code-queue:active-run-heartbeat-visible"], 30_000, process.env, options.checkHeartbeatMs));
items.push(await commandItem("code-queue:trace-gap-not-stale", ["bun", "scripts/code-queue-liveness-diagnostics-test.ts", "--only", "code-queue:trace-gap-not-stale"], 30_000, process.env, options.checkHeartbeatMs));
@@ -572,6 +574,7 @@ export async function runChecks(config: UniDeskConfig, options: CheckOptions = d
items.push(skippedItem("provider:runner-triage-contract", "Provider runner triage contract is opt-in with script checks", "--scripts-typecheck or --full"));
items.push(skippedItem("ssh:argv-guidance-contract", "SSH argv guidance and failure hint contract is opt-in with script checks", "--scripts-typecheck or --full"));
items.push(skippedItem("deploy:artifact-matrix-contract", "deploy artifact matrix contract is opt-in with script checks", "--scripts-typecheck or --full"));
items.push(skippedItem("decision-center:diary-summary-contract", "Decision Center diary summary contract is opt-in with script checks", "--scripts-typecheck or --full"));
items.push(skippedItem("decision-center:desired-state-contract", "Decision Center desired-state drift contract is opt-in with script checks", "--scripts-typecheck or --full"));
items.push(skippedItem("code-queue:liveness-diagnostics-fixtures", "Code Queue liveness diagnostics fixtures are opt-in with script checks", "--scripts-typecheck or --full"));
items.push(skippedItem("baidu-netdisk:artifact-guard-contract", "Baidu Netdisk artifact guard contract is opt-in with script checks", "--scripts-typecheck or --full"));
+25 -7
View File
@@ -151,7 +151,7 @@ function readMarkdownFile(path: string): { absolutePath: string; markdown: strin
return { absolutePath, markdown };
}
function bodyFromArgs(args: string[], command: string): { body: string; bodySource: Record<string, string> } {
function optionalBodyFromArgs(args: string[], command: string): { body: string | undefined; bodySource: Record<string, string> } {
const body = optionValue(args, ["--body"]);
const bodyFile = optionValue(args, ["--body-file", "--markdown-file"]);
const markdownFile = optionValue(args, ["--file"]);
@@ -163,6 +163,12 @@ function bodyFromArgs(args: string[], command: string): { body: string; bodySour
const { absolutePath, markdown } = readMarkdownFile(file);
return { body: markdown, bodySource: { kind: "file", path: absolutePath } };
}
return { body: undefined, bodySource: { kind: "none" } };
}
function bodyFromArgs(args: string[], command: string): { body: string; bodySource: Record<string, string> } {
const { body, bodySource } = optionalBodyFromArgs(args, command);
if (body !== undefined) return { body, bodySource };
throw new Error(`${command} requires --body text or --body-file path`);
}
@@ -407,14 +413,18 @@ async function todayDiaryAsync(fetcher: (path: string, init?: { method?: string;
function diaryEditPayload(args: string[], command: string): { key: string; payload: Record<string, unknown>; bodySource: Record<string, string> } {
const key = positionalArgs(args)[0];
if (!key) throw new Error(`${command} requires entry id or YYYY-MM-DD date`);
const { body, bodySource } = bodyFromArgs(args, command);
const payload: Record<string, unknown> = { body };
const { body, bodySource } = optionalBodyFromArgs(args, command);
const payload: Record<string, unknown> = {};
const title = optionValue(args, ["--title"]);
const summary = optionValue(args, ["--summary"]);
const sourceFile = optionValue(args, ["--source-file", "--source-path", "--source"]);
if (body !== undefined) payload.body = body;
if (title !== undefined) payload.title = title;
if (summary !== undefined) payload.summary = summary;
if (sourceFile !== undefined) payload.sourceFile = sourceFile;
const tags = splitList(optionValues(args, ["--tag", "--tags"]));
if (tags.length > 0) payload.tags = tags;
if (Object.keys(payload).length === 0) throw new Error(`${command} requires --body text, --body-file path, --summary, --title, --source-file, or --tag`);
return { key, payload, bodySource };
}
@@ -429,26 +439,34 @@ async function editDiaryAsync(args: string[], fetcher: (path: string, init?: { m
}
function editTodayDiary(args: string[]): unknown {
const { body, bodySource } = bodyFromArgs(args, "decision diary today --edit");
const payload: Record<string, unknown> = { body };
const { body, bodySource } = optionalBodyFromArgs(args, "decision diary today --edit");
const payload: Record<string, unknown> = {};
const title = optionValue(args, ["--title"]);
const summary = optionValue(args, ["--summary"]);
const sourceFile = optionValue(args, ["--source-file", "--source-path", "--source"]);
if (body !== undefined) payload.body = body;
if (title !== undefined) payload.title = title;
if (summary !== undefined) payload.summary = summary;
if (sourceFile !== undefined) payload.sourceFile = sourceFile;
const tags = splitList(optionValues(args, ["--tag", "--tags"]));
if (tags.length > 0) payload.tags = tags;
if (Object.keys(payload).length === 0) throw new Error("decision diary today --edit requires --body text, --body-file path, --summary, --title, --source-file, or --tag");
return { bodySource, result: unwrapProxyResponse(decisionProxy("/api/diary/today", { method: "PUT", body: payload })) };
}
async function editTodayDiaryAsync(args: string[], fetcher: (path: string, init?: { method?: string; body?: unknown }) => Promise<unknown>): Promise<unknown> {
const { body, bodySource } = bodyFromArgs(args, "decision diary today --edit");
const payload: Record<string, unknown> = { body };
const { body, bodySource } = optionalBodyFromArgs(args, "decision diary today --edit");
const payload: Record<string, unknown> = {};
const title = optionValue(args, ["--title"]);
const summary = optionValue(args, ["--summary"]);
const sourceFile = optionValue(args, ["--source-file", "--source-path", "--source"]);
if (body !== undefined) payload.body = body;
if (title !== undefined) payload.title = title;
if (summary !== undefined) payload.summary = summary;
if (sourceFile !== undefined) payload.sourceFile = sourceFile;
const tags = splitList(optionValues(args, ["--tag", "--tags"]));
if (tags.length > 0) payload.tags = tags;
if (Object.keys(payload).length === 0) throw new Error("decision diary today --edit requires --body text, --body-file path, --summary, --title, --source-file, or --tag");
return { bodySource, result: unwrapProxyResponse(await decisionProxyAsync(fetcher, "/api/diary/today", { method: "PUT", body: payload })) };
}
+2 -2
View File
@@ -42,10 +42,10 @@ export function rootHelp(): unknown {
{ command: "decision diary import <markdown-file> [--source-file path] [--tag tag] [--include-entries]", description: "Import a dated work log Markdown into PostgreSQL diary entries split as YYYY-MM/YYYY-MM-DD.md." },
{ command: "decision diary list [--month YYYY-MM] [--from YYYY-MM-DD] [--to YYYY-MM-DD] [--limit N] [--include-body]", description: "List daily Markdown diary entries stored by Decision Center." },
{ command: "decision diary history [--month YYYY-MM] [--from YYYY-MM-DD] [--to YYYY-MM-DD] [--limit N] [--include-body]", description: "Read diary history through the productized history API alias." },
{ command: "decision diary today [--edit --body-file path] [--title text] [--tag tag]", description: "Get or create today's diary entry using the service's real current date; --edit saves today's Markdown." },
{ command: "decision diary today [--edit --body-file path|--body text|--summary text] [--title text] [--tag tag]", description: "Get or create today's diary entry using the service's real current date; --edit saves today's Markdown and optional explicit summary." },
{ command: "decision diary months", description: "List available Decision Center diary months with day counts." },
{ command: "decision diary show <YYYY-MM-DD|id> [--source-file path]", description: "Show one daily diary Markdown entry; source-file disambiguates same-day entries from multiple imports." },
{ command: "decision diary edit|upsert <YYYY-MM-DD|id> --body-file path [--title text] [--source-file path] [--tag tag]", description: "Create or edit one daily diary entry through PUT /api/diary/entries/:idOrDate via backend-core proxy." },
{ command: "decision diary edit|upsert <YYYY-MM-DD|id> [--body-file path|--body text] [--summary text] [--title text] [--source-file path] [--tag tag]", description: "Create or edit one daily diary entry through PUT /api/diary/entries/:idOrDate via backend-core proxy, keeping summary independent from body." },
{ command: "decision list [--doc-no DC-...] [--doc-type ...] [--doc-priority P0|P1|P2|P3] [--year YYYY] [--type ...] [--status ...] [--level|--priority ...] [--limit N] [--include-body]", description: "List Decision Center records through the user-service proxy; bodies are omitted unless --include-body is set." },
{ command: "decision requirement list|create|show|update|upsert [id|docNo] [--title text] [--body-file path] [--type external_goal|internal_goal|goal|decision|blocker|debt|experiment] [--doc-no DC-...] [--doc-type ...] [--doc-priority P0|P1|P2|P3] [--signer text] [--issued-at ISO]", description: "Manage productized requirement records over the PostgreSQL records model, excluding meeting records." },
{ command: "decision show <id|docNo>", description: "Show one Decision Center record." },