Repository navigation
Description
Affected: Plane Cloud (app.plane.so), official GitHub integration, bidirectional sync. Severity: silent data loss on GitHub. Issue and comment bodies authored by humans are overwritten by the makeplane bot user. Recoverable only via the GraphQL userContentEdits API; not visible in the GitHub web Edit-history UI as "corruption" because the corrupted version becomes the displayed body.
Summary
Under bidirectional sync, Plane→GH push fires on certain Plane-side write operations and pushes a fresh markdown serialisation of the work item / comment to GitHub. The serialisation is produced by running Plane's stored TipTap HTML through a tiptap-html → markdown converter. The stored HTML has already been corrupted at GH→Plane import time (see sub-issues below) so what gets pushed back to GitHub is a corrupted version of the original human-authored body.
There is no in-product notification that this has happened.
What triggers a Plane→GH push
Empirically observed on Plane Cloud under bidirectional sync.
Pushes that fire:
- POST a Plane comment with
external_source = "GITHUB"and a realexternal_id— pushes the comment body to GH. - PATCH a Plane comment whose
external_source = "GITHUB"— pushes that comment body (corresponding GH comment overwritten ~13-75s later).
Pushes that do NOT fire:
PATCH /work-items/<id>/with priority-only change — tested twice with real priority transitions; no Plane→GH activity in 9 minutes.PATCHwith a no-op value — suppressed when stored state matches GH.- PATCH of a Plane-native comment (no
external_source). - Re-enabling bidir after Plane-side writes under unidir — Plane does not flush a queue on re-enable; pending divergence stays inert until a fresh modify event.
Description-body pushes: reliably reproduce within ~90 seconds of a multi-write sequence that includes one or more comment POSTs under bidir. Isolating individual operations did not reproduce within the same window — trigger isolation incomplete and likely needs internal webhook logs to nail down.
What we'd want fixed
Suppress Plane→GH push when the field's value is unchanged from the most recent GH→Plane import for that field. The integration should not be the source of a userContentEdits revision unless a human has actually edited the Plane copy. MVP: string-equality on serialised markdown.
This single fix would prevent the destructive overwrite even if the converters themselves remain buggy — the field-level corruption is already covered separately in the sub-issues below.
Surface destructive overwrites in the product. Currently the makeplane bot edits flood notifications with no signal that user-authored content has been replaced.
Sub-issues
Each pattern below is a distinct corruption mode confirmed reproducible:
- GitHub
@mentionscorrupted to[From GitHub: user](url)on sync #9071 —@mentionscorrupted to[From GitHub: user](url) - GitHub
#NNNissue/PR refs duplicate and break URL on sync #9072 —#NNNrefs duplicate, URL drops repo owner - GitHub
<img>tags losealt, replaced with auth-gated silo URLs #9073 —<img>tags losealt, replaced with auth-gated silo URLs - GFM tables flattened to sequential
<p>blocks on sync #9074 — GFM tables flattened to sequential<p>blocks - Bare URLs wrapped in
[url](url)self-links on sync #9075 — Bare URLs wrapped in[url](url)self-links - Soft-wrapped paragraphs converted to hard
<p>breaks on sync #9076 — Soft-wrapped paragraphs converted to hard<p>breaks
The corruption originates at GH → Plane import time, not at the Plane → GH push. We verified this by running Plane's open-source tiptap-html → markdown converter (packages/utils/src/editor/markdown-parser/ in makeplane/plane) locally over real description_html dumps from an affected workspace: the converter faithfully reproduces the observed GH-side corruption. The import-side markdown → tiptap-html converter (in the closed makeplane/silo-commercial image) is what stores the already-broken HTML that the serialiser later faithfully emits.
Recovery for affected GitHub bodies
{
repository(owner: "<owner>", name: "<repo>") {
issue(number: <n>) {
userContentEdits(first: 20) {
nodes { editedAt editor { login } diff }
}
}
}
}The oldest diff is the pristine human-authored body. PATCH it back via gh api -X PATCH repos/<owner>/<repo>/issues/<n> -F body=@pristine.md.
Workarounds while waiting for fix
- Run sync unidirectionally (GH → Plane only). Plane stays a downstream view; GH is canonical and stays clean.
- For any required Plane-side writes via the API: flip to unidir before the writes, run them, verify GH was untouched via
userContentEditsaudit, then re-enable bidir. Note re-enabling does not flush a queue, but a subsequent modify event on any affected work item can still trigger a stale push.
Activity
Saurabhkmr98 commentedon Aug 31, 2026
We've fixed this in our latest release. Please try and let us know, meanwhile closing this issue.