tewip-ai 0.70.8 → 0.70.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -62,12 +62,25 @@ export function impliedLandmark(layerName) {
62
62
  return null;
63
63
  }
64
64
  const PLACEHOLDER = /^(?:lorem ipsum|placeholder|label|text|body|paragraph|xxx+|\.\.\.|…)$/i;
65
- /** Text worth checking for: real words a person would notice missing. */
65
+ /**
66
+ * Text worth checking for: real words a person would notice missing - **or a number**.
67
+ *
68
+ * This required two consecutive letters, which filters the noise it was written for (a lone glyph,
69
+ * an ellipsis, "Label") and also silently discarded every number in the design. Field-tested: a
70
+ * checkout frame specifying **£54.99** against a page serving **£59.99** produced a byte-identical
71
+ * report - "5 check(s) from the design, 3 met, 2 different" - because the price was never one of the
72
+ * five. A wrong price on a checkout screen is the drift a design review exists to catch, and it was
73
+ * the one kind that could not be seen.
74
+ *
75
+ * A run of two or more digits now counts, so a price, a total, a quantity or a rate becomes a check.
76
+ * A single character still does not, nor does punctuation on its own: `3`, `—` and `...` are noise,
77
+ * `£54.99` and `20%` are the design speaking.
78
+ */
66
79
  function meaningful(text) {
67
80
  const t = text.trim();
68
81
  if (t.length < 2 || t.length > 120)
69
82
  return false;
70
- if (!/[A-Za-z]{2}/.test(t))
83
+ if (!/[A-Za-z]{2}/.test(t) && !/\d{2}/.test(t))
71
84
  return false;
72
85
  return !PLACEHOLDER.test(t);
73
86
  }
@@ -11,6 +11,7 @@ import { datasetFiles, readJsonl, readTriageRecords } from '../../reporters/stor
11
11
  import { alreadyTried, cannotAct, planFor } from './act.js';
12
12
  import { decide } from './policy.js';
13
13
  import { CANNOT_FAIL } from '../vouch/vouch.js';
14
+ import { applyCorrection, correctionFor, loadCorrections } from '../corrections.js';
14
15
  import { vouchAll } from '../vouch/vouch-command.js';
15
16
  const words = (text) => (text.toLowerCase().match(/[a-z][a-z0-9]{2,}/g) ?? []).filter((w) => !['the', 'and', 'for', 'this', 'that', 'with', 'keep', 'green', 'sprint', 'flow', 'test', 'tests'].includes(w));
16
17
  /** Does this test or scenario belong to the goal? Deterministic, so a person can predict it. */
@@ -25,6 +26,7 @@ export function observe(root, goal, only) {
25
26
  const dir = join(root, '.tewip', 'history');
26
27
  const runs = [];
27
28
  const records = [];
29
+ const corrections = [];
28
30
  if (existsSync(dir)) {
29
31
  for (const project of only ? [only] : readdirSync(dir)) {
30
32
  const where = join(dir, project);
@@ -32,16 +34,34 @@ export function observe(root, goal, only) {
32
34
  continue;
33
35
  runs.push(...readJsonl(datasetFiles(where).runs));
34
36
  records.push(...readTriageRecords(datasetFiles(where).triage));
37
+ corrections.push(...loadCorrections(where));
35
38
  }
36
39
  }
37
40
  runs.sort((a, b) => (a.finishedAt > b.finishedAt ? 1 : -1));
38
41
  const newest = runs.at(-1);
39
42
  const wanted = words(goal);
43
+ // A person's correction decides the category, as it does everywhere else.
44
+ //
45
+ // This read `r.diagnosis` straight off the record, so a correction never reached the decision.
46
+ // Field-tested: a person marked a failure REAL_BUG, and `pursue` went on calling it LOCATOR_DRIFT
47
+ // and concluding "the goal is failing and Tewip cannot say why". That matters more here than in a
48
+ // report, because this is the command that **acts** - and its own documented order is "a real bug
49
+ // first... it will not touch a real bug", a safety rule that depends on recognising one. The
50
+ // strongest possible signal that something is a real bug is a person saying so.
51
+ //
52
+ // Phase 74 fixed this inside `buildItems` for every command that goes through it. This one does
53
+ // not, so it applies them itself.
40
54
  const failing = records
41
55
  .filter((r) => r.runId === newest?.runId && about(wanted, r.testId))
42
56
  .map((r) => {
43
- const d = r.diagnosis;
44
- return { testId: r.testId, category: String(d?.category ?? 'UNKNOWN'), confidence: Number(d?.confidence ?? 0) };
57
+ const correction = corrections.length > 0 ? correctionFor(r, corrections) : null;
58
+ const own = r.diagnosis;
59
+ const d = correction && own ? applyCorrection(own, correction) : own;
60
+ return {
61
+ testId: r.testId,
62
+ category: String((correction?.category ?? d?.category) ?? 'UNKNOWN'),
63
+ confidence: Number(d?.confidence ?? (correction ? 1 : 0)),
64
+ };
45
65
  });
46
66
  // A test that failed in more than one earlier run and is not failing now has come and gone.
47
67
  const failedBefore = new Map();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tewip-ai",
3
- "version": "0.70.8",
3
+ "version": "0.70.10",
4
4
  "mcpName": "io.github.hmamut39/tewip",
5
5
  "description": "Tewip finds why each failing test failed - real bug, flaky test, environment, test data or changed selector - for any stack, from the command line, CI, or your editor.",
6
6
  "keywords": [