@nightlab-agent/still-true 0.4.0 → 0.5.0
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.
- package/README.md +4 -0
- package/package.json +1 -1
- package/src/extract.mjs +13 -6
package/README.md
CHANGED
|
@@ -41,6 +41,10 @@ In a Markdown file with headings, it reads a section only if its heading starts
|
|
|
41
41
|
- `not checked`: it could not place the reference, or GitHub has no such issue, or it is private. Nothing was verified for that line.
|
|
42
42
|
- `no claim read`: a reference on a line it read, where it found no statement about that reference's state. `see #12` lands here, and so can a real statement in a sentence it could not parse. It is a list to read, not a verdict: no exit code changes for it. It does not cover statements that name no reference at all.
|
|
43
43
|
|
|
44
|
+
A statement about a reference's state may start with `only`, as in `only owner/repo#12 (merged 11-07, known)`. `owner/repo#12, merged 11-07` also reads one merged claim, including after a separate clause such as `Status: draft;`. These are narrow subject-and-state forms: `see owner/repo#12, merged 11-07` still gives no claim.
|
|
45
|
+
|
|
46
|
+
A standalone retraction field in parentheses or after a comma takes back its clause. A field immediately after a semicolon does too: `#12 is open; no longer true` gives no claim. The wording is `no longer` or `not` followed by `true`, `the case`, `accurate` or `current`, or just `outdated`, `wrong` or `incorrect`; filler such as `which is` or `anymore` is allowed. Extra prose such as `fixes outdated documentation`, quoted text and inline code do not retract. A semicolon retraction takes back the clause before it, back to the previous semicolon or sentence end, so `#12 is open, #13 is open; no longer true` retracts both; a claim in its own clause is still checked: `#12 is open; no longer true; #13 is open` still checks #13. A note containing only a retracted claim exits `0` normally and `3` with `--strict`, since nothing was verified; its reference appears under `no claim read`.
|
|
47
|
+
|
|
44
48
|
Queued work needs a reference at the start of a clause, optionally labelled PR, issue or pull request, then an optional commit hash and a counted review obligation such as `argent#1768 81004b3 owes one critic` or `argent#1768, one review owed`. Counts are a, an, one, two, three or a positive integer; the nouns are critic(s) or review(s). `still` may precede the obligation. One comma may separate the reference/hash from it, and a following wait may start with `and` or a comma. An open reference stays quiet. A reference alone, a past-tense obligation, or other unquoted trailing prose in that clause does not qualify. Quoted text and inline code are masked before matching: `argent#1768 owes one critic "not anymore"` and ``argent#1768 owes one critic `done` `` still qualify. A wholly quoted obligation stays out, as do historical and planning scopes. Blockquoted lines contribute no claims, including when the quote follows nested list markers. A quote does not silence the preceding paragraph or an independent list item after it; an immediately following unmarked prose line can still continue the quote.
|
|
45
49
|
|
|
46
50
|
Look and contradiction lines include GitHub's `state_reason` when supplied, such as `closed (not_planned)` or `closed (completed)`. The reason adds context; it does not change the verdict.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nightlab-agent/still-true",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Check a handoff note against GitHub: finds what it says about PRs and issues and prints the claims that are no longer true. Exits 1 on a contradiction.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": { "still-true": "bin/still-true.js" },
|
package/src/extract.mjs
CHANGED
|
@@ -23,8 +23,8 @@ const NONSTATE = /\b(?:(?:not|never|no longer)\s+(?:(?:yet|still|currently|alrea
|
|
|
23
23
|
// Only a field that is nothing but the retraction counts, so "(fixes outdated documentation)" keeps its claim.
|
|
24
24
|
const RETRACTION = /^(?:(?:no\s+longer|not)\s+(?:true|the\s+case|accurate|current)|outdated|wrong|incorrect)$/i;
|
|
25
25
|
const RETRACT_FILLER = /\b(?:which|that|this|it|is|was|now|anymore|any\s+more|see\s+below)\b/gi;
|
|
26
|
-
const
|
|
27
|
-
|
|
26
|
+
const isRetraction = (field) => RETRACTION.test(field.replace(RETRACT_FILLER, ' ').replace(/[.!]/g, ' ').replace(/\s+/g, ' ').trim());
|
|
27
|
+
const RETRACTED = { test: (body) => body.split(/[(),;]/).some(isRetraction) };
|
|
28
28
|
const NOT_WAITING = /\b(?:(?:not|never|no\s+longer|no\s+more|stop(?:s|ped)?|done|finished|quit|gave\s+up|nothing(?:\s+is)?|without|instead\s+of)\s+(?:(?:yet|still|currently|really)\s+)?(?:waits?|waiting)|(?:n['’]t)\s+(?:waits?|waiting))\b/i;
|
|
29
29
|
const QUALIFIED_STATE = /^\s+(?:to|into|for|or|after)\b/i;
|
|
30
30
|
// A later comma field must look like a state fact, not "open question".
|
|
@@ -32,9 +32,9 @@ const COMMA_STATE_TAIL = /^(?:$|\s+(?:\d|today\b|yesterday\b|last\b|since\b|by\b
|
|
|
32
32
|
// An initial "open PR" needs a predicate to distinguish it from a command.
|
|
33
33
|
const OPEN_PREDICATE = /^\s+(?:is|has|needs|touches)\b/i;
|
|
34
34
|
const OPEN_POSSESSIVE = /(?:['’]s|\b(?:my|our|your|their|his|her|its))\s*$/i;
|
|
35
|
-
// Subjects may have a noun label or a possessive determiner, but objects of
|
|
35
|
+
// Subjects may have "only", a noun label or a possessive determiner, but objects of
|
|
36
36
|
// phrases such as "a PR for issue #12" are not the subject of that PR's state.
|
|
37
|
-
const SUBJECT_PREFIX = /^(?:(?:PR|issue|pull request)|(?:[\w'-]+\s+)+(?:PR|issue|pull request)|(?:the\s+)?[\w-]+['’]s|(?:my|our|your|their|his|her|its)(?:\s+own)?)?$/i;
|
|
37
|
+
const SUBJECT_PREFIX = /^(?:only|(?:PR|issue|pull request)|(?:[\w'-]+\s+)+(?:PR|issue|pull request)|(?:the\s+)?[\w-]+['’]s|(?:my|our|your|their|his|her|its)(?:\s+own)?)?$/i;
|
|
38
38
|
const OBJECT_PREFIX = /\b(?:for|of|on|about|after)\b/i;
|
|
39
39
|
|
|
40
40
|
function nextQuote(raw, i, quote) {
|
|
@@ -52,6 +52,12 @@ function endQuote(raw, quote) {
|
|
|
52
52
|
|
|
53
53
|
function clauses(raw) {
|
|
54
54
|
const result = [];
|
|
55
|
+
let afterSemicolon = false;
|
|
56
|
+
const add = (clause) => {
|
|
57
|
+
// Keep a standalone semicolon retraction with the clause it takes back.
|
|
58
|
+
if (afterSemicolon && isRetraction(unquoted(clause))) result[result.length - 1] += `; ${clause}`;
|
|
59
|
+
else result.push(clause);
|
|
60
|
+
};
|
|
55
61
|
let start = 0, quote = null, code = null, depth = 0;
|
|
56
62
|
for (let i = 0; i < raw.length; i++) {
|
|
57
63
|
const char = raw[i];
|
|
@@ -68,11 +74,12 @@ function clauses(raw) {
|
|
|
68
74
|
if (char === '(') depth++;
|
|
69
75
|
if (char === ')') depth--;
|
|
70
76
|
if (depth === 0 && (char === ';' || (/[.!?]/.test(char) && /\s|^$/.test(raw[i + 1] ?? '')))) {
|
|
71
|
-
|
|
77
|
+
add(raw.slice(start, char === ';' ? i : i + 1).trim());
|
|
78
|
+
afterSemicolon = char === ';';
|
|
72
79
|
start = i + 1;
|
|
73
80
|
}
|
|
74
81
|
}
|
|
75
|
-
|
|
82
|
+
add(raw.slice(start).trim());
|
|
76
83
|
return result.filter(Boolean);
|
|
77
84
|
}
|
|
78
85
|
|