rulereceipt 0.1.67 → 0.1.68
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.
|
@@ -83,7 +83,9 @@ const ACTION_CLAIMS = [
|
|
|
83
83
|
// Idioms that borrow "pushed" but aren't a git push. "pushed the fix/code/
|
|
84
84
|
// changes/branch to <remote>" stays a claim; effort/figurative senses do
|
|
85
85
|
// not (finding #6, 2026-09-26).
|
|
86
|
-
|
|
86
|
+
// "pushed nothing/none" is a statement that NO push happened — the opposite
|
|
87
|
+
// of a push claim (finding 2026-09-29, false-accusation corpus run).
|
|
88
|
+
exclude: /\bpushed\s+(?:back|for|through|forward|ahead|past|hard|on|nothing|none|myself|ourselves|yourself|themselves|the\s+(?:boundar|button|envelope|limit|deadline|pace)|to\s+(?:get|finish|complete|ship|meet|hit|make|wrap|move))/i,
|
|
87
89
|
command: /\bgit\s+push\b/i,
|
|
88
90
|
},
|
|
89
91
|
{
|
package/dist/checks/classify.js
CHANGED
|
@@ -312,6 +312,28 @@ function isContentToken(literal) {
|
|
|
312
312
|
return false;
|
|
313
313
|
return (literal.match(/[A-Za-z0-9]/g) ?? []).length >= 3;
|
|
314
314
|
}
|
|
315
|
+
/**
|
|
316
|
+
* A file/directory/route token, as opposed to a code construct or an import
|
|
317
|
+
* specifier. It names a PATH, so searching file CONTENT for it (codeContent's
|
|
318
|
+
* job) matches every import, changelog line, package.json entry or prose that
|
|
319
|
+
* merely MENTIONS the path — a "found `dist/` written into a file" false
|
|
320
|
+
* accusation. Found in the false-accusation corpus run 2026-09-29: ~30 distinct
|
|
321
|
+
* FAIL texts were exactly this (`dist/`, `src/`, `README.md`, `AGENTS.md`,
|
|
322
|
+
* `./types`, `/status`, …). These belong to fileLifecycle (a path mutation) or,
|
|
323
|
+
* absent a mutation verb, to the deterministic checker, which reports UNCLEAR
|
|
324
|
+
* rather than fabricating a FAIL.
|
|
325
|
+
*
|
|
326
|
+
* Deliberately NARROW so real import specifiers stay checkable: `lucide-react`
|
|
327
|
+
* and `@scope/pkg` are NOT file tokens (no leading `./` or `/`, no trailing
|
|
328
|
+
* `/`, no file extension), so a "never import X" rule keeps working.
|
|
329
|
+
*/
|
|
330
|
+
const FILE_EXTENSION = /\.(json|ya?ml|toml|md|mdx|env|lock|ini|cfg|conf|xml|txt|js|jsx|mjs|cjs|ts|tsx|py|rb|go|rs|sh|sql|css|scss|html)$/i;
|
|
331
|
+
function looksLikeFilePathToken(literal) {
|
|
332
|
+
return (/\/$/.test(literal) || // trailing slash: a directory ("dist/", "src/")
|
|
333
|
+
/^(?:\.{1,2}\/|\/)/.test(literal) || // leading ./ ../ or /: a path or route
|
|
334
|
+
FILE_EXTENSION.test(literal) // a bare filename with a known extension
|
|
335
|
+
);
|
|
336
|
+
}
|
|
315
337
|
// Catches rules like "add tests for every change" or "every new function
|
|
316
338
|
// needs a test" - no literal backtick token to pattern-match, so without
|
|
317
339
|
// this they'd fall all the way through to judgment (an LLM call) even
|
|
@@ -743,7 +765,11 @@ export function classifyRule(rule) {
|
|
|
743
765
|
// only, so this is an action not a mention. Plain words and shell commands
|
|
744
766
|
// are excluded by isContentToken; protected files already routed above.
|
|
745
767
|
if (polarity === "forbid") {
|
|
746
|
-
|
|
768
|
+
// A file/dir/route token is a path reference, not code to search for inside
|
|
769
|
+
// file content — routing it to codeContent produces "found `dist/` written
|
|
770
|
+
// into a file" on any mention. Excluded here; it falls through to the
|
|
771
|
+
// deterministic checker (UNCLEAR, never a fabricated FAIL).
|
|
772
|
+
const contentTokens = [...patterns].filter((p) => isContentToken(p) && !looksLikeFilePathToken(p));
|
|
747
773
|
if (contentTokens.length > 0) {
|
|
748
774
|
return { kind: "codeContent", rule, patterns: contentTokens, polarity, polarityInferred };
|
|
749
775
|
}
|
|
@@ -47,8 +47,28 @@ function editedContentFromEvent(event) {
|
|
|
47
47
|
*/
|
|
48
48
|
function containsCall(content, pattern) {
|
|
49
49
|
const leadsWithIdentifier = /^[A-Za-z0-9_$]/.test(pattern);
|
|
50
|
-
if (!leadsWithIdentifier)
|
|
51
|
-
|
|
50
|
+
if (!leadsWithIdentifier) {
|
|
51
|
+
// A CALL like `.forEach(` legitimately follows an object (`arr.forEach()`),
|
|
52
|
+
// so member access before it is fine — bare containment.
|
|
53
|
+
if (pattern.endsWith("("))
|
|
54
|
+
return content.includes(pattern);
|
|
55
|
+
// A punct-leading NON-call token (a dotfile/extension like `.env`, `.log`)
|
|
56
|
+
// sitting right after an identifier is a property access or the tail of a
|
|
57
|
+
// longer token, not the token itself: `.env` inside `process.env` is not
|
|
58
|
+
// the .env file. Require a non-identifier char (or the start) before it.
|
|
59
|
+
// Found in the false-accusation corpus run 2026-09-29 — a `.env` rule
|
|
60
|
+
// FAILed every file using `process.env`.
|
|
61
|
+
let fromPunct = 0;
|
|
62
|
+
for (;;) {
|
|
63
|
+
const at = content.indexOf(pattern, fromPunct);
|
|
64
|
+
if (at === -1)
|
|
65
|
+
return false;
|
|
66
|
+
const before = at === 0 ? "" : content[at - 1];
|
|
67
|
+
if (!/[A-Za-z0-9_$]/.test(before))
|
|
68
|
+
return true;
|
|
69
|
+
fromPunct = at + 1;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
52
72
|
let from = 0;
|
|
53
73
|
for (;;) {
|
|
54
74
|
const at = content.indexOf(pattern, from);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rulereceipt",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.68",
|
|
4
4
|
"description": "Checks whether your AI coding agent followed your rules, with evidence. Works with Claude Code (Codex in testing); reads CLAUDE.md, AGENTS.md, Cursor, Copilot and Windsurf rules.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|