Skip to content

[comp] Production Deploy - #3536

Closed
github-actions[bot] wants to merge 2 commits into
releasefrom
main
Closed

[comp] Production Deploy#3536
github-actions[bot] wants to merge 2 commits into
releasefrom
main

Conversation

@github-actions

@github-actions github-actions Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

This is an automated pull request to release the candidate branch into production, which will trigger a deployment.
It was created by the [Production PR] action.


Summary by cubic

Fixes policy content writes that were saving empty blocks and ensures drafts stay in sync so publishing doesn’t revert edits. Adds transaction row locks and a single lock order to prevent deadlocks and race conditions. Addresses Linear CS-722.

  • Bug Fixes
    • Preserve TipTap nodes by reading raw obj.content in UpdateVersionContentDto to prevent [[],[],...] serialization.
    • On draft policy updates, write non-empty edits to both content and draftContent to avoid phantom “unpublished changes” and publish reverts.
    • In updateVersionContent, lock the policy row, reject edits to published/pending versions, and mirror edits into Policy.draftContent (and Policy.content when editing the live draft).
    • Use a consistent lock/order in publishes and approvals: update Policy before PolicyVersion to avoid Postgres deadlocks.
    • Added focused regression tests for serialization, draft syncing, publish-without-version behavior, and lock ordering.

Written for commit c3ffd85. Summary will update on new commits.

Review in cubic

github-actions Bot and others added 2 commits July 29, 2026 01:57
* fix(in-progress): [bug] mcp issue - serialization bug

## Problem
CloudconPTY Ltd

Client is attempting to update policy via MCP, however claude ran into an issue and points to a serialization bug in Comp AI:

"Retrying now with the same content.The call went through this time — but something's wrong. Look at what came back: the `content` field saved as **20 empty arrays** (`[[],[],...]`) instead of my 20 content nodes. The write landed (the `updatedAt` timestamp changed to just now), but the content didn't serialize correctly — the policy body may now be blank rather than holding the new text.

Let me verify exactly what's stored before doing anything else.This didn't work either, and I need to be upfront: the retry returned successfully but **corrupted the content instead of saving it.** Here's the actual state I just confirmed by re-reading the policy:

* The policy's `content` (its live body) is now **20 empty blocks** — effectively blank.
* The `currentVersion` content is also blank.
* The good news: your **original text is still intact** in the policy's `draftContent` field, untouched.

What's happening is a serialization bug in the Comp AI MCP write path: when I send the structured policy body, each content block arrives as an empty array `[]` rather than the actual heading/paragraph. So the write "succeeds" but lands empty. This is why I don't want to keep firing it — every attempt through this tool will keep blanking the `content` field the same way, and it won't ever land the real text. It's not something retrying fixes.

Because the policy was never published and is still a draft, the original wording surviving in `draftContent` means you likely haven't lost anything the editor shows — but I won't guarantee that, and importantly: **don't publish this policy until the body is restored**, or you'd publish a blank version.

Given the write path is broken, the reliable way to get the rewrite in is to paste it into the Comp AI policy editor directly, bypassing the integration entirely. The finished text is in my earlier message — I can re-send it as clean copy-pasteable text if that's easier.

How would you like to proceed?

1. I re-send the rewritten policy as plain text for you to paste into Comp AI's editor (surest fix — gets the correct new content in and overwrites the blank body).
2. I attempt to write the *original* text back into `content` so it's not blank — but I expect this to hit the exact same bug and stay empty, so I'd only do it if you want to confirm the behavior.
3. You raise the serialization issue with Comp AI support, since their MCP write endpoint is mangling structured content.

I'd recommend option 1. Want me to re-post the text?"

--- ticket comments (read the WHOLE thread and use the LATEST reconciled understanding — customers/CX often correct themselves, e.g. "my mistake, it works in chrome"; scope the bug to what is actually CONFIRMED now, not an earlier claim that was later retracted) ---
[redacted-email]: https://linear.app/compai/profiles/paul

hey so i re-checked the serialization bug you mention and it's actually already fixed in the current code we added a Transform decorator on the content field in the DTO a while back that prevents the empty array issue. the pipe that was causing `[[],[],...]` doesn't touch it anymore.

that said the customer now says claude doesn't see trycomp tools at all and can't connect to the MCP server, which sounds different from the content blank thing. i can't tell from the code if that's a real issue or maybe they just need to restart the claude app or reconnect. can you ask them: do they see the trycomp tools listed when they first open claude, or is it blank from the start? and when they try to use it what error do they get exactly something in the claude app or does it just hang? that'll help me figure out if it's mcp connection or something else
Unthread: Brendan Leung: I tried generating a new API key to use already as well​‌​
Unthread: Brendan Leung: I was using the Claude desktop app and tried starting a new chat and asking something like “list compliance tasks” which worked initially but encountered the first issue with the serialisation. Now, it doesn’t recognise anything from trycomp.​‌​
Unthread: Paul: The error here is often erroneous - are you using Claude Code? Can you go into the session and try to begin working?​‌​
Unthread: Brendan Leung: Hi <@U0936FZ6TFB|Paul>, any update on this? We're trying to update these policies ASAP​‌​
Unthread: Paul: Hi <@U07ET6LPSHK|Brendan Leung> just checking this​‌​
Unthread: Brendan Leung: Hey <@U0A64NBGY3S|Mark>, trying again by reconnecting to MCP server but seems like there's an issue connecting now - is this a known issue at the moment?​‌​
[redacted-email]: https://linear.app/compai/profiles/paul

Fix merged. Covers exactly what Brendan described.

**What was broken:** The API's validation pipeline was silently coercing each TipTap content node (heading, paragraph, etc.) into an empty array `[]` before saving. So a 20-node policy body became 20 empty arrays write "succeeded" but content was blank. The MCP path hit this the same as any other API caller.

**What the fix does:** Reads the raw content from the original request before that coercion runs. Content nodes now survive the pipeline intact.

**Does it cover their report?** Yes, exactly. The symptom they described `[[],[],...]` instead of actual content nodes is precisely what this fix corrects.

---

**What to tell Brendan:** The fix is shipped. He can retry updating his policy via MCP now content should save correctly. He'll want to verify the current policy body first (it may still have the 20 blank blocks from the earlier failed write) and re-send the correct content. His `draftContent` was untouched, so he can use that as the source if he needs the original text.
Unthread: Mark: Hey <@U07ET6LPSHK|Brendan Leung>, i have noticed that there is a comment from Engineering stating they have fixed the serialisation issue, are you able to try again and let me know?​‌​
Unthread: Mark: Hey <@U07ET6LPSHK|Brendan Leung>, this is currently in review with Engineering, so has made progress. I will follow up now with them to see if we can get this turned around today.​‌​
Unthread: Brendan Leung: Hey <@U0936FZ6TFB|Paul> any update on this issue?​‌​
[redacted-email]: fixed the serialization issue in the mcp write path for policy updates content was arriving empty because the structured blocks werent being serialized correctly before saving we patched it and tested it working now customer can use the integration again to update their policy
#3388
Unthread: Linked to Unthread ticket:
> [AI assistant edits resulted in incomplete sentences and incorrect formatting, unable to restore previous version #11108](https://compai.unthread.io/dashboard/inbox/all/conversations/f6dffd3d-3864-4027-bb97-10074eb8ea1c)
[redacted-email]: This comment thread is synced to a corresponding [thread in Slack](https://comp-ai-workspace.slack.com/archives/C09CV8JCGKD/p1783667450211219). All replies are displayed in both locations.

--- PREVIOUS FIX ATTEMPTS (this ticket was reopened 2x) ---
attempt 1: merged (PR #3388)
  came back because: Customer reports the serialization bug is still occurring when using Claude desktop app to ask for compliance tasks, and now the app doesn't recognize trycomp at all.
attempt 2: reopened but could not confirm: The serialization defect is real and precisely located in our code: main.ts:127-134 runs the global ValidationPipe with enableImplicitConversion, so class-transformer reads the reflected Array design-type of `content: unknown[]` and instantiates each TipTap node as `new Array()` with string keys, which Prisma/JSON serializes as `[]` — the customer's `[[],[],...]`. Current main already defeats it at create-policy.dto.ts:98 (`@Transform(({ obj }) => obj.content)`, inherited by UpdatePolicyDto via PartialType; I ran the existing spec against the identical pipe options and it passes), and I swept mechanism-first for residual paths: the only other policy-content write bypasses the pipe via `@Req().body` (policies.controller.ts:1268), org-chart takes `@Body() Record<string, unknown>` (native metatype → ValidationPipe skips), and every other object-array DTO field uses `@Type()` or a union type that reflects as Object — so no live path can still blank persisted JSON. The symptom actually live per the LATEST thread message ("reconnecting to MCP server but seems like there's an issue connecting now" / Claude sees no trycomp tools) is a different, runtime connect/tool-discovery claim I cannot prove from static code, and its only levers are the generated+published apps/mcp-server (never-touch) or the MCP OAuth/mcpOrgBinding path in hybrid-auth.guard.ts (§4 auth) — needs real run/log evidence, not a code guess.
  came back because: The customer reports the MCP server connection is still not working after the fix.
--- A previous fix for this ALREADY SHIPPED (merged to main) and the ticket came back, so the shipped fix is INSUFFICIENT. Do NOT conclude "already fixed" just because the code already contains that change — it is present and still not enough. Re-investigate the root cause, find the remaining gap the earlier attempt missed, and fix THAT. ---

## Fix
[Bug] MCP issue - serialization bug

## Verification
✅ added a regression test; suite green locally

* fix(in-progress): address cubic review

Address review findings.

* fix(in-progress): address cubic review

Address review findings.
@vercel

vercel Bot commented Jul 29, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
comp-framework-editor (staging) Ready Ready Preview, Comment Jul 29, 2026 1:28pm
2 Skipped Deployments
Project Deployment Actions Updated (UTC)
app (staging) Skipped Skipped Jul 29, 2026 1:28pm
portal (staging) Skipped Skipped Jul 29, 2026 1:28pm

Request Review

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 4 files

Confidence score: 3/5

  • In apps/api/src/policies/policies.service.ts, updating a draft with content: [] appears to keep stale draftContent while persisting empty content, so the no-versionId publish path can publish old text instead of the intended cleared policy—treat explicit empty arrays as a real draft update (clear draftContent) and add a regression test for this publish flow.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/api/src/policies/policies.service.ts">

<violation number="1" location="apps/api/src/policies/policies.service.ts:420">
P2: Clearing a draft policy's content with an explicit empty array leaves `draftContent` on the previous text. The same update then persists `content: []`, while the no-`versionId` publish path prefers non-empty `draftContent` over `content`, so publishing after a clear can restore the old policy body instead of preserving the requested empty document. Since `content` is array-validated but not non-empty-validated, this branch should either preserve the explicit empty array in `draftContent` or reject it before writing; it should not silently leave the two policy fields inconsistent.

(Based on your team's feedback about preserving explicitly saved empty arrays.) [FEEDBACK_USED]</violation>
</file>

Reply with feedback, questions, or to request a fix.

Fix all with cubic | Re-trigger cubic

Comment on lines +420 to +422
if (contentValue.length > 0) {
updatePayload.draftContent = contentValue;
}

@cubic-dev-ai cubic-dev-ai Bot Jul 29, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Clearing a draft policy's content with an explicit empty array leaves draftContent on the previous text. The same update then persists content: [], while the no-versionId publish path prefers non-empty draftContent over content, so publishing after a clear can restore the old policy body instead of preserving the requested empty document. Since content is array-validated but not non-empty-validated, this branch should either preserve the explicit empty array in draftContent or reject it before writing; it should not silently leave the two policy fields inconsistent.

(Based on your team's feedback about preserving explicitly saved empty arrays.)

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/api/src/policies/policies.service.ts, line 420:

<comment>Clearing a draft policy's content with an explicit empty array leaves `draftContent` on the previous text. The same update then persists `content: []`, while the no-`versionId` publish path prefers non-empty `draftContent` over `content`, so publishing after a clear can restore the old policy body instead of preserving the requested empty document. Since `content` is array-validated but not non-empty-validated, this branch should either preserve the explicit empty array in `draftContent` or reject it before writing; it should not silently leave the two policy fields inconsistent.

(Based on your team's feedback about preserving explicitly saved empty arrays.) </comment>

<file context>
@@ -410,6 +410,16 @@ export class PoliciesService {
+        // (POST :id/versions/publish with no versionId, which snapshots
+        // draftContent) revert the content that was just written. An empty
+        // array carries no text, so it must never wipe the stored draft.
+        if (contentValue.length > 0) {
+          updatePayload.draftContent = contentValue;
+        }
</file context>
Suggested change
if (contentValue.length > 0) {
updatePayload.draftContent = contentValue;
}
updatePayload.draftContent = contentValue;
Fix with cubic

@tofikwest tofikwest closed this Jul 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant