rulereceipt 0.1.51 → 0.1.52
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/dist/checks/classify.js +20 -0
- package/package.json +1 -1
package/dist/checks/classify.js
CHANGED
|
@@ -567,6 +567,13 @@ export function approvalGateActions(rule) {
|
|
|
567
567
|
function isApprovalGateRule(rule) {
|
|
568
568
|
return approvalGateActions(rule).length > 0;
|
|
569
569
|
}
|
|
570
|
+
/**
|
|
571
|
+
* A subordinating condition that scopes when a prohibition applies. A literal
|
|
572
|
+
* matcher cannot read it, so a forbid carrying one is routed to judgment
|
|
573
|
+
* rather than flagged on every occurrence. Kept to clear scoping words so a
|
|
574
|
+
* flat prohibition that merely contains "if" in passing is not swept in.
|
|
575
|
+
*/
|
|
576
|
+
const CONDITIONAL_SCOPE = /\b(?:when(?:ever)?|unless|except\s+when|only\s+(?:if|when)|as\s+long\s+as|provided\s+that|in\s+cases?\s+where)\b|\bif\s+(?:you|the|it|they|we|a|an|on|in|running|building|committing|pushing|deploying)\b/i;
|
|
570
577
|
export function classifyRule(rule) {
|
|
571
578
|
// Checked first: if this isn't a rule at all, no check of any kind
|
|
572
579
|
// should run against it — not a keyword match, not an LLM call.
|
|
@@ -634,6 +641,19 @@ export function classifyRule(rule) {
|
|
|
634
641
|
if (BRANCH_WORD.test(text) && branchName !== undefined) {
|
|
635
642
|
return { kind: "gitBranchPolicy", rule, branchName, polarity, polarityInferred };
|
|
636
643
|
}
|
|
644
|
+
// A forbid scoped by a condition the literal checkers cannot evaluate.
|
|
645
|
+
//
|
|
646
|
+
// Raised by etoryoki on anthropics/claude-code#2544: "Never run `terraform
|
|
647
|
+
// apply` when you're on main" read as "never run it" flags every correct run
|
|
648
|
+
// elsewhere. A deterministic literal match sees only the command, not the
|
|
649
|
+
// "when/unless/if" that scopes it, so it accuses every occurrence. Routing
|
|
650
|
+
// these to judgment lets the condition actually be weighed — the same choice
|
|
651
|
+
// etoryoki made ("treat conditional or hedged sentences as not checkable
|
|
652
|
+
// instead of guessing"). Placed AFTER the branch check on purpose:
|
|
653
|
+
// gitBranchPolicy evaluates its own branch condition and must keep those.
|
|
654
|
+
if (polarity === "forbid" && CONDITIONAL_SCOPE.test(text)) {
|
|
655
|
+
return { kind: "judgment", rule };
|
|
656
|
+
}
|
|
637
657
|
// Route on ANY code-shaped literal, but check ONLY the code-shaped ones.
|
|
638
658
|
// This used to pass every literal through once one of them looked like
|
|
639
659
|
// code, so a rule mentioning `foo(` and `name` searched written files for
|