rulereceipt 0.1.22 → 0.1.23

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.
@@ -31,7 +31,49 @@ const IMPERATIVE_INSTRUCTION = /(?:^|[.;:!?]\s+|^\s*[-*+]\s*|\n\s*[-*+]\s*)(use|
31
31
  * with no instruction in it has nothing to check compliance against,
32
32
  * whatever its punctuation.
33
33
  */
34
+ /**
35
+ * A section whose TITLE announces a record of something that happened —
36
+ * an incident, a postmortem, a retrospective. These are written to
37
+ * explain history, not to instruct the agent.
38
+ *
39
+ * Found by running this tool on a real session (2026-08-31): a section
40
+ * titled "Real incident (2026-08-28): Vercel had the same office/personal
41
+ * mixup" was enforced as a REQUIRE rule whose pattern was an employer
42
+ * name pulled out of the narrative, so the report announced FOLLOWED
43
+ * because that name appeared somewhere in the session. The rule being
44
+ * "satisfied" was a sentence describing a past mistake.
45
+ *
46
+ * The existing directive test cannot catch these, and correctly so: a
47
+ * good incident note ends with the lesson ("Verify with `vercel whoami`
48
+ * before every deploy"), so it genuinely does contain a directive. What
49
+ * the section IS gets announced by its title, which is where this looks.
50
+ *
51
+ * Kept to a small closed class of words that name a record of an event,
52
+ * in the same spirit as DIRECTIVE_LANGUAGE above — a bounded property of
53
+ * language, not an enumeration of document formats. Enumerating formats
54
+ * is the mistake this project already made once and wrote up publicly.
55
+ */
56
+ const EVENT_RECORD_TITLE = /\b(incident|post-?mortem|retro(spective)?|outage|what went wrong)\b/i;
57
+ /**
58
+ * A title that OPENS with an instruction is a rule, whatever it goes on
59
+ * to mention. "Never repeat the 2026-08-28 incident" is a directive that
60
+ * happens to name an incident; "Real incident (2026-08-28): ..." is a
61
+ * report that happens to contain the word never further along.
62
+ */
63
+ const TITLE_OPENS_WITH_DIRECTIVE = /^\s*[-*+\d.\s]*(never|always|must|do not|don't|dont|avoid|ensure|prefer|only|make sure|be sure)\b/i;
64
+ function isEventRecord(rule) {
65
+ if (TITLE_OPENS_WITH_DIRECTIVE.test(rule.title))
66
+ return false;
67
+ if (IMPERATIVE_INSTRUCTION.test(rule.title))
68
+ return false;
69
+ return EVENT_RECORD_TITLE.test(rule.title);
70
+ }
34
71
  function isNotARule(rule) {
72
+ // Checked before the directive test on purpose: an incident note that
73
+ // ends with its lesson contains a real directive, and would otherwise
74
+ // be enforced as though the history itself were the rule.
75
+ if (isEventRecord(rule))
76
+ return true;
35
77
  const combined = `${rule.title} ${rule.text}`;
36
78
  if (DIRECTIVE_LANGUAGE.test(combined))
37
79
  return false;
package/dist/cli.js CHANGED
@@ -123,7 +123,9 @@ function writeHtmlReport(results, meta, cwd, target) {
123
123
  // name, an office email, and absolute paths. Home paths are redacted
124
124
  // automatically; nothing else can be, so say so plainly at the moment
125
125
  // the file is created rather than burying it in a policy page.
126
- console.log("Read it before you send it: it quotes your rule text and session evidence verbatim, so anything sensitive in your CLAUDE.md is in there too. (Home paths are shortened to ~.)");
126
+ console.log("\n⚠ This report includes your rule text and session evidence VERBATIM.\n" +
127
+ " Review it before sharing outside your team — only you know what's in your rules file.\n" +
128
+ " Nothing is auto-redacted: this tool cannot tell which of your own rules are sensitive.");
127
129
  }
128
130
  catch (err) {
129
131
  console.log(`\n(--html: couldn't write ${outPath} — ${err instanceof Error ? err.message : String(err)})`);
@@ -34,11 +34,18 @@ function stripControlChars(value) {
34
34
  * directory layout — that is a leak in the one artifact most likely to
35
35
  * leave the machine.
36
36
  *
37
- * This is a mechanical, judgment-free redaction: it removes the home
38
- * prefix and nothing else. It is NOT a general secret scrubber, and must
39
- * not be described as one. Rule text and evidence are still reproduced
40
- * verbatim, because that is what makes the report useful — which is why
41
- * the CLI warns the user to read the file before sending it.
37
+ * This is DISPLAY FORMATTING, not a safety mechanism, and the difference
38
+ * matters. It is a fixed, deterministic substitution of one known string
39
+ * — always correct, never guessing — and it makes paths easier to read as
40
+ * a side benefit. It protects nothing.
41
+ *
42
+ * Auto-detecting "sensitive" content and scrubbing it would be a losing
43
+ * game: it can never catch everything, and a partial scrub is worse than
44
+ * none because it invites the user to trust the output. Rule text and
45
+ * evidence are reproduced verbatim on purpose, because that is what makes
46
+ * the report worth sending. The real safeguard is the warning the CLI
47
+ * prints at write time, which puts the responsibility where it belongs —
48
+ * with the person who knows what is in their own rules file.
42
49
  */
43
50
  function redactHome(value) {
44
51
  const home = homedir();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rulereceipt",
3
- "version": "0.1.22",
3
+ "version": "0.1.23",
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",