Skip to content

Commit 55f2e53

Browse files
bug maintenance slack (#1939)
* bug maintenance slack * ci: apply automated fixes * fix test --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
1 parent cc88fbb commit 55f2e53

File tree

6 files changed

+54
-25
lines changed

6 files changed

+54
-25
lines changed

apps/server/src/routes/slack/agent.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,13 @@ function buildSystemPrompt(workspaceName: string): string {
1818
return `You are the OpenStatus assistant for workspace "${workspaceName}".
1919
You help teams create and manage status reports and maintenance windows through Slack.
2020
21-
IMPORTANT: You have NO knowledge of this workspace's data. NEVER guess or make up status pages, components, or reports. ALWAYS call the appropriate tool first to get real data.
22-
- Questions about pages or components -> call listStatusPages
23-
- Questions about reports -> call listStatusReports
24-
- Questions about maintenances -> call listMaintenances
25-
- Creating a report -> call listStatusPages first, then createStatusReport
26-
- Scheduling maintenance -> call listStatusPages first, then createMaintenance
21+
IMPORTANT: You have NO knowledge of this workspace's data. NEVER guess or make up IDs (page IDs, component IDs, report IDs). You MUST call the appropriate tool first to get real data.
22+
- Questions about pages or components -> call listStatusPages FIRST
23+
- Questions about reports -> call listStatusReports FIRST
24+
- Questions about maintenances -> call listMaintenances FIRST
25+
- Creating a report -> you MUST call listStatusPages first to get the real pageId, then call createStatusReport with that pageId
26+
- Scheduling maintenance -> you MUST call listStatusPages first to get the real pageId, then call createMaintenance with that pageId
27+
- NEVER pass a pageId you did not receive from listStatusPages. Guessing a pageId WILL cause an error.
2728
2829
Capabilities:
2930
- Create status reports on status pages (createStatusReport)

apps/server/src/routes/slack/blocks.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -187,13 +187,12 @@ export function buildConfirmationBlocks(
187187
break;
188188
}
189189
case "createMaintenance": {
190-
const { title, message, from, to, pageId, pageComponentIds } =
191-
action.params;
190+
const { title, message, from, to, pageComponentIds } = action.params;
192191
blocks.push({
193192
type: "section",
194193
text: {
195194
type: "mrkdwn",
196-
text: `*Schedule Maintenance*\n\n*Title:* ${title}\n*From:* ${formatDate(from)}\n*To:* ${formatDate(to)}\n*Page ID:* ${pageId}${
195+
text: `*Schedule Maintenance*\n\n*Title:* ${title}\n*From:* ${formatDate(from)}\n*To:* ${formatDate(to)}${
197196
pageComponentIds?.length
198197
? `\n*Components:* ${pageComponentIds.join(", ")}`
199198
: ""

apps/server/src/routes/slack/interactions.test.ts

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -394,7 +394,7 @@ describe("createMaintenance execution", () => {
394394
expect(cancelCall).toBeDefined();
395395
});
396396

397-
test("shows error for invalid page id", async () => {
397+
test("shows error when AI hallucinates page id", async () => {
398398
seedMaintenanceAction({ pageId: 99999 });
399399

400400
const res = await signAndPost(app, {
@@ -485,7 +485,29 @@ describe("createMaintenance execution", () => {
485485
});
486486

487487
test("does not leak internal error details to user", async () => {
488-
seedMaintenanceAction({ pageId: 99999 });
488+
const data = {
489+
id: "maint-001",
490+
workspaceId: 2,
491+
limits: {},
492+
botToken: "xoxb-test",
493+
channelId: "C1",
494+
threadTs: "2.1",
495+
messageTs: "2.2",
496+
userId: "U_OWNER",
497+
createdAt: Date.now(),
498+
action: {
499+
type: "createMaintenance" as const,
500+
params: {
501+
title: "DB Maintenance",
502+
message: "Upgrade.",
503+
from: new Date(Date.now() + 86400000).toISOString(),
504+
to: new Date(Date.now() + 86400000 + 3600000).toISOString(),
505+
pageId: 99999,
506+
},
507+
},
508+
};
509+
redisStore.set(`slack:action:${data.id}`, JSON.stringify(data));
510+
redisStore.set(`slack:thread:${data.threadTs}`, data.id);
489511

490512
await signAndPost(app, {
491513
type: "block_actions",

apps/server/src/routes/slack/interactions.ts

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -409,11 +409,11 @@ async function executeAction(
409409
.get();
410410

411411
if (!pageRecord) {
412-
throw new Error(
413-
`Page ${maintenancePageId} not found in this workspace`,
414-
);
412+
throw new Error("Page not found in this workspace");
415413
}
416414

415+
const resolvedPageId = pageRecord.id;
416+
417417
let componentIds: number[] = [];
418418
if (maintenanceComponentIds?.length) {
419419
const numericIds = maintenanceComponentIds.map((id) => Number(id));
@@ -440,12 +440,9 @@ async function executeAction(
440440
}
441441

442442
const componentPageId = validComponents[0]?.pageId;
443-
if (
444-
componentPageId !== null &&
445-
componentPageId !== maintenancePageId
446-
) {
443+
if (componentPageId !== null && componentPageId !== resolvedPageId) {
447444
throw new Error(
448-
`pageId ${maintenancePageId} does not match the page (${componentPageId}) that the selected components belong to`,
445+
"Selected components do not belong to the target status page",
449446
);
450447
}
451448

@@ -456,7 +453,7 @@ async function executeAction(
456453
.insert(maintenance)
457454
.values({
458455
workspaceId,
459-
pageId: maintenancePageId,
456+
pageId: resolvedPageId,
460457
title,
461458
message,
462459
from: fromDate,
@@ -484,7 +481,9 @@ async function executeAction(
484481
});
485482
}
486483

487-
const maintenancePageUrl = await getPageUrl(maintenancePageId);
484+
const maintenancePageUrl = newMaintenance.pageId
485+
? await getPageUrl(newMaintenance.pageId)
486+
: null;
488487

489488
await slack.chat.update({
490489
channel: channelId,

apps/server/src/routes/slack/tools/create-maintenance.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { z } from "zod";
44
export function createCreateMaintenanceTool() {
55
return tool({
66
description:
7-
"Schedule a maintenance window on a status page. Parse natural language dates into ISO 8601 format (e.g. 'next Friday 2-3 PM' -> proper ISO strings). The maintenance will be shown to the user for confirmation before publishing.",
7+
"Schedule a maintenance window on a status page. IMPORTANT: You MUST call listStatusPages first to get the real pageId — never guess or make up a pageId. Parse natural language dates into ISO 8601 format (e.g. 'next Friday 2-3 PM' -> proper ISO strings). The maintenance will be shown to the user for confirmation before publishing.",
88
inputSchema: z.object({
99
title: z.string().describe("Short title for the maintenance window"),
1010
message: z
@@ -18,7 +18,11 @@ export function createCreateMaintenanceTool() {
1818
to: z
1919
.string()
2020
.describe("End time in ISO 8601 format (e.g. 2025-03-14T15:00:00Z)"),
21-
pageId: z.number().describe("ID of the status page to post on"),
21+
pageId: z
22+
.number()
23+
.describe(
24+
"ID of the status page — MUST come from listStatusPages, never guess this value",
25+
),
2226
pageComponentIds: z
2327
.array(z.string())
2428
.optional()

apps/server/src/routes/slack/tools/create-status-report.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { z } from "zod";
44
export function createCreateStatusReportTool() {
55
return tool({
66
description:
7-
"Create a new status report. Draft the title and message based on the conversation. The report will be shown to the user for confirmation before publishing.",
7+
"Create a new status report. IMPORTANT: You MUST call listStatusPages first to get the real pageId — never guess or make up a pageId. Draft the title and message based on the conversation. The report will be shown to the user for confirmation before publishing.",
88
inputSchema: z.object({
99
title: z.string().describe("Short title for the status report"),
1010
status: z
@@ -15,7 +15,11 @@ export function createCreateStatusReportTool() {
1515
.describe(
1616
"Professional status update message for the public status page",
1717
),
18-
pageId: z.number().describe("ID of the status page to post on"),
18+
pageId: z
19+
.number()
20+
.describe(
21+
"ID of the status page — MUST come from listStatusPages, never guess this value",
22+
),
1923
pageComponentIds: z
2024
.array(z.string())
2125
.optional()

0 commit comments

Comments
 (0)