Skip to content
This repository was archived by the owner on Nov 10, 2026. It is now read-only.
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
9 changes: 5 additions & 4 deletions src/dissatisfaction.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@
// a report of some other error — must not revise a verdict, because the Hub
// counts every report and there is no way to take one back.
//
// `X不X` questions are the trap this has to survive: `行不行` and `对不对` are
// requests for an opinion and contain `不行` and `不对` verbatim.
// Questions are the trap this has to survive: `行不行`, `对不对`, `不行吗` and
// a closing `不对吧` ask for an opinion and contain `不行` and `不对` verbatim.
const ASKS_FOR_OPINION = /\s*(?:[吗嘛么]|吧?\s*[??]|吧\s*[。.!!~~]*\s*$)/;
const CORRECTION_PATTERNS = [
/(?<!行)不行(?!不)/,
/(?<!对)不对(?!不)/,
new RegExp(`(?<!行)不行(?!不)(?!${ASKS_FOR_OPINION.source})`),
new RegExp(`(?<!对)不对(?!不)(?!${ASKS_FOR_OPINION.source})`),
/没(?:有)?(?:用|效果|解决|成功|生效)/,
/不管用/,
/还是(?:不|没|失败|报错|错)/,
Expand Down
10 changes: 4 additions & 6 deletions src/verification.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@ const VERIFICATION_COMMANDS = [
const FAILURE_SIGNALS = new Set(['test_failure', 'log_error']);

// Summaries of a passing run still name failures by count ("0 failed",
// "failures: 0"); those lines are not evidence of one.
const ZERO_COUNT = /\b0\s+(?:failed|failures?|errors?)\b|\b(?:failed|failures?|errors?)\s*[:=]\s*0\b/i;
// "failures: 0"); only those counts are dropped, so "1 failed, 0 errors"
// still reads as a failure.
const ZERO_COUNT = /\b0\s+(?:failed|failures?|errors?)\b|\b(?:failed|failures?|errors?)\s*[:=]\s*0\b/gi;

const SHELL_TOOL_NAMES = ['bash'];

Expand All @@ -37,10 +38,7 @@ function textOf(result) {
// the output is read too, not only `isError`.
export function verificationFailed(result) {
if (result?.isError) return true;
const evidence = textOf(result)
.split('\n')
.filter((line) => !ZERO_COUNT.test(line))
.join('\n');
const evidence = textOf(result).replace(ZERO_COUNT, '');
return detectSignals(evidence).some((signal) => FAILURE_SIGNALS.has(signal));
}

Expand Down
8 changes: 8 additions & 0 deletions test/feedback-label.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,14 @@ test('a correction wins over politeness', () => {
assert.equal(feedbackLabelOf("thanks, but it still doesn't work"), 'correction');
});

test('a question asking whether it is wrong is not a correction', () => {
for (const text of ['不行吗?', '这样不对吧', '不对吧。', '这样不行吗', '这样不对吧?']) assert.equal(feedbackLabelOf(text), null, text);
});

test('a plain statement that it is wrong still is', () => {
for (const text of ['这样不对', '不行,还是报错', '不对吧,你再看看', '这样不行!']) assert.equal(feedbackLabelOf(text), 'correction', text);
});

test('a new request carries no label', () => {
for (const text of ['now add a logout button', '这个行不行?', '', undefined]) assert.equal(feedbackLabelOf(text), null, String(text));
});
5 changes: 5 additions & 0 deletions test/verification.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ test('failure output fails it even when bash reported success', () => {
assert.equal(verificationFailed(output("ℹ pass 0\nℹ fail 1\n\n✖ failing tests:\n\ntest at test:1:1\n✖ test (59.9ms)\n 'test failed'\n")), true);
});

test('a zero count beside a failure does not hide it', () => {
assert.equal(verificationFailed(output('Tests: 1 failed, 0 errors')), true);
assert.equal(verificationFailed(output('failures: 0, errors: 2 — test failed')), true);
});

test('a passing summary that counts zero failures passes', () => {
assert.equal(verificationFailed(output('ℹ tests 78\nℹ pass 78\nℹ fail 0')), false);
assert.equal(verificationFailed(output('Tests: 0 failed, 12 passed')), false);
Expand Down
Loading