mirror of
https://github.com/alkimake/paperclip.git
synced 2026-06-15 10:30:37 +09:00
fix(ui): fix message attribution for agent-posted comments with user author IDs (#5780)
## Thinking Path > - Paperclip’s issue chat is an audit surface: reviewers need to trust who actually authored a message. > - Some historical agent comments were persisted with `authorUserId` and no surviving `createdByRunId`, so the UI rendered real agent output as if it came from the board user. > - A pure timestamp-window fallback is too risky because human reviewers can comment while agents are running. > - The safe recovery path is to derive attribution only when the server can prove it from same-issue run logs that include the exact posted comment id, then let the chat renderer prefer that recovered agent attribution. > - This keeps historical threads trustworthy without mutating old database rows or guessing in ambiguous cases. ## What Changed - Added shared `IssueComment` fields for derived attribution so server and UI can carry recovered `derivedAuthorAgentId`, `derivedCreatedByRunId`, and `derivedAuthorSource` consistently. - Added server-side attribution recovery in `server/src/services/issues.ts` that reads same-issue run logs and only derives agent authorship when a run log contains the exact `comment id: ...` emitted during posting. - Updated issue chat rendering in `ui/src/lib/issue-chat-messages.ts` to prefer direct agent authorship, then activity-log `runAgentId`, then the server-derived attribution. - Removed the unsafe UI-only run-window fallback from `ui/src/pages/IssueDetail.tsx` so human comments posted during an active run are not silently relabeled as agent output. - Added regression coverage for both the run-log derivation path and the chat-rendering fallback behavior. - Bounded server-side run-log enrichment to 8 concurrent reads per request and removed the unused `issueCommentSchema` declaration during PR cleanup. ## Verification - `pnpm exec vitest run ui/src/lib/issue-chat-messages.test.ts server/src/__tests__/issues-service.test.ts` - `pnpm test:run:general` - Live validation on May 12, 2026 in `PAPA-322`: confirmed the previously misattributed historical comments on `PAPA-316` now render as Claude-authored on `http://goldie.gerbil-company.ts.net:3100`. - Reviewer check: open `PAPA-316` in the running instance and confirm historical comments such as `## Investigation: exe.dev 422 + codex re-test` render under Claude instead of the board user. ## Risks - Low risk. The change is scoped to comment attribution recovery and rendering. - Derived attribution is intentionally conservative: if there is no exact run-log proof, the comment remains user-authored instead of guessing. - Run-log recovery depends on retained same-issue logs, so older comments without that evidence remain unchanged. ## Model Used - OpenAI Codex via the Paperclip `codex_local` adapter (GPT-5-class coding agent with tool use in the local Paperclip runtime; the exact deployment/model ID is not surfaced by this workspace). ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [ ] If this change affects the UI, I have included before/after screenshots - [ ] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Paperclip <noreply@paperclip.ing>
This commit is contained in:
parent
9746dab4e8
commit
c445e59256
5 changed files with 379 additions and 17 deletions
|
|
@ -24,7 +24,12 @@ import {
|
|||
startEmbeddedPostgresTestDatabase,
|
||||
} from "./helpers/embedded-postgres.js";
|
||||
import { instanceSettingsService } from "../services/instance-settings.ts";
|
||||
import { clampIssueListLimit, ISSUE_LIST_MAX_LIMIT, issueService } from "../services/issues.ts";
|
||||
import {
|
||||
clampIssueListLimit,
|
||||
deriveIssueCommentRunLogAttribution,
|
||||
ISSUE_LIST_MAX_LIMIT,
|
||||
issueService,
|
||||
} from "../services/issues.ts";
|
||||
import { buildProjectMentionHref, MAX_ISSUE_REQUEST_DEPTH } from "@paperclipai/shared";
|
||||
|
||||
const embeddedPostgresSupport = await getEmbeddedPostgresTestSupport();
|
||||
|
|
@ -38,6 +43,69 @@ describe("issue list limit helpers", () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe("deriveIssueCommentRunLogAttribution", () => {
|
||||
it("recovers agent attribution from run logs that printed the posted comment id", () => {
|
||||
const commentId = randomUUID();
|
||||
const runId = randomUUID();
|
||||
const agentId = randomUUID();
|
||||
|
||||
const derived = deriveIssueCommentRunLogAttribution(
|
||||
[
|
||||
{
|
||||
id: commentId,
|
||||
authorAgentId: null,
|
||||
authorUserId: "user-1",
|
||||
createdByRunId: null,
|
||||
createdAt: new Date("2026-05-11T18:55:40.090Z"),
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
runId,
|
||||
agentId,
|
||||
createdAt: new Date("2026-05-11T18:51:56.246Z"),
|
||||
startedAt: new Date("2026-05-11T18:51:56.257Z"),
|
||||
finishedAt: new Date("2026-05-11T18:55:45.600Z"),
|
||||
logContent: `comment id: ${commentId}\n`,
|
||||
},
|
||||
],
|
||||
);
|
||||
|
||||
expect(derived.get(commentId)).toEqual({
|
||||
derivedAuthorAgentId: agentId,
|
||||
derivedCreatedByRunId: runId,
|
||||
derivedAuthorSource: "run_log_comment_post",
|
||||
});
|
||||
});
|
||||
|
||||
it("does not rewrite comments without exact run-log proof", () => {
|
||||
const commentId = randomUUID();
|
||||
const derived = deriveIssueCommentRunLogAttribution(
|
||||
[
|
||||
{
|
||||
id: commentId,
|
||||
authorAgentId: null,
|
||||
authorUserId: "user-1",
|
||||
createdByRunId: null,
|
||||
createdAt: new Date("2026-05-11T18:55:40.090Z"),
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
runId: randomUUID(),
|
||||
agentId: randomUUID(),
|
||||
createdAt: new Date("2026-05-11T18:51:56.246Z"),
|
||||
startedAt: new Date("2026-05-11T18:51:56.257Z"),
|
||||
finishedAt: new Date("2026-05-11T18:55:45.600Z"),
|
||||
logContent: "posted results without echoing the comment id",
|
||||
},
|
||||
],
|
||||
);
|
||||
|
||||
expect(derived.has(commentId)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
async function ensureIssueRelationsTable(db: ReturnType<typeof createDb>) {
|
||||
await db.execute(sql.raw(`
|
||||
CREATE TABLE IF NOT EXISTS "issue_relations" (
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue