mirror of
https://github.com/vxcontrol/pentagi.git
synced 2026-09-22 10:15:43 +00:00
fix(editor): stop escaping a lone ~ so Go-template prose round-trips byte-exact
faithfulEscape backslash-escaped every ~, but in GFM only ~~ is strikethrough — a lone ~ is literal. Escaping it injected a stray \ into prompt prose (e.g. ~10%/~30% in generator.tmpl + refiner.tmpl), and since the editor's getMarkdown output is the Go text/template stored and sent to the LLM, the \ shipped to the model. Escape ~ only in runs of 2+ (a balanced ~~strike~~ is handled by the Strike mark upstream and never reaches this path); backtick/backslash unchanged. Pinned by byte-identity tests for lone tildes (the corpus test asserts only word-multiset + convergence, so \~ slipped through). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
7a96d9dbd4
commit
443fcc90ee
@@ -24,8 +24,12 @@ const createFaithfulMarked = () => {
|
||||
// ``` ` * _ [ ] ~ ``` — both corrupt our content (tags become entities, `[1-1000]`/`*.php`/`snake_case`
|
||||
// gain stray backslashes). Text serialization is hard-coded in the manager (no per-extension hook), so we
|
||||
// retune that one method: drop the entity-encoding entirely, and backslash-escape only the chars that
|
||||
// would otherwise re-parse as inline syntax (`` ` ``, `~`, `\`).
|
||||
const faithfulEscape = (text: string): string => text.replace(/([\\`~])/g, '\\$1');
|
||||
// would otherwise re-parse as inline syntax: `` ` `` and `\` always, and `~` only when doubled — a lone
|
||||
// `~` is literal in GFM, so escaping it would inject a stray `\` into prose like `~10%`.
|
||||
const faithfulEscape = (text: string): string =>
|
||||
text.replace(/[\\`]|~+/g, (match) =>
|
||||
match[0] === '~' ? (match.length > 1 ? match.replace(/~/g, '\\~') : match) : `\\${match}`,
|
||||
);
|
||||
|
||||
type ManagerWithEncode = {
|
||||
codeTypes: Set<string>;
|
||||
|
||||
@@ -63,6 +63,19 @@ describe('selective escape — no stray backslashes on literal punctuation', ()
|
||||
);
|
||||
});
|
||||
|
||||
describe('lone tilde stays literal — no stray backslash injected (M1)', () => {
|
||||
it.each(['~10% for environment setup', 'approximately ~5 minutes', 'a ~ b ~ c'])(
|
||||
'keeps %s byte-identical',
|
||||
(s) => {
|
||||
expect(roundTrip(s)).toBe(s);
|
||||
},
|
||||
);
|
||||
|
||||
it('still round-trips ~~strikethrough~~ (double tilde unaffected)', () => {
|
||||
expect(roundTrip('use ~~deprecated~~ here')).toContain('~~deprecated~~');
|
||||
});
|
||||
});
|
||||
|
||||
describe('inline marks round-trip', () => {
|
||||
it.each(['**bold**', '*italic*', '`code span`', '~~strike~~', '[a link](https://example.com)', '**bold `code` end**'])(
|
||||
'preserves %s',
|
||||
|
||||
Reference in New Issue
Block a user