Wiki link markup shows as raw text wherever a recap or note is drawn outside the wiki #1135

Closed
opened 2026-10-12 15:37:12 +00:00 by claude-bot · 1 comment
Collaborator

Found by the owner on dev (v4.5.0-rc11), 2026-10-12, while re-checking lane 11: This week → Previously on, in The Amberwake Accord, reads "The undercroft stair under The sea wall ended at the gate the The Tidewatch Ledger calls the first…". The owner: "I'm noticing wiki formatting artifacts in other pages."

Cause

A session's summary can carry wiki links ([[Title]], [[Title|shown text]], [[Title#Section|shown text]]): the demo's recaps are written with them (webapp/backend/demo/CONTENT.md: "Wiki links welcome"), and a GM can type them. Nothing outside the wiki renders or removes the markup:

  • components/session/PreviouslyOn.jsx (This week and the session page), pages/table/ColdOpen.jsx, pages/table/PlayersTable.jsx draw the recap's paragraphs as plain strings.
  • The session's Read tab does too (pages/session/ReadView.jsx, Summary).
  • On dev the markup is in 42 sessions.summary rows, 64 summary versions, 6 session notes (10 versions) and 22 pages' sidebar_fields, besides the wiki bodies where it belongs.

So every place that shows a summary, a note or an excerpt of a wiki body as plain text is suspect: Home's "Last time" line, the Sessions list's recap line, Storyline, search snippets, the bell, the wiki front page's feed, Prep's "where we left", and what leaves the app (the approved-summary Discord post, the recap email, the bot's /recap).

Approach

  1. One helper on each side that turns the markup into its shown text ([[T|label]] → label, [[T]] → T, [[T#S|l]] → l, an escaped \[[ left as typed), built on the existing parsers (wiki/wikiText.js, services/wiki_links.py), not a second regex.
  2. Apply it at every place that draws such text as plain text, in the app and outbound; find them by sweeping, not from the list above alone.
  3. Tests per surface, and a journey assertion that no page of the seeded campaign shows [[.

Whether a recap's links should be real links to the wiki (for the readers who may see the page) is a design question for the owner, not part of this fix.

Found by the owner on dev (v4.5.0-rc11), 2026-10-12, while re-checking lane 11: This week → Previously on, in The Amberwake Accord, reads "The undercroft stair under [[The sea wall|the sea wall]] ended at the gate the [[The Tidewatch Ledger|ledger]] calls the first…". The owner: "I'm noticing wiki formatting artifacts in other pages." ## Cause A session's summary can carry wiki links (`[[Title]]`, `[[Title|shown text]]`, `[[Title#Section|shown text]]`): the demo's recaps are written with them (`webapp/backend/demo/CONTENT.md`: "Wiki links welcome"), and a GM can type them. Nothing outside the wiki renders or removes the markup: - `components/session/PreviouslyOn.jsx` (This week and the session page), `pages/table/ColdOpen.jsx`, `pages/table/PlayersTable.jsx` draw the recap's paragraphs as plain strings. - The session's Read tab does too (`pages/session/ReadView.jsx`, `Summary`). - On dev the markup is in 42 `sessions.summary` rows, 64 summary versions, 6 session notes (10 versions) and 22 pages' `sidebar_fields`, besides the wiki bodies where it belongs. So every place that shows a summary, a note or an excerpt of a wiki body as plain text is suspect: Home's "Last time" line, the Sessions list's recap line, Storyline, search snippets, the bell, the wiki front page's feed, Prep's "where we left", and what leaves the app (the approved-summary Discord post, the recap email, the bot's `/recap`). ## Approach 1. One helper on each side that turns the markup into its shown text (`[[T|label]]` → label, `[[T]]` → T, `[[T#S|l]]` → l, an escaped `\[[` left as typed), built on the existing parsers (`wiki/wikiText.js`, `services/wiki_links.py`), not a second regex. 2. Apply it at every place that draws such text as plain text, in the app and outbound; find them by sweeping, not from the list above alone. 3. Tests per surface, and a journey assertion that no page of the seeded campaign shows `[[`. Whether a recap's links should be real links to the wiki (for the readers who may see the page) is a design question for the owner, not part of this fix.
rbrooks referenced this issue from a commit 2026-10-12 15:58:23 +00:00
rbrooks referenced this issue from a commit 2026-10-12 15:58:23 +00:00
rbrooks referenced this issue from a commit 2026-10-12 16:48:16 +00:00
Author
Collaborator

Done: merged in PR #1136, on dev in v4.5.0-rc12 (2026-10-12, 22:25 UTC)

Owner's decision (Q173 and chat, D296): wiki links in recaps, notes and facts are real links in the app and plain words in everything that leaves it.

What was built

  • wiki_links.plain_text / wiki/plainWikiText.js, one shared case file: markup to the words a reader should see, used inside clickable rows and excerpts (Home's "Last time", the Sessions list, timeline lines, search), for anything the server cuts, and outbound (the Discord post, /recap, /history, the reminder excerpt, the recap email, the calendar feed).
  • wiki_links.visible_links and wiki/RecapText.jsx: the server sends, per reader, only the link targets that reader may open (the article's own audience checks, four queries however many links), and the browser draws a link only for those. A hidden page, a missing page and a name that fits several pages all draw as plain words. Surfaces: the Read tab, Review's summary plate, "Previously on" (This week, the session page, the table), Prep's "where we left", Storyline, the GM's view of players' notes, card faces, the article's Facts.
  • No response a non-GM can receive carries links resolved as a GM (shelf, prep and copy-from are GM-only routes; what is shown at the table carries no links); tests assert a GM-only page's id and title appear nowhere in a player's JSON.

Found by reviews of this branch and fixed in it

  • The inline parser crashed on a typed private-use character (the article renderer too).
  • Drawing these texts through the wiki renderer would have loaded images and web links from member-written text; RecapText now draws only wiki links (parseInline(text, {only: ["link"]})), with hostile-input tests on every surface.
  • Two guard tests (tests/wiki/test_body_readers.py, test_title_readers.py) needed the new reads listed; the builder's backend selection had not included tests/wiki, which cost two CI rounds.

Checked on dev (rc12): every service healthy, /api/meta reports 4.5.0-rc12, logs clean, demo data rebuilt. For the owner's account, the latest Amberwake recap ("The Tide-Gate") has six links, five of which resolve to pages; the sixth, "The Tidewatch Ledger", names two pages (an artifact and a quest) and so stays as words by design. Not looked at in a browser by me; the journey j6 asserts the link and the absence of [[ on This week and the Read tab, and passed in CI.

Known limits

  • A recap longer than 1,200 characters is cut for This week's "Previously on" and shown as words there.
  • A name that fits several pages is not linked; an article sends such a link to its disambiguation page. Worth a follow-up if the owner wants it.
  • Exports and print views were not audited.
  • Wiki articles still load images from outside addresses: #1138, for 4.5.1.
## Done: merged in PR #1136, on dev in v4.5.0-rc12 (2026-10-12, 22:25 UTC) **Owner's decision (Q173 and chat, D296):** wiki links in recaps, notes and facts are real links in the app and plain words in everything that leaves it. **What was built** - `wiki_links.plain_text` / `wiki/plainWikiText.js`, one shared case file: markup to the words a reader should see, used inside clickable rows and excerpts (Home's "Last time", the Sessions list, timeline lines, search), for anything the server cuts, and outbound (the Discord post, `/recap`, `/history`, the reminder excerpt, the recap email, the calendar feed). - `wiki_links.visible_links` and `wiki/RecapText.jsx`: the server sends, per reader, only the link targets that reader may open (the article's own audience checks, four queries however many links), and the browser draws a link only for those. A hidden page, a missing page and a name that fits several pages all draw as plain words. Surfaces: the Read tab, Review's summary plate, "Previously on" (This week, the session page, the table), Prep's "where we left", Storyline, the GM's view of players' notes, card faces, the article's Facts. - No response a non-GM can receive carries links resolved as a GM (shelf, prep and copy-from are GM-only routes; what is shown at the table carries no links); tests assert a GM-only page's id and title appear nowhere in a player's JSON. **Found by reviews of this branch and fixed in it** - The inline parser crashed on a typed private-use character (the article renderer too). - Drawing these texts through the wiki renderer would have loaded images and web links from member-written text; `RecapText` now draws only wiki links (`parseInline(text, {only: ["link"]})`), with hostile-input tests on every surface. - Two guard tests (`tests/wiki/test_body_readers.py`, `test_title_readers.py`) needed the new reads listed; the builder's backend selection had not included `tests/wiki`, which cost two CI rounds. **Checked on dev (rc12):** every service healthy, `/api/meta` reports 4.5.0-rc12, logs clean, demo data rebuilt. For the owner's account, the latest Amberwake recap ("The Tide-Gate") has six links, five of which resolve to pages; the sixth, "The Tidewatch Ledger", names two pages (an artifact and a quest) and so stays as words by design. Not looked at in a browser by me; the journey `j6` asserts the link and the absence of `[[` on This week and the Read tab, and passed in CI. **Known limits** - A recap longer than 1,200 characters is cut for This week's "Previously on" and shown as words there. - A name that fits several pages is not linked; an article sends such a link to its disambiguation page. Worth a follow-up if the owner wants it. - Exports and print views were not audited. - Wiki articles still load images from outside addresses: #1138, for 4.5.1.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
rbrooks/Quest-Board#1135
No description provided.