@davesheffer/hunch 0.38.0 → 0.38.1

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.
Files changed (2) hide show
  1. package/dist/cli/index.js +37 -1
  2. package/package.json +1 -1
package/dist/cli/index.js CHANGED
@@ -1182,7 +1182,43 @@ program
1182
1182
  console.log("");
1183
1183
  }
1184
1184
  console.log(markdown ? renderMarkdown(report) : renderText(report));
1185
- if (reportFailsStrict(report))
1185
+ // ARCHITECTURAL CONFORMANCE: does the RESULTING code still satisfy every recorded
1186
+ // architectural invariant? This is graph-reachability, not a diff — so it catches semantic
1187
+ // violations a pattern-matcher / SAST can't express (a controller that now reaches the DB
1188
+ // directly). It must run over the CHANGED code, so re-parse the working tree first — but
1189
+ // ONLY when conformance predicates exist (zero cost on repos that don't use them). The
1190
+ // gate cases (--staged / --base / --commit HEAD) all have the working tree AT the change.
1191
+ // Surfaced always; gates the commit/PR under --strict, with the receipt of the why.
1192
+ const hasConformance = store.recs("decisions").some((d) => (d.conformance?.length ?? 0) > 0);
1193
+ if (hasConformance) {
1194
+ indexRepo(store, root, { churn: false }); // refresh the symbol/dep graph from the working tree
1195
+ store.reindex();
1196
+ }
1197
+ const confViolations = hasConformance ? checkConformance(store).filter((c) => !c.satisfied) : [];
1198
+ if (confViolations.length) {
1199
+ if (markdown) {
1200
+ console.log(`\n### ⛔ Architectural conformance — ${confViolations.length} invariant(s) violated\n`);
1201
+ for (const c of confViolations) {
1202
+ const dec = store.json.get("decisions", c.decision);
1203
+ const why = dec?.context ? ` · _why: ${dec.context}_` : "";
1204
+ const bug = dec?.caused_by_bug ? ` · prevents recurrence of \`${dec.caused_by_bug}\`` : "";
1205
+ console.log(`- ⛔ **${c.detail}** — \`${c.decision}\` "${c.title}"${why}${bug}`);
1206
+ }
1207
+ }
1208
+ else {
1209
+ console.log(`\n⛔ Architectural conformance — ${confViolations.length} invariant(s) the code no longer satisfies (an AI change drifted from the architecture):`);
1210
+ for (const c of confViolations) {
1211
+ const dec = store.json.get("decisions", c.decision);
1212
+ console.log(` ${c.detail} (${c.decision} "${c.title}")`);
1213
+ if (dec?.context)
1214
+ console.log(` ↳ why: ${dec.context}`);
1215
+ if (dec?.caused_by_bug)
1216
+ console.log(` ↳ prevents recurrence of: ${dec.caused_by_bug}`);
1217
+ }
1218
+ console.log(` The semantic invariant a linter can't see — run \`hunch conform\` for the full picture.`);
1219
+ }
1220
+ }
1221
+ if (reportFailsStrict(report) || (!!opts.strict && confViolations.length > 0))
1186
1222
  process.exitCode = 1;
1187
1223
  store.close();
1188
1224
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@davesheffer/hunch",
3
- "version": "0.38.0",
3
+ "version": "0.38.1",
4
4
  "license": "Apache-2.0",
5
5
  "author": "Dave Sheffer <dave.sheffer1@gmail.com>",
6
6
  "description": "Architectural Conformance for AI-generated code: a git-native graph that deterministically blocks AI changes which break your architecture — the semantic invariants (layering, must-reach, dependency direction) pattern-SAST can't express — grounded in the decisions and bugs behind each rule, across any MCP assistant (Claude Code, Cursor, Copilot, Windsurf, Codex).",