From 7caab85cb5040ed484217dd316fd07cdf4c001e3 Mon Sep 17 00:00:00 2001 From: Adrian Bonpin Date: Wed, 7 Oct 2026 16:23:55 +0800 Subject: [PATCH] fix: separate raised citation markers with a space, not a comma The comma sits at the baseline, so between two raised digits it drops away from them and reads as a stray mark on the line. A space separates them without introducing a character that lives at the wrong height. [1][2] and [1, 2] both still arrive as two raised digits, still in the order the model wrote them, and a run containing a number we were never shown is still left exactly as written. --- README.md | 2 +- .../specs/2026-10-07-source-citations-design.md | 5 +++-- src/__tests__/citations.test.ts | 8 ++++---- src/lib/citations.ts | 6 ++++-- 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 0165d36..7cd034b 100644 --- a/README.md +++ b/README.md @@ -302,7 +302,7 @@ Footnotes deserve a note of their own, because the window cannot draw them. Vici In a sentence the marker is raised, so a claim resting on a search result reads as `rain is likely¹` rather than with the brackets at full size. It is a Unicode superscript digit rather than a `` tag, because a link label is plain text that nothing downstream can break, and a digit is raised by design in every font. The entry under the answer keeps the plain number, `[1] Rain in Cebu`, where being able to scan a list matters more than the convention. -Markers the model wrote touching are separated as they are drawn, because `¹²` reads as twelve, or as twenty one, and either way as one number. The prompt asks for exactly that touching shape, so `[1][2]` and the same numbers written apart both arrive as `¹, ²`. +Markers the model wrote touching are separated as they are drawn, because `¹²` reads as twelve, or as twenty one, and either way as one number. The prompt asks for exactly that touching shape, so `[1][2]` and the same numbers written apart both arrive as two raised digits with a space between them. A space rather than a comma: a comma sits at the baseline, so between raised digits it drops away from them and reads as a stray mark. Numbers are handed out where the tools run, once per turn, and printed in the tool result the model reads. So `[3]` means one page for the model and for the reader, three searches in one turn do not each start again at `1`, and a page found by a search and read again later keeps the number it already had. A marker stays as written unless every number in it is a source with a destination this window should open: half a link reads as a citation that works, and a `javascript:` address out of a search result is a real vulnerability rather than a rendering quirk. diff --git a/docs/superpowers/specs/2026-10-07-source-citations-design.md b/docs/superpowers/specs/2026-10-07-source-citations-design.md index 558e15c..5d37e72 100644 --- a/docs/superpowers/specs/2026-10-07-source-citations-design.md +++ b/docs/superpowers/specs/2026-10-07-source-citations-design.md @@ -163,9 +163,10 @@ The pass follows the rules the rest of the pipeline already follows. machinery the maths pass uses. A `[1]` inside a snippet is text. - **`[n]`, `[^n]`, comma lists, and runs.** `[1, 3]` is two sources inside one marker, and `[1][2]` is two markers written touching. Both are drawn as raised - digits separated by a comma, because `¹²` reads as a single number and leaves the + digits separated by a space, because `¹²` reads as a single number and leaves the reader no way to tell where one citation ends, which is a defect the first version - of this shipped with. + of this shipped with. A space rather than a comma, because a comma sits at the + baseline and so drops away from digits that are raised above it. - **All or nothing.** A marker is linked only when every index it names is a known source. `[1, 9]` where 9 was never seen stays exactly as written, rather than becoming half a link. diff --git a/src/__tests__/citations.test.ts b/src/__tests__/citations.test.ts index a98e359..ad7654f 100644 --- a/src/__tests__/citations.test.ts +++ b/src/__tests__/citations.test.ts @@ -127,10 +127,10 @@ describe("linkCitations", () => { expect(linkCitations("Rain [^1].", sources).text).toBe("Rain [¹](https://first.example)."); }); - it("links a list of markers and keeps the comma", () => { + it("separates markers written as one list", () => { const result = linkCitations("Both [1, 3] agree.", sources); - expect(result.text).toBe("Both [¹](https://first.example), [³](https://third.example) agree."); + expect(result.text).toBe("Both [¹](https://first.example) [³](https://third.example) agree."); expect([...result.cited].sort()).toEqual([1, 3]); }); @@ -148,13 +148,13 @@ describe("linkCitations", () => { // says it is two sources, or where the first one stops and the second begins. const result = linkCitations("Both [1][3] agree.", sources); - expect(result.text).toBe("Both [¹](https://first.example), [³](https://third.example) agree."); + expect(result.text).toBe("Both [¹](https://first.example) [³](https://third.example) agree."); expect([...result.cited].sort()).toEqual([1, 3]); }); it("treats a space between two markers the same way", () => { expect(linkCitations("Both [1] [3] agree.", sources).text).toBe( - "Both [¹](https://first.example), [³](https://third.example) agree.", + "Both [¹](https://first.example) [³](https://third.example) agree.", ); }); diff --git a/src/lib/citations.ts b/src/lib/citations.ts index cdec6f1..5dd6cf1 100644 --- a/src/lib/citations.ts +++ b/src/lib/citations.ts @@ -228,9 +228,11 @@ export function linkCitations(text: string, sources: Source[]): CitationResult { } for (const index of wanted) cited.add(index); - // Separated even when the model wrote them touching: `¹, ²` is two citations, + // Separated even when the model wrote them touching: `¹ ²` is two citations, // where `¹²` is a number, and the reader has no way to tell it from a real one. - return links.join(", "); + // A space rather than a comma, which sits at the baseline and so drops away from + // digits that are raised above it. + return links.join(" "); }), );