Reports · Issue #4463

Built-in question answers persist on the tested main revision

2026-09-30 · Bug · Medium priority · Medium effort

NOT REPRODUCED · High confidence for the tested built-in submission and database persistence path. The installed desktop 0.44.0 symptom and transcript rendering remain unresolved; this is not an assertion that the entire issue is fixed.

Claim and scope

The issue claims that a built-in AskUserQuestion answer reaches the agent but the interaction resolution stores only its kind. This test installs the actual repository built-in through the server plugin installer, invokes its registered AskUserQuestion tool, submits a synthetic option plus free text through the server interaction service, and reads the resulting interaction and lifecycle event from real SQLite tables. It then backs up that isolated database to disk, opens a new SQLite connection, and repeats both reads.

Both clean runs retain the answer in description.title, description.detail and description.payload, in addition to returning it to the tool caller. A separate synthetic plugin request with no describeSubmission callback produces exactly the bare plugin_submitted resolution while returning its answer in memory. That control establishes the distinction between delivery and persistence; it does not prove that a missing callback caused the installed-app incident.

The fixture uses the real plugin registration/API/lifecycle and database code. Synthetic host, project and thread rows provide context, with no connected host daemon, external provider session, browser, desktop bundle or real user data. The harness database begins in memory; the separate file backup and reopened connection validate database contents, not crash durability or a running server restart. No UI behavior is claimed, so no screenshot is presented.

Expected and actual

Expected: answering “Which display color?” with “Blue” and free text “with large text” preserves the answer. Actual in both runs: tool execution succeeds; interaction status is resolved; the interaction row and lifecycle event contain the title “Answered Which display color? — Blue; with large text”, matching detail and structured answer payload. The reopened database has identical records. The no-description control stores only the kind. The full actual output appears below.

Trusted base and environment

Fetched public origin/main: 379cc870fdcd3019c906104d3cb51ad4e3c1e06a. Linux x86_64, Node 24.19.0, repository-pinned pnpm 9.15.0, Vitest 4.1.1. Separate clean worktrees at the same SHA, separate node_modules and writable caches, shared dependency download store. Both frozen installs and server builds exit 0. No new dependency was added. Each run creates and cleans fresh synthetic harness state; no listener or test port is required.

Exact repeatable steps

git clone https://github.com/get-bb/bb.git bb-repro
cd bb-repro
git checkout --detach 379cc870fdcd3019c906104d3cb51ad4e3c1e06a
corepack pnpm install --frozen-lockfile
corepack pnpm exec turbo run build --filter=@bb/server --force
# Save the test below as apps/server/test/services/plugins/issue-4463.test.ts
corepack pnpm exec turbo run test --filter=@bb/server --force -- test/services/plugins/issue-4463.test.ts --silent=false

Ensure pnpm 9.15.0 is also used by subprocesses. This environment used a task-local Corepack shim and disposable writable caches. Repeat the same commands and identical test in a second clean checkout at the same SHA. Both final test commands must exit 0 with one passing test and the two JSON evidence records. No source modification beyond this temporary test is required.

apps/server/test/services/plugins/issue-4463.test.ts

import { join } from "node:path";
import { eq } from "drizzle-orm";
import { expect, it, vi } from "vitest";
import { createConnection, pendingInteractions, events } from "@bb/db";
import { createTestAppHarness } from "../../helpers/test-app.js";
import { seedEnvironment, seedHostSession, seedProjectWithSource, seedThread } from "../../helpers/seed.js";

it("4463 submits the actual built-in form and reads its description from a reopened database", async () => {
  const harness = await createTestAppHarness();
  try {
    const { host } = seedHostSession(harness.deps, { id: "host-synthetic-form" });
    const { project } = seedProjectWithSource(harness.deps, { hostId: host.id });
    const environment = seedEnvironment(harness.deps, { hostId: host.id, projectId: project.id });
    const thread = seedThread(harness.deps, { projectId: project.id, environmentId: environment.id });
    const installed = await harness.pluginService.install("builtin:ask-user-question", { kind: "root" });
    expect(installed.status).toBe("running");
    const found = harness.pluginService.findAgentTool("AskUserQuestion");
    expect(found).toBeDefined();
    const call = harness.pluginService.invokeAgentTool({
      ...found!, input: { questions: [{ question: "Which display color?", header: "Color", multiSelect: false,
        options: [{ label: "Blue", description: "Blue display" }, { label: "Green", description: "Green display" }] }] },
      ctx: { threadId: thread.id, projectId: project.id, signal: new AbortController().signal },
    });
    await vi.waitFor(() => expect(harness.deps.pendingInteractions.listPendingThreadInteractions(thread.id)).toHaveLength(1));
    const [pending] = harness.deps.pendingInteractions.listPendingThreadInteractions(thread.id);
    const selected = { answers: { q0: { selected: ["q0o0"], freeText: "with large text" } } };
    const submitted = await harness.deps.pendingInteractions.respondToPluginInteraction({
      interactionId: pending!.id, threadId: thread.id, value: selected,
    });
    const toolResult = await call;
    expect(toolResult.success).toBe(true);
    expect(JSON.stringify(toolResult)).toContain("Blue; with large text");
    const read = (db: typeof harness.db) => {
      const row = db.select({ status: pendingInteractions.status, resolution: pendingInteractions.resolution })
        .from(pendingInteractions).where(eq(pendingInteractions.id, pending!.id)).get()!;
      const lifecycle = db.select({ type: events.type, data: events.data }).from(events)
        .where(eq(events.threadId, thread.id)).all().filter(row => row.type === "system/interaction/lifecycle")
        .map(row => JSON.parse(row.data)).at(-1);
      return { status: row.status, resolution: JSON.parse(row.resolution!), lifecycleResolution: lifecycle?.interaction?.resolution };
    };
    const expectedDescription = { title: "Answered Which display color? — Blue; with large text",
      detail: "- Which display color? — Blue; with large text" };
    const direct = read(harness.db);
    expect(direct.status).toBe("resolved");
    expect(direct.resolution).toMatchObject({ kind: "plugin_submitted", description: expectedDescription });
    expect(direct.lifecycleResolution).toMatchObject({ kind: "plugin_submitted", description: expectedDescription });
    expect(submitted.resolution).toEqual(direct.resolution);
    const snapshot = join(harness.config.dataDir, "synthetic-persisted.sqlite");
    await harness.db.$client.backup(snapshot);
    const reopened = createConnection(snapshot);
    let durable;
    try { durable = read(reopened); expect(durable).toEqual(direct); }
    finally { reopened.$client.close(); }
    console.log(JSON.stringify({ phase: "actual-builtin-persistence", toolResult, direct, reopened: durable }));
    const fallback = harness.deps.pendingInteractions.requestPluginInteraction({
      pluginId: "synthetic-no-description", threadId: thread.id, rendererId: "color", title: "Color",
      payload: { colors: ["Blue", "Green"] }, describeSubmission: null, timeoutMs: 10000,
    });
    const [control] = harness.deps.pendingInteractions.listPendingThreadInteractions(thread.id);
    await harness.deps.pendingInteractions.respondToPluginInteraction({ interactionId: control!.id, threadId: thread.id, value: { color: "Blue" } });
    expect(await fallback).toEqual({ outcome: "submitted", value: { color: "Blue" } });
    const controlRow = harness.db.select({ resolution: pendingInteractions.resolution }).from(pendingInteractions)
      .where(eq(pendingInteractions.id, control!.id)).get()!;
    expect(JSON.parse(controlRow.resolution!)).toEqual({ kind: "plugin_submitted" });
    console.log(JSON.stringify({ phase: "no-description-control", resolution: JSON.parse(controlRow.resolution!) }));
  } finally { await harness.cleanup(); }
}, 60000);

Actual evidence and same-agent second clean reproduction

First clean checkout

[
  {
    "phase": "actual-builtin-persistence",
    "toolResult": {
      "success": true,
      "contentItems": [
        {
          "type": "inputText",
          "text": "{\"questions\":[{\"question\":\"Which display color?\",\"header\":\"Color\",\"options\":[{\"label\":\"Blue\",\"description\":\"Blue display\"},{\"label\":\"Green\",\"description\":\"Green display\"}],\"multiSelect\":false}],\"answers\":{\"Which display color?\":\"Blue; with large text\"},\"annotations\":{\"Which display color?\":{\"notes\":\"with large text\"}}}"
        }
      ]
    },
    "direct": {
      "status": "resolved",
      "resolution": {
        "kind": "plugin_submitted",
        "description": {
          "title": "Answered Which display color? \u2014 Blue; with large text",
          "detail": "- Which display color? \u2014 Blue; with large text",
          "payload": {
            "questions": [
              {
                "question": "Which display color?",
                "header": "Color",
                "options": [
                  {
                    "label": "Blue",
                    "description": "Blue display"
                  },
                  {
                    "label": "Green",
                    "description": "Green display"
                  }
                ],
                "multiSelect": false
              }
            ],
            "answers": {
              "Which display color?": "Blue; with large text"
            },
            "annotations": {
              "Which display color?": {
                "notes": "with large text"
              }
            }
          }
        }
      },
      "lifecycleResolution": {
        "kind": "plugin_submitted",
        "description": {
          "title": "Answered Which display color? \u2014 Blue; with large text",
          "detail": "- Which display color? \u2014 Blue; with large text",
          "payload": {
            "questions": [
              {
                "question": "Which display color?",
                "header": "Color",
                "options": [
                  {
                    "label": "Blue",
                    "description": "Blue display"
                  },
                  {
                    "label": "Green",
                    "description": "Green display"
                  }
                ],
                "multiSelect": false
              }
            ],
            "answers": {
              "Which display color?": "Blue; with large text"
            },
            "annotations": {
              "Which display color?": {
                "notes": "with large text"
              }
            }
          }
        }
      }
    },
    "reopened": {
      "status": "resolved",
      "resolution": {
        "kind": "plugin_submitted",
        "description": {
          "title": "Answered Which display color? \u2014 Blue; with large text",
          "detail": "- Which display color? \u2014 Blue; with large text",
          "payload": {
            "questions": [
              {
                "question": "Which display color?",
                "header": "Color",
                "options": [
                  {
                    "label": "Blue",
                    "description": "Blue display"
                  },
                  {
                    "label": "Green",
                    "description": "Green display"
                  }
                ],
                "multiSelect": false
              }
            ],
            "answers": {
              "Which display color?": "Blue; with large text"
            },
            "annotations": {
              "Which display color?": {
                "notes": "with large text"
              }
            }
          }
        }
      },
      "lifecycleResolution": {
        "kind": "plugin_submitted",
        "description": {
          "title": "Answered Which display color? \u2014 Blue; with large text",
          "detail": "- Which display color? \u2014 Blue; with large text",
          "payload": {
            "questions": [
              {
                "question": "Which display color?",
                "header": "Color",
                "options": [
                  {
                    "label": "Blue",
                    "description": "Blue display"
                  },
                  {
                    "label": "Green",
                    "description": "Green display"
                  }
                ],
                "multiSelect": false
              }
            ],
            "answers": {
              "Which display color?": "Blue; with large text"
            },
            "annotations": {
              "Which display color?": {
                "notes": "with large text"
              }
            }
          }
        }
      }
    }
  },
  {
    "phase": "no-description-control",
    "resolution": {
      "kind": "plugin_submitted"
    }
  }
]
 Test Files  1 passed (1)
      Tests  1 passed (1)
   Duration  5.64s (transform 3.34s, setup 1.13s, import 3.51s, tests 859ms, environment 0ms)

Second clean checkout

[
  {
    "phase": "actual-builtin-persistence",
    "toolResult": {
      "success": true,
      "contentItems": [
        {
          "type": "inputText",
          "text": "{\"questions\":[{\"question\":\"Which display color?\",\"header\":\"Color\",\"options\":[{\"label\":\"Blue\",\"description\":\"Blue display\"},{\"label\":\"Green\",\"description\":\"Green display\"}],\"multiSelect\":false}],\"answers\":{\"Which display color?\":\"Blue; with large text\"},\"annotations\":{\"Which display color?\":{\"notes\":\"with large text\"}}}"
        }
      ]
    },
    "direct": {
      "status": "resolved",
      "resolution": {
        "kind": "plugin_submitted",
        "description": {
          "title": "Answered Which display color? \u2014 Blue; with large text",
          "detail": "- Which display color? \u2014 Blue; with large text",
          "payload": {
            "questions": [
              {
                "question": "Which display color?",
                "header": "Color",
                "options": [
                  {
                    "label": "Blue",
                    "description": "Blue display"
                  },
                  {
                    "label": "Green",
                    "description": "Green display"
                  }
                ],
                "multiSelect": false
              }
            ],
            "answers": {
              "Which display color?": "Blue; with large text"
            },
            "annotations": {
              "Which display color?": {
                "notes": "with large text"
              }
            }
          }
        }
      },
      "lifecycleResolution": {
        "kind": "plugin_submitted",
        "description": {
          "title": "Answered Which display color? \u2014 Blue; with large text",
          "detail": "- Which display color? \u2014 Blue; with large text",
          "payload": {
            "questions": [
              {
                "question": "Which display color?",
                "header": "Color",
                "options": [
                  {
                    "label": "Blue",
                    "description": "Blue display"
                  },
                  {
                    "label": "Green",
                    "description": "Green display"
                  }
                ],
                "multiSelect": false
              }
            ],
            "answers": {
              "Which display color?": "Blue; with large text"
            },
            "annotations": {
              "Which display color?": {
                "notes": "with large text"
              }
            }
          }
        }
      }
    },
    "reopened": {
      "status": "resolved",
      "resolution": {
        "kind": "plugin_submitted",
        "description": {
          "title": "Answered Which display color? \u2014 Blue; with large text",
          "detail": "- Which display color? \u2014 Blue; with large text",
          "payload": {
            "questions": [
              {
                "question": "Which display color?",
                "header": "Color",
                "options": [
                  {
                    "label": "Blue",
                    "description": "Blue display"
                  },
                  {
                    "label": "Green",
                    "description": "Green display"
                  }
                ],
                "multiSelect": false
              }
            ],
            "answers": {
              "Which display color?": "Blue; with large text"
            },
            "annotations": {
              "Which display color?": {
                "notes": "with large text"
              }
            }
          }
        }
      },
      "lifecycleResolution": {
        "kind": "plugin_submitted",
        "description": {
          "title": "Answered Which display color? \u2014 Blue; with large text",
          "detail": "- Which display color? \u2014 Blue; with large text",
          "payload": {
            "questions": [
              {
                "question": "Which display color?",
                "header": "Color",
                "options": [
                  {
                    "label": "Blue",
                    "description": "Blue display"
                  },
                  {
                    "label": "Green",
                    "description": "Green display"
                  }
                ],
                "multiSelect": false
              }
            ],
            "answers": {
              "Which display color?": "Blue; with large text"
            },
            "annotations": {
              "Which display color?": {
                "notes": "with large text"
              }
            }
          }
        }
      }
    }
  },
  {
    "phase": "no-description-control",
    "resolution": {
      "kind": "plugin_submitted"
    }
  }
]
 Test Files  1 passed (1)
      Tests  1 passed (1)
   Duration  5.81s (transform 3.49s, setup 1.25s, import 3.58s, tests 852ms, environment 0ms)

The same agent personally executed the final test in both clean checkouts; this is not independent verification. Both tests exited 0 and emitted identical evidence. Preliminary first-checkout setup attempts used an incorrect relative path and then an unsupported installPath call for a reserved built-in. These stopped before form submission and were corrected to the supported builtin installer; they are not counted as bug results. No unsupported installed-version or visual conclusion was carried forward.

Source evidence and precise uncertainty

No root cause for the installed 0.44.0 incident is established. The current trusted implementation does persist the tested built-in description. A source-history observation alone cannot establish ALREADY FIXED, and the bare resolution alone cannot identify which fallback or version mismatch occurred. This report does not test the timeline suppression/rendering claims or retrospectively recover earlier records.

Proposed next test and fix direction

No production fix was applied or justified for the passing current-main path. Next, use an isolated matching desktop release to record whether the built-in callback runs, whether description validation or its two-second deadline fails, and what the API/database returns before testing transcript rendering. If an omission is confirmed, preserve a safe diagnostic reason or correct the callback propagation/validation path. Do not indiscriminately persist raw values from arbitrary plugin forms; the plugin-supplied description is the intended persistence boundary.

Trust and publication checks

All issue prose, commands, logs and suggested fixes were treated as untrusted claims. None of its commands, code, branches, external URLs or real runtime were executed or accessed. This test was derived from trusted repository implementation and existing server tests. All current comments were read (zero before publication); no existing report or linked open PR was found. Public repository visibility, source permalinks, inline evidence consistency and privacy were checked. No assets or external runtime data are needed. Raw setup/build/test logs remain local. The repository-referenced publisher script is absent, so HTML/reference/privacy checks were performed directly; the generated index was not edited.