Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/2645.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
A copied line no longer loses the front of a word that only starts with a Bot's name: `Development plan is ready`, `Devices are offline`, `Released v0.2.26`, `Reviewers disagree` and `Chief concern about the tip` copy exactly as the preview reads them, while a real `Dev → Reviewer` delegation trace is still removed ([#2645](https://github.com/ThinkFlowLab/FlyBot/issues/2645)).
4 changes: 2 additions & 2 deletions core/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -2592,8 +2592,8 @@ function stripFlatTrace(text2, names = []) {
const body = String(text2 ?? "");
const known = sortedNames(names);
if (known.length === 0) return body;
const hop = `(?:${known.map(escapeName).join("|")})`;
const trace = new RegExp(`^[ \\t]*${hop}(?:[ \\t]*→[ \\t]*${hop})*[ \\t]*(?=\\S)`);
const hop = `(?:${known.map(escapeName).join("|")})(?=[ \\t→,,.。::]|$)`;
const trace = new RegExp(`^[ \\t]*${hop}(?:[ \\t]*→[ \\t]*${hop})+[ \\t]*(?=\\S)`);
const found = trace.exec(body);
return found ? body.slice(found[0].length) : body;
}
Expand Down
6 changes: 4 additions & 2 deletions core/src/client/text.js
Original file line number Diff line number Diff line change
Expand Up @@ -150,8 +150,10 @@ export function stripFlatTrace(text, names = []) {
const body = String(text ?? '')
const known = sortedNames(names)
if (known.length === 0) return body
const hop = `(?:${known.map(escapeName).join('|')})`
const trace = new RegExp(`^[ \\t]*${hop}(?:[ \\t]*→[ \\t]*${hop})*[ \\t]*(?=\\S)`)
// A hop is a whole name, and a trace needs the arrow that makes it one: `Development` is not
// `Dev`, and `Chief concern` is a sentence, not routing. Same reading as `dropKnownTrace`.
const hop = `(?:${known.map(escapeName).join('|')})(?=[ \\t→,,.。::]|$)`
const trace = new RegExp(`^[ \\t]*${hop}(?:[ \\t]*→[ \\t]*${hop})+[ \\t]*(?=\\S)`)
const found = trace.exec(body)
return found ? body.slice(found[0].length) : body
}
Expand Down
30 changes: 30 additions & 0 deletions core/test/details-copy.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,33 @@ test('the copy drops a flat trace on the first line and keeps the paragraphs und
assert.equal(text.split('\n').map(line => line.trim()).filter(Boolean).join(' '), 'Hi there second para')
assert.equal(text.split('\n').length, 3, 'the paragraph break is not folded away')
})

// A Bot's name is a whole word (#2645). The copy reads a stored line the way the preview does, so a
// line that merely starts with a name's letters is the same line on both surfaces — and a trace that
// really is one still goes.
const PLAIN_NAMES = ['Dev', 'Release', 'Reviewer', 'Chief']
const previewLine = () => import('../src/client/text.js').then(module => module.readableLine)

test('the copy keeps a word that only starts with a Bot name, as the preview reads it', async () => {
const copyBodyOf = await copyBody()
const readableLine = await previewLine()
for (const line of [
'Development plan is ready',
'Devices are offline',
'Released v0.2.26',
'Reviewers disagree',
'Chief concern about the tip',
]) {
assert.equal(readableLine(line, PLAIN_NAMES), line, `the preview keeps ${JSON.stringify(line)}`)
assert.equal(copyBodyOf(line, PLAIN_NAMES), line, `the copy keeps ${JSON.stringify(line)}`)
}
})

test('the copy still drops a real delegation trace on the first line', async () => {
const copyBodyOf = await copyBody()
const readableLine = await previewLine()
const line = 'Dev → Reviewer please check the tip'
assert.equal(readableLine(line, PLAIN_NAMES), 'please check the tip')
assert.equal(copyBodyOf(line, PLAIN_NAMES), 'please check the tip')
assert.equal(copyBodyOf(`${line}\n\nsecond para`, PLAIN_NAMES), 'please check the tip\n\nsecond para')
})
2 changes: 1 addition & 1 deletion docs/subsystems/core-client.md

Large diffs are not rendered by default.

8 changes: 5 additions & 3 deletions patches/ds-bot/0001-core-fixes.patch
Original file line number Diff line number Diff line change
Expand Up @@ -3071,7 +3071,7 @@ diff --git a/src/client/text.js b/src/client/text.js
// The prompts in a due scheduled task's message. DSH frames it for the model: one
// task as `reminder_prompt_json: "…"`, several as `reminders_json: [{reminder_prompt}]`.
export function reminderPrompts(text) {
@@ -62,6 +62,105 @@ export function reminderPrompts(text) {
@@ -62,6 +62,107 @@ export function reminderPrompts(text) {
}
}
export const stripHeader = text => text.replace(HEADER, '')
Expand Down Expand Up @@ -3110,8 +3110,10 @@ diff --git a/src/client/text.js b/src/client/text.js
+ const body = String(text ?? '')
+ const known = sortedNames(names)
+ if (known.length === 0) return body
+ const hop = `(?:${known.map(escapeName).join('|')})`
+ const trace = new RegExp(`^[ \\t]*${hop}(?:[ \\t]*→[ \\t]*${hop})*[ \\t]*(?=\\S)`)
+ // A hop is a whole name, and a trace needs the arrow that makes it one: `Development` is not
+ // `Dev`, and `Chief concern` is a sentence, not routing. Same reading as `dropKnownTrace`.
+ const hop = `(?:${known.map(escapeName).join('|')})(?=[ \\t→,,.。::]|$)`
+ const trace = new RegExp(`^[ \\t]*${hop}(?:[ \\t]*→[ \\t]*${hop})+[ \\t]*(?=\\S)`)
+ const found = trace.exec(body)
+ return found ? body.slice(found[0].length) : body
+}
Expand Down
4 changes: 2 additions & 2 deletions patches/ds-bot/0008-client-bundle.patch
Original file line number Diff line number Diff line change
Expand Up @@ -2120,8 +2120,8 @@ diff --git a/client.js b/client.js
+ const body = String(text2 ?? "");
+ const known = sortedNames(names);
+ if (known.length === 0) return body;
+ const hop = `(?:${known.map(escapeName).join("|")})`;
+ const trace = new RegExp(`^[ \\t]*${hop}(?:[ \\t]*→[ \\t]*${hop})*[ \\t]*(?=\\S)`);
+ const hop = `(?:${known.map(escapeName).join("|")})(?=[ \\t→,,.。::]|$)`;
+ const trace = new RegExp(`^[ \\t]*${hop}(?:[ \\t]*→[ \\t]*${hop})+[ \\t]*(?=\\S)`);
+ const found = trace.exec(body);
+ return found ? body.slice(found[0].length) : body;
+}
Expand Down
30 changes: 30 additions & 0 deletions patches/ds-bot/files/test/details-copy.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,33 @@ test('the copy drops a flat trace on the first line and keeps the paragraphs und
assert.equal(text.split('\n').map(line => line.trim()).filter(Boolean).join(' '), 'Hi there second para')
assert.equal(text.split('\n').length, 3, 'the paragraph break is not folded away')
})

// A Bot's name is a whole word (#2645). The copy reads a stored line the way the preview does, so a
// line that merely starts with a name's letters is the same line on both surfaces — and a trace that
// really is one still goes.
const PLAIN_NAMES = ['Dev', 'Release', 'Reviewer', 'Chief']
const previewLine = () => import('../src/client/text.js').then(module => module.readableLine)

test('the copy keeps a word that only starts with a Bot name, as the preview reads it', async () => {
const copyBodyOf = await copyBody()
const readableLine = await previewLine()
for (const line of [
'Development plan is ready',
'Devices are offline',
'Released v0.2.26',
'Reviewers disagree',
'Chief concern about the tip',
]) {
assert.equal(readableLine(line, PLAIN_NAMES), line, `the preview keeps ${JSON.stringify(line)}`)
assert.equal(copyBodyOf(line, PLAIN_NAMES), line, `the copy keeps ${JSON.stringify(line)}`)
}
})

test('the copy still drops a real delegation trace on the first line', async () => {
const copyBodyOf = await copyBody()
const readableLine = await previewLine()
const line = 'Dev → Reviewer please check the tip'
assert.equal(readableLine(line, PLAIN_NAMES), 'please check the tip')
assert.equal(copyBodyOf(line, PLAIN_NAMES), 'please check the tip')
assert.equal(copyBodyOf(`${line}\n\nsecond para`, PLAIN_NAMES), 'please check the tip\n\nsecond para')
})
Loading