Skip to content

id(web): edge whitespace in text runs is written on save but stripped on reopen — a whitespace-only paragraph is deleted outright #1420

Closed

Description

@dezren39

Problem

In pkgs/id's web markdown codec, whitespace at the edges of a text run — state the rich editor readily holds — is written to the saved file but cannot survive the codec's own reparse. CommonMark strips up to three leading spaces on a paragraph line and ignores trailing spaces, so on the next open the spaces are gone. The worst case is structural: a paragraph whose only content is spaces is deleted outright on reopen, collapsing the document's block structure.

Verified on main 37418e56 with a verbatim-codec probe crate (comrak 0.56.0, compiled and run on the laptop; PM-first cases in hidden_files/probe-2026-10-27-0927-laneA-out2.log + out3.log):

PM document Saved file After reopen
paragraph leading spaces leading spaces leading spaces — 2 spaces lost
paragraph foo (trailing) foo foo — trailing spaces lost
paragraphs [ , x] + blank + x only x — the whitespace-only paragraph is gone
foo + hard_break + bar foo\ + newline + bar foo + hard_break + bar — spaces after the break lost
table header A, cell x | A | / | x | A / x — cell padding lost

All five are non-fixpoint across a full open→save→open cycle (the second save differs from the first: e.g. leading spaces → leading spaces; the space-only case loses a whole block). Note the saved bytes look correct — the loss happens on reopen, because the codec emits plain spaces for state markdown cannot represent (compare the mark-wrapper path, which protects edge spaces with   entities — no equivalent protection exists for unmarked run edges).

Why it matters

Users typing in the rich editor create this state without doing anything exotic: indenting a paragraph's first line with spaces (a common writing habit), trailing spaces, or an empty spacer paragraph made of spaces. The editor shows one document, the file on disk appears to match it, and the next open silently shows a different one — in the spacer-paragraph case with a block deleted. Even where the resolution is "markdown cannot represent this, normalize it away", that normalization should happen at save (visibly, as a fixpoint) rather than as a silent reopen-time loss, and the whitespace-only paragraph deserves an explicit decision (map to an empty paragraph representation? drop at save?) instead of the current emergent deletion.

Acceptance criteria

  • A policy is picked and pinned for edge whitespace in unmarked text runs (paragraph start/end, after hard_break, table cells): either preserved across open→save→open, or normalized at save so the first saved file is already a byte fixpoint and the editor-visible result is the documented one.
  • The whitespace-only paragraph case is explicitly decided: it must not silently vanish on reopen — either it round-trips (e.g. as an empty paragraph) or the save direction drops/normalizes it so file and reopen agree from the first save.
  • The five probe cases above are covered by regression tests in pkgs/id/src/web/markdown.rs; existing web::markdown tests pass, and the   protection for marked spans (cf. id(web): a marked span starting or ending with a space loses its mark on reopen when a word character touches it — saved   form cannot reparse #1418) is unaffected.

Related: #1418 (the marked-span counterpart — edge spaces inside delimiters), #1045 (unknown-mark policy — same pick-and-pin-a-policy shape), #1307 (.txt byte-faithfulness, different mode).

Activity

  1. developing-today commented on Oct 27, 2026

    @developing-today
    Contributor
  2. added
    kind/bugSomething isn't working (prefixed form of the classic bug label)
    queue/queuedHas a queued task in the Supabase agent queue
    source/creatorFiled by the new-issue-creator job
    task/triageQueue packet kind: verify, reproduce, and route this item
    priority/lowNice to have; do when convenient
    app/idPackage/app: id (pkgs/ or apps/ in this repo)
    on Oct 27, 2026
  3. dezren39 commented on Oct 27, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · filed

    • Agent: new-issue-creator (lane A, new-issue-creator-code-a)
    • Session / ID: cron new-issue-creator-code-a, run 2026-10-27 09:27 CDT
    • Found via: filed this run
    • Task: Triage the edge-whitespace loss: unmarked text-run edge spaces (paragraph start/end, after hard_break, table cells) are written on save but stripped on reopen; a whitespace-only paragraph is deleted outright. Pick and pin a policy (preserve, or normalize at save to a fixpoint) with the whitespace-only paragraph explicitly decided.
    • Expectation: effort/s; done = the five probe cases behave per the pinned policy across open→save→open with regression tests, no regression to marked-span handling (id(web): a marked span starting or ending with a space loses its mark on reopen when a word character touches it — saved   form cannot reparse #1418).
    • Links: evidence probe (laptop, verbatim codec on main 37418e5): hidden_files/probe-2026-10-27-0927-laneA-out2.log + out3.log; body: hidden_files/issue-code-id-edge-whitespace-body.md
  4. added
    work/assignedQueue worker lifecycle state
    queue/claimedQueue worker lifecycle state
    and removed
    queue/queuedHas a queued task in the Supabase agent queue
    on Oct 27, 2026
  5. 54 remaining items

  6. added
    queue/parkedQueue task is parked (waiting on CI, time, or an event)
    status/parkedDeliberately paused with a recorded resume condition
    queue/claimedQueue worker lifecycle state
    and removed
    queue/claimedQueue worker lifecycle state
    status/parkedDeliberately paused with a recorded resume condition
    queue/parkedQueue task is parked (waiting on CI, time, or an event)
    on Oct 28, 2026
  7. dezren39 commented on Oct 28, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · picked up

  8. dezren39 commented on Oct 28, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · done

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

app/idPackage/app: id (pkgs/ or apps/ in this repo)effort/sSmall: expected under 2 hours of workkind/bugSomething isn't working (prefixed form of the classic bug label)priority/lowNice to have; do when convenientsource/creatorFiled by the new-issue-creator jobstatus:donetask/triageQueue packet kind: verify, reproduce, and route this item

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions