Skip to content

fix(docs): markdown 3+ tables drift placeholder offset by one per table#613

Open
chrischall wants to merge 2 commits into
openclaw:mainfrom
chrischall:fix/607-multi-table-index-drift
Open

fix(docs): markdown 3+ tables drift placeholder offset by one per table#613
chrischall wants to merge 2 commits into
openclaw:mainfrom
chrischall:fix/607-multi-table-index-drift

Conversation

@chrischall
Copy link
Copy Markdown

Closes #607.

Root cause

The per-table offset accumulator in both replaceDocsMarkdownRange and insertDocsMarkdownAt:

if tableEnd > tableIndex {
    tableOffset += (tableEnd - tableIndex) - 1
}

assumed the placeholder "\n" we wrote into plainText for each table position was consumed by the InsertTable call. Empirically that placeholder remains in the doc — InsertTable inserts the new table BEFORE the existing character at the requested index, so every subsequent placeholder shifts forward by the full (tableEnd - tableIndex), not (tableEnd - tableIndex) - 1.

That -1 accumulated one missing character of drift per table:

Table Cumulative drift
1 0
2 1
3 2

Why the issue only became visually obvious at the 3rd table: with 1 char of drift, table 2 lands on the blank line between paragraph and table, which is itself a newline — the doc still parses, you just lose the visual gap. With 2 chars of drift, table 3 lands inside the preceding paragraph's terminator/punctuation, splitting three.\n so the period becomes a standalone paragraph after the table.

Fix

Extracted the formula into a small helper:

func nextTableInsertOffset(currentOffset, tableIndex, tableEnd int64) int64 {
    if tableEnd <= tableIndex {
        return currentOffset
    }
    return currentOffset + (tableEnd - tableIndex)
}

and replaced both inline call sites with tableOffset = nextTableInsertOffset(tableOffset, tableIndex, tableEnd). The helper lives next to the rest of the table-insertion machinery in docs_table_inserter.go.

Tests

  • TestNextTableInsertOffset_AccumulatesFullTableSize — exercises a 3-table sequence with hand-computed expected offsets and explicitly calls out the previous buggy values in the failure messages.
  • TestNextTableInsertOffset_ZeroSizedTableLeavesOffsetUnchanged — guards the tableEnd <= tableIndex short-circuit (previously hidden inside an if).
  • TestNextTableInsertOffset_MatchesIssueRepro — parameterised over any table size G, asserts N tables of size G accumulate to NG (previous formula gave N(G-1)).

go test ./internal/cmd/ -count=1 → ok 23.8s.

chrischall and others added 2 commits May 19, 2026 20:08
Closes openclaw#607.

The per-table offset accumulator in replaceDocsMarkdownRange and
insertDocsMarkdownAt added `(tableEnd - tableIndex) - 1` after each
table, on the theory that the placeholder "\n" we wrote into plainText
was consumed by the InsertTable call. Empirically the placeholder
remains in the doc, so each subsequent table inserted one character too
early — invisible for table 2 (the placeholder it shifted onto became
its own leading newline), but for table 3 the off-by-2 drift landed
mid-paragraph, splitting the preceding paragraph's trailing punctuation
into a standalone paragraph after the table.

Extracted the formula into `nextTableInsertOffset(currentOffset,
tableIndex, tableEnd)` which accumulates the full (tableEnd -
tableIndex) and updated both call sites. The helper is directly unit-
tested against the issue repro shape.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

docs write --append --markdown: 3rd+ table in single call reorders trailing punctuation of preceding paragraph

1 participant