@davesheffer/hunch 0.36.2 → 0.36.3
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/eval/guards.js +28 -1
- package/package.json +1 -1
package/dist/eval/guards.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { verdict } from "../core/checkreport.js";
|
|
2
2
|
import { pathMatchesGlob } from "../core/glob.js";
|
|
3
|
+
import { effectiveForbids } from "../core/constraintmatch.js";
|
|
3
4
|
const surfaced = (v) => v === "block" || v === "warn";
|
|
4
5
|
const matches = (expect, got) => (expect === "catch" ? surfaced(got) : got === expect);
|
|
5
6
|
/** Parse + validate a hand-authored golden set. */
|
|
@@ -47,7 +48,18 @@ export function generateGuardCases(store) {
|
|
|
47
48
|
.recs("constraints")
|
|
48
49
|
.filter((c) => c.status === "active" && c.severity === "blocking" && c.scope.length && isVouched(c.provenance?.source));
|
|
49
50
|
for (const c of blocking) {
|
|
50
|
-
|
|
51
|
+
const file = pathForGlob(c.scope[0]);
|
|
52
|
+
const line = violatingLine(c);
|
|
53
|
+
if (line) {
|
|
54
|
+
// CONTENT-MATCHED (dep/symbol): synthesize the REAL violation in scoped code and require a
|
|
55
|
+
// hard BLOCK — content is verified per commit, so a vouched matcher must block (not just warn).
|
|
56
|
+
cases.push({ name: `BLOCK · ${c.statement.slice(0, 56)}`, files: [file], diff: addLineDiff(file, line), expect: "block" });
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
// scope-only (or pattern-only, which we can't synthesize): a change in scope must at least be
|
|
60
|
+
// SURFACED (block or warn) — staleness/firmness decides which.
|
|
61
|
+
cases.push({ name: `CATCH · ${c.statement.slice(0, 56)}`, files: [file], expect: "catch" });
|
|
62
|
+
}
|
|
51
63
|
}
|
|
52
64
|
for (const p of ["docs/__eval__notes.md", "scripts/__eval__.txt", ".github/__eval__.yml"]) {
|
|
53
65
|
if (!blocking.some((c) => c.scope.some((g) => pathMatchesGlob(p, g)))) {
|
|
@@ -56,6 +68,21 @@ export function generateGuardCases(store) {
|
|
|
56
68
|
}
|
|
57
69
|
return cases;
|
|
58
70
|
}
|
|
71
|
+
/** A line of scoped code that actually trips a constraint's matcher — an import of the
|
|
72
|
+
* forbidden dep, or a call to the forbidden symbol. null when there's no synthesizable
|
|
73
|
+
* violation (scope-only, or a free-form regex we can't reverse). */
|
|
74
|
+
function violatingLine(c) {
|
|
75
|
+
const f = effectiveForbids(c);
|
|
76
|
+
if (f?.deps.length)
|
|
77
|
+
return `import _x from "${f.deps[0]}";`;
|
|
78
|
+
if (f?.symbols.length)
|
|
79
|
+
return `const _v = ${f.symbols[0]}();`;
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
/** A minimal unified diff that ADDS `line` to a (code) file, for a synthetic eval case. */
|
|
83
|
+
function addLineDiff(file, line) {
|
|
84
|
+
return `diff --git a/${file} b/${file}\n--- a/${file}\n+++ b/${file}\n@@ -1,1 +1,2 @@\n const __base = 1;\n+${line}\n`;
|
|
85
|
+
}
|
|
59
86
|
const isVouched = (source) => !!source && (source.includes("human_confirmed") || source === "derived");
|
|
60
87
|
/** A concrete path that matches a scope glob, for a synthetic case. Exact-file scopes pass
|
|
61
88
|
* through; wildcard scopes get a representative file inside them. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@davesheffer/hunch",
|
|
3
|
-
"version": "0.36.
|
|
3
|
+
"version": "0.36.3",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"author": "Dave Sheffer <dave.sheffer1@gmail.com>",
|
|
6
6
|
"description": "Hunch — an Engineering Memory OS: a persistent, git-native graph of the decisions, bugs, and rules behind your code, served to any MCP coding assistant (Claude Code, Cursor, Copilot, Windsurf, Codex).",
|