Skip to content

Markdown list bullets are lossy through the skill-guidance seed/render round-trip #1974

Closed

Description

@malibio

Summary

Surfaced during review of #1972 (issue #1965). Out of scope there, captured here while the diagnosis is fresh.

#1965 fixed JSON examples being corrupted through the skill-guidance round-trip (unfenced JSON lost all indentation). The same round-trip is also lossy for markdown list bullets, and this one is arguably the bigger prompt-fidelity loss because it affects far more of the guidance text.

The defect

Source (packages/agent/src/skill_pipeline.rs, Research & Search guidance):

- Use 'collection' to narrow search to a namespace/folder (e.g. collection="Architecture").
- Use 'node_types' to filter by type (e.g. node_types=["task"]) — prefer over 'collection' for type-based filtering.

What the model actually receives after seed → render_skill_instructions → flatten_subtree_content:

Use 'collection' to narrow search to a namespace/folder (e.g. collection="Architecture").

Use 'node_types' to filter by type (e.g. node_types=["task"]) — prefer over 'collection' for type-based filtering.

Two losses:

  1. The - marker is stripped, so the text no longer reads as a list.
  2. Each bullet becomes its own node, and flatten_subtree_content joins nodes with \n\n — so a tight list renders as loose paragraphs.

Why it happens

prepare_nodes_from_markdown stores a bullet's content without its - marker (bullet-ness is carried by node structure, not the content string). The read path (flatten_subtree_content, packages/core/src/services/node_service/hierarchy.rs:959) re-emits node.content verbatim and joins with a blank line — it has no notion of re-deriving list markers or tight vs. loose spacing.

Contrast with code-block, which round-trips losslessly precisely because its fence markers are preserved inside content.

Scope

This affects every guidance block that uses bullets — Research & Search (parameter guidance, listing/filter examples), Schema Creation (title_template examples), Graph Editing (ambiguity examples), and the TARGET_TYPE_MUST_EXIST rule's examples. It is not specific to any one skill.

Possible directions (not yet decided)

  • Have flatten_subtree_content's consumer re-derive list markers from node type/structure — but that function is generic and used beyond skills, so changing it has blast radius.
  • Give the skill-render path its own markdown serializer that reconstructs list syntax, rather than reusing the flat content join.
  • Or accept the flattening and stop authoring guidance as lists.

Worth measuring before choosing: guidance wording is load-bearing for agent behavior, so any of these changes the model-facing prompt for every skill and should be evaluated, not assumed neutral.

Related

Activity

  1. mstomar125 commented on Sep 12, 2026

    @mstomar125
    Contributor

    Status-confirmation (2026-09-12): the defect is still present and unfixed against current main.

    • packages/agent/src/skill_pipeline.rs:368-369 still authors the exact bulleted guidance quoted in the issue.
    • prepare_nodes_from_markdown (packages/core/src/markdown/mod.rs:530-531) still strips the - marker into content_line without recording bullet-ness anywhere else.
    • flatten_subtree_content (now packages/core/src/services/node_service/hierarchy.rs:1077, moved from the issue's cited :959) still re-emits node.content verbatim with no marker reconstruction.
    • Its only caller, render_skill_instructions (packages/core/src/ops/skill_ops.rs:76), still does .join("\n\n") — the tight-list-becomes-loose-paragraphs behavior described in the issue is confirmed unchanged.

    Ownership check per this issue's scope note: this is core-owned code (hierarchy.rs, skill_ops.rs, markdown/mod.rs all under packages/core) — not packages/agent or packages/skill territory. packages/agent/src/skill_pipeline.rs supplies only the guidance content; the lossy rendering logic lives entirely in packages/core. So no sign-off gate applies here.

    That said, the issue is right that this isn't a safe one-liner: flatten_subtree_content is generic and used beyond skills (real blast radius), and each of the three listed directions changes model-facing prompt text for every skill — which the issue itself correctly flags as needing measurement, not an assumed-neutral fix. Not forcing that implementation here. Leaving open in Backlog with this confirmation.

  2. malibio commented on Sep 28, 2026

    @malibio
    CollaboratorAuthor

    Frontend: 280 files / 5225 tests passed

  3. self-assigned this
    on Sep 28, 2026
  4. added a commit that references this issue on Sep 28, 2026
    687841f
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions