Skip to content

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

Closed

Description

@dezren39

Problem

In pkgs/id's web markdown codec, a delimiter-mark span (em, strong, strikethrough) whose text starts or ends with a space loses its mark on the next open whenever a word character touches the span on that same side. The save emits the   entity form inside the delimiters (e.g. a* b*), and that form cannot reparse as emphasis: the delimiter run is followed by & (punctuation, the entity start) and preceded by a word character, so it fails CommonMark's flanking rules for opening emphasis. On reopen the mark is gone and the delimiters remain as literal text — a* b* — and a second save escapes them (a\* b\*). The codec's own output is not readable by its own parser for these shapes.

Verified on main 37418e56 with a verbatim-codec probe crate (comrak 0.56.0, compiled and run on the laptop; hidden_files/probe-2026-10-27-0927-laneA-out4.log). PM documents below are editor shapes (a user selects a space-leading word mid-sentence and italicizes it):

PM paragraph Saved After reopen
a + em( b) a* b* plain text a* b* — mark destroyed, literal *s remain
a + strong( b) a** b** plain text a** b**
a + strikethrough( b) a~~ b~~ plain text (same failure)
a + em(b ) + c a *b *c plain text a *b *c
a + strong+em( b) (same   form) mark destroyed
heading a + em( b) # a* b* mark destroyed (same)

Controls in the same probe round-trip PM-identical and byte-fixpoint, which pins the trigger to the outside neighbor on the space-carrying side: span at paragraph start/end, neighbor is a space or punctuation, or the span is padded on both edges with spaced neighbours (a * b * c survives). An authored-markdown control confirms the mechanism at the parser level: the literal markdown a* b* parses to plain text, while a * b* (space before the delimiter) parses to em( b).

Root cause

Save direction, pkgs/id/src/web/markdown.rs: create_marked_text (:1737) builds the mark wrappers (em/strong/strikethrough arms :1762-1764) around the run's full text, keeping a leading/trailing space inside the wrapper; comrak's format_commonmark then renders that edge space as an   entity inside the delimiters. Nothing in the codec checks whether the emitted delimited form can reparse — with a word character immediately outside the delimiter, it cannot (flanking rules above), so the first save writes a file the editor itself will misread on the next open.

Why it matters

The shape is ordinary editor output: select b (with its leading space) inside a sentence and press Ctrl+I, or apply bold to the last word of a sentence including the space before it. The loss is silent, destroys the formatting, and injects literal */~~ characters into the visible text; because the result is not a fixpoint, later saves compound it with backslash escapes. Siblings #1407/#1412 also leak   entities but have different root causes (marks around an image node; marks spanning html_inline nodes) — this one is a single flat span with no other nodes involved.

Acceptance criteria

Related: #1407, #1412 (different   mechanisms), #968 (nested-mark fragmentation, closed), #981 (softbreak-crossing spans, closed).

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/normalOrdinary priority (the default)
    app/idPackage/app: id (pkgs/ or apps/ in this repo)
    effort/sSmall: expected under 2 hours of work
    on Oct 27, 2026
  3. dezren39 commented on Oct 27, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · filed

  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. 65 remaining items

  6. added
    status/parkedDeliberately paused with a recorded resume condition
    and removed
    queue/claimedQueue worker lifecycle state
    on Oct 28, 2026
  7. added
    queue/claimedQueue worker lifecycle state
    and removed
    status/parkedDeliberately paused with a recorded resume condition
    queue/parkedQueue task is parked (waiting on CI, time, or an event)
    on Oct 28, 2026
  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/normalOrdinary priority (the default)source/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