rulereceipt 0.1.30 → 0.1.31
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.
|
@@ -18,6 +18,45 @@ function eventSearchText(event) {
|
|
|
18
18
|
}
|
|
19
19
|
return "";
|
|
20
20
|
}
|
|
21
|
+
/**
|
|
22
|
+
* Standard short spellings of destructive flags, canonicalised before
|
|
23
|
+
* matching.
|
|
24
|
+
*
|
|
25
|
+
* Without this, a prohibition returned PASS whenever its exact literal was
|
|
26
|
+
* absent — so a rule banning `git push --force` printed "no occurrence
|
|
27
|
+
* found" for a session that ran `git push -f`. Found 2026-09-08. That is
|
|
28
|
+
* the inverse of the false-FAIL problem in the postmortem and the more
|
|
29
|
+
* dangerous direction: a wrong FAIL sends someone to check the evidence, a
|
|
30
|
+
* wrong PASS stops them looking.
|
|
31
|
+
*
|
|
32
|
+
* Deliberately three entries, not an open-ended table. These are stable
|
|
33
|
+
* decades-old CLI conventions for the specific destructive operations rules
|
|
34
|
+
* actually ban. Expansion is gated on the COMMAND CONTEXT, because a bare
|
|
35
|
+
* `-f` means something different everywhere: `grep -f patterns.txt` is not
|
|
36
|
+
* forcing anything, and rewriting it would invent a violation.
|
|
37
|
+
*
|
|
38
|
+
* `--force-with-lease` must keep passing a `--force` ban — it is the safe
|
|
39
|
+
* command, and the trailing word boundary in matchesPattern already keeps
|
|
40
|
+
* the two apart.
|
|
41
|
+
*/
|
|
42
|
+
const FLAG_ALIASES = [
|
|
43
|
+
{ context: /\bgit\s+push\b/, short: /(?:^|\s)-[A-Za-z]*f/, canonical: "git push --force" },
|
|
44
|
+
{ context: /\bgit\s+commit\b/, short: /(?:^|\s)-[A-Za-z]*n/, canonical: "git commit --no-verify" },
|
|
45
|
+
{ context: /\brm\b/, short: /(?:^|\s)-(?:[A-Za-z]*r[A-Za-z]*f|[A-Za-z]*f[A-Za-z]*r)/, canonical: "rm -rf" },
|
|
46
|
+
];
|
|
47
|
+
/**
|
|
48
|
+
* The text a pattern is matched against: the event's own text, plus the
|
|
49
|
+
* canonical spelling of any aliased flag it used. Kept separate from the
|
|
50
|
+
* text quoted back as evidence, so a report always shows what the session
|
|
51
|
+
* actually ran, never a canonical form it never typed.
|
|
52
|
+
*/
|
|
53
|
+
function searchHaystack(event) {
|
|
54
|
+
const raw = eventSearchText(event);
|
|
55
|
+
if (event.kind !== "tool_use")
|
|
56
|
+
return raw;
|
|
57
|
+
const extra = FLAG_ALIASES.filter((a) => a.context.test(raw) && a.short.test(raw)).map((a) => a.canonical);
|
|
58
|
+
return extra.length > 0 ? `${raw} ${extra.join(" ")}` : raw;
|
|
59
|
+
}
|
|
21
60
|
function escapeRegex(literal) {
|
|
22
61
|
return literal.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
23
62
|
}
|
|
@@ -56,7 +95,7 @@ export function runDeterministicChecks(classifications, events) {
|
|
|
56
95
|
let foundEvent;
|
|
57
96
|
let foundPattern;
|
|
58
97
|
for (const event of events) {
|
|
59
|
-
const haystack =
|
|
98
|
+
const haystack = searchHaystack(event);
|
|
60
99
|
for (const pattern of patterns) {
|
|
61
100
|
if (matchesPattern(haystack, pattern)) {
|
|
62
101
|
foundEvent = event;
|
|
@@ -96,7 +135,7 @@ export function runDeterministicChecks(classifications, events) {
|
|
|
96
135
|
ruleTitle: rule.title,
|
|
97
136
|
ruleSource: rule.source,
|
|
98
137
|
status: "PASS",
|
|
99
|
-
evidence: `no occurrence of ${patterns.map((p) => `"${p}"`).join(" or ")}
|
|
138
|
+
evidence: `no occurrence of ${patterns.map((p) => `"${p}"`).join(" or ")} in the commands and messages recorded this session — this is a text scan of the transcript, so it is evidence rather than proof: a spelling this checker does not know would not be caught`,
|
|
100
139
|
};
|
|
101
140
|
}
|
|
102
141
|
// polarity === "require": absence is the failure, not presence. But
|