From f65e73ba1ac58f963ad797f8eb7b07f047dea9f2 Mon Sep 17 00:00:00 2001 From: Dotta Date: Mon, 9 Nov 2026 04:17:29 -0600 Subject: [PATCH] fix(status-cards): spell out the summary sentinels and the failure path in update tasks An update task told any assigned summarizer to preserve the Summarizer's STATUS and SUMMARY-DRAFT sentinels without defining them. A non-built-in summarizer searched for the format for minutes, including other runs' local files. State the exact lines, that the PUT body is plain Markdown, and that a summarizer that cannot produce a summary comments and blocks the task so the card keeps its previous summary. Co-Authored-By: Claude Opus 5.5 --- server/src/__tests__/status-cards.test.ts | 5 +++++ server/src/services/status-cards.ts | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/server/src/__tests__/status-cards.test.ts b/server/src/__tests__/status-cards.test.ts index a80da72f207..c32f3f4a233 100644 --- a/server/src/__tests__/status-cards.test.ts +++ b/server/src/__tests__/status-cards.test.ts @@ -500,6 +500,11 @@ describeEmbeddedPostgres("status card routes", () => { expect(res.body.enqueued).toBe(true); // The queued refresh carries the card's page, so the requester can link it instead of waiting for the summary. expect(res.body.card.href).toBe(`/${company.issuePrefix}/status/${card.id}`); + // Any assigned summarizer, not only the built-in one, gets the full write-back and failure contract inline. + const [generation] = await db.select().from(issues).where(eq(issues.id, res.body.generatingIssue.id)); + expect(generation?.description).toContain("put the final Markdown between a `<<>>` line and a `<<>>` line"); + expect(generation?.description).toContain("there is no other format to look up"); + expect(generation?.description).toContain("comment the reason on this task and set it to blocked"); }); it("requires tasks:assign for mutations", async () => { diff --git a/server/src/services/status-cards.ts b/server/src/services/status-cards.ts index e286af843f8..110d1c59928 100644 --- a/server/src/services/status-cards.ts +++ b/server/src/services/status-cards.ts @@ -95,7 +95,7 @@ function updateDescription(input: { previousSummary: string | null; snapshot: CompanySearchIssueSummary[]; }) { - const mechanical = `Return the completed Markdown through \`PUT /api/status-cards/${input.card.id}/summary\` with \`generationIssueId\`, a short non-empty \`changeSummary\`, and the model id. Do not call issue-list endpoints. Preserve the streaming STATUS and <<>> sentinels used by the Summarizer. Issues the Markdown references by identifier (e.g. ABC-123) or issue link automatically join the card's watched set, so reference an issue only when the board should keep tracking it.`; + const mechanical = `Return the completed Markdown through \`PUT /api/status-cards/${input.card.id}/summary\` with \`generationIssueId\`, a short non-empty \`changeSummary\`, and the model id. Do not call issue-list endpoints. While you work, write short plain-text \`STATUS:\` lines; before the write, put the final Markdown between a \`<<>>\` line and a \`<<>>\` line in your reply text. These lines only drive the card's live preview; the PUT body is the plain Markdown, and there is no other format to look up. If you cannot produce a summary from what you can read, comment the reason on this task and set it to blocked; the card then keeps its previous summary and shows the failure. Issues the Markdown references by identifier (e.g. ABC-123) or issue link automatically join the card's watched set, so reference an issue only when the board should keep tracking it.`; // The card prompt is the board's single standing request: it already says // what to watch and how the update should read, so it doubles as the // summary instructions — there is no separate default prompt to append to