rulereceipt 0.1.34 → 0.1.35

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.
@@ -297,7 +297,27 @@ const FORBID_SIGNAL = /\b(never|don'?t|do not|forbidden|banned|prohibited|disall
297
297
  // often prescribes with a bare imperative ("Use `gh pr merge --merge`")
298
298
  // rather than "you must use" — and it's exactly those imperative clauses
299
299
  // whose literals get misattributed to the prohibiting half.
300
- const PRESCRIPTIVE_VERB = /\b(use|run|prefer|apply|follow|call|invoke|stick to|update|write|create|add|include|keep|maintain|document)\b/i;
300
+ /**
301
+ * Prescriptive verbs, in two lists, because they serve two jobs that pull in
302
+ * opposite directions.
303
+ *
304
+ * MIXED_POLARITY_VERB is the narrow one, used only to spot a prohibition
305
+ * followed by a prescription — "NEVER squash when merging PRs. Use `gh pr
306
+ * merge --merge`" — where the prescribed half's literals would otherwise be
307
+ * checked with the prohibition's polarity.
308
+ *
309
+ * INFERRED_REQUIRE_VERB is the wide one, used only to read the direction of a
310
+ * bare imperative when no signal word says which way a rule points.
311
+ *
312
+ * They were one list until 2026-09-14. Widening it for the second job broke
313
+ * the first: a rule reading "these databases must NEVER be deleted" stopped
314
+ * being checked at all, because a later bullet — "Any DB whose table names
315
+ * include: ..." — carries "include" with no forbid word beside it and so read
316
+ * as mixed polarity. That rule had been the only decided verdict on a real
317
+ * file, and the report went to 0 followed out of 15.
318
+ */
319
+ const MIXED_POLARITY_VERB = /\b(use|run|prefer|apply|follow|call|invoke|stick to)\b/i;
320
+ const INFERRED_REQUIRE_VERB = /\b(use|run|prefer|apply|follow|call|invoke|stick to|update|write|create|add|include|keep|maintain|document)\b/i;
301
321
  function hasMixedPolarity(rule) {
302
322
  const text = `${rule.title} ${rule.text}`;
303
323
  if (!FORBID_SIGNAL.test(text))
@@ -317,7 +337,7 @@ function hasMixedPolarity(rule) {
317
337
  // polarity and sent to judgment instead of being checked.
318
338
  const masked = text.replace(/`[^`]*`/g, (m) => "`" + "x".repeat(Math.max(m.length - 2, 0)) + "`");
319
339
  const clauses = masked.split(/[.;\n]|(?:\s+-\s+)/).filter((c) => c.trim().length > 0);
320
- return clauses.some((clause) => !FORBID_SIGNAL.test(clause) && (REQUIRE_SIGNAL.test(clause) || PRESCRIPTIVE_VERB.test(clause)));
340
+ return clauses.some((clause) => !FORBID_SIGNAL.test(clause) && (REQUIRE_SIGNAL.test(clause) || MIXED_POLARITY_VERB.test(clause)));
321
341
  }
322
342
  /**
323
343
  * The direction of a rule, or null when the text does not say.
@@ -352,7 +372,7 @@ function detectPolarity(rule) {
352
372
  // pattern that never appears reports UNCLEAR, while a forbidden one that
353
373
  // appears reports a violation. Guessing toward require can waste a check;
354
374
  // guessing toward forbid accuses someone.
355
- if (PRESCRIPTIVE_VERB.test(text))
375
+ if (INFERRED_REQUIRE_VERB.test(text))
356
376
  return "require";
357
377
  return null;
358
378
  }
@@ -370,7 +390,7 @@ function polarityWasInferred(rule) {
370
390
  const text = `${rule.title} ${rule.text}`;
371
391
  if (FORBID_SIGNAL.test(text) || REQUIRE_SIGNAL.test(text))
372
392
  return false;
373
- return PRESCRIPTIVE_VERB.test(text);
393
+ return INFERRED_REQUIRE_VERB.test(text);
374
394
  }
375
395
  /**
376
396
  * A rule is only treated as deterministic when it names a specific,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rulereceipt",
3
- "version": "0.1.34",
3
+ "version": "0.1.35",
4
4
  "description": "Checks whether a Claude Code session actually followed your CLAUDE.md / AGENTS.md rules, with evidence.",
5
5
  "repository": {
6
6
  "type": "git",