pi-jev-guard 0.7.1 → 0.7.2

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/README.md CHANGED
@@ -148,7 +148,9 @@ Non blocca mai; `/jev output` mostra l'ultimo verdetto.
148
148
 
149
149
  Sempre in `automatic`, il **grounding** (opt-in, default spento) confronta le
150
150
  affermazioni sulla sessione — "ho letto il file", "i test passano", "ho
151
- eseguito X" — con un registro evidenza costruito dai `tool_result`
151
+ eseguito X", "il guard riporta X" — con un registro evidenza costruito dai
152
+ `tool_result` e dai fatti generati dal guard (footer di verifica, run
153
+ `jev_verify`)
152
154
  (comandi, test, letture, modifiche; redatto, max 30 voci, solo sessione
153
155
  corrente). Due domande Jev strette: contraddizione con l'evidenza e
154
156
  affermazione non supportata. Senza evidenza non si giudica: "senza
@@ -165,7 +167,9 @@ Il grounding è probabilistico; per **compilazione, lint e test** serve una
165
167
  prova deterministica. La verifica progetto conserva, solo nella sessione,
166
168
  `edit`/`write` riusciti e gli esiti osservati dei check. Il footer viene
167
169
  generato dall'estensione, non dal modello: un test non eseguito non può
168
- diventare “test passati”.
170
+ diventare “test passati”. Un report di “verifica” scritto dal modello non ha
171
+ valore: il footer autorevole è solo quello del guard (marcato `generata dal
172
+ guard, non dal modello`) e una guideline ai tool scoraggia le imitazioni.
169
173
 
170
174
  #### Nuovi progetti: zero manifest locale
171
175
 
@@ -32,6 +32,7 @@ import {
32
32
  OUTPUT_QUESTIONS,
33
33
  buildOutputState,
34
34
  createVerdictCache,
35
+ elideText,
35
36
  evaluateOutput,
36
37
  outputKey,
37
38
  toolResultText,
@@ -52,6 +53,7 @@ import {
52
53
  decideGroundingAction,
53
54
  evaluateGrounding,
54
55
  groundingKey,
56
+ guardEvidenceEntry,
55
57
  recordEvidence,
56
58
  recordToolResult,
57
59
  parseExitCode,
@@ -73,6 +75,7 @@ import {
73
75
  selectVerificationChecks,
74
76
  trustVerificationPolicy,
75
77
  trustedVerificationPolicy,
78
+ verificationNotice,
76
79
  type VerificationManifestTrust,
77
80
  type VerificationStatus,
78
81
  } from "../src/verification.ts";
@@ -224,7 +227,7 @@ export default function (pi: ExtensionAPI) {
224
227
  * Esegue SOLO check estratti dalla policy acquisita (locale, globale o
225
228
  * profilo integrato). Serve sia al tool del modello sia a /jev verify.
226
229
  */
227
- async function runProjectVerification(args: {
230
+ async function executeProjectVerification(args: {
228
231
  cwd: string;
229
232
  selection: string | undefined;
230
233
  signal?: AbortSignal;
@@ -273,6 +276,27 @@ export default function (pi: ExtensionAPI) {
273
276
  };
274
277
  }
275
278
 
279
+ /** Wrapper audit: ogni run (tool o comando) è evidenza per il grounding. */
280
+ async function runProjectVerification(args: {
281
+ cwd: string;
282
+ selection: string | undefined;
283
+ signal?: AbortSignal;
284
+ trusted: boolean;
285
+ }): Promise<VerificationToolRunResult> {
286
+ const outcome = await executeProjectVerification(args);
287
+ recordGuardFact({
288
+ tool: "jev_verify",
289
+ summary: `jev_verify: ${args.selection ?? "recommended"} → ${
290
+ outcome.results.length > 0
291
+ ? outcome.results.map((r) => `${r.id}:${r.ok ? "pass" : "fail"}`).join(", ")
292
+ : "nessun check eseguito"
293
+ }`,
294
+ ok: outcome.results.length > 0 && outcome.results.every((r) => r.ok),
295
+ excerpt: outcome.message,
296
+ });
297
+ return outcome;
298
+ }
299
+
276
300
  /** Provider live corrente dal registry (sempre fresco, mai snapshot). */
277
301
  function liveUpstreamProvider(
278
302
  registry: ModelRegistry,
@@ -860,6 +884,39 @@ export default function (pi: ExtensionAPI) {
860
884
  }
861
885
  }
862
886
 
887
+ /**
888
+ * I fatti generati dal guard (footer di verifica, run jev_verify) sono
889
+ * evidenza di prima classe per il grounding: senza questo, citare "il guard
890
+ * riporta X" risulterebbe sempre un'affermazione non supportata.
891
+ */
892
+ function recordGuardFact(args: {
893
+ tool: "jev-guard" | "jev_verify";
894
+ summary: string;
895
+ ok: boolean;
896
+ excerpt?: string;
897
+ }): void {
898
+ try {
899
+ if (sessionMode !== "automatic" || !config.grounding.enabled) return;
900
+ const excerpt = args.excerpt?.trim()
901
+ ? elideText(args.excerpt.trim(), Math.max(0, config.grounding.excerptChars))
902
+ : undefined;
903
+ const entry = guardEvidenceEntry({
904
+ tool: args.tool,
905
+ summary: args.summary,
906
+ ok: args.ok,
907
+ ...(excerpt ? { excerpt } : {}),
908
+ seq: ++evidenceSeq,
909
+ at: Date.now(),
910
+ });
911
+ recordEvidence(evidenceLedger, entry, {
912
+ maxEntries: config.grounding.maxEntries,
913
+ invalidateTestsOnEdit: config.grounding.invalidateTestsOnEdit,
914
+ });
915
+ } catch {
916
+ // Mai rompere il flusso per il ledger.
917
+ }
918
+ }
919
+
863
920
  function throttledGroundingError(error: unknown): void {
864
921
  const now = Date.now();
865
922
  if (now - lastGroundingErrorAt < 60_000) return;
@@ -996,6 +1053,14 @@ export default function (pi: ExtensionAPI) {
996
1053
  return async () => {
997
1054
  const { status } = verificationSnapshot(cwd);
998
1055
  if (status.changedFiles.length === 0) return { action: "publish" };
1056
+ // Il footer è un fatto del guard: entra nell'evidenza grounding,
1057
+ // così citarlo ("il guard riporta X") resta un claim verificabile.
1058
+ recordGuardFact({
1059
+ tool: "jev-guard",
1060
+ summary: `jev-guard: policy=${status.manifestSourceLabel ?? "sconosciuta"}; modifiche=${status.changedFiles.join(", ") || "nessuna"}; check=${status.checks.map((item) => `${item.check.id}:${item.state}`).join(", ") || "nessuno"}`,
1061
+ ok: status.allPassed,
1062
+ ...(verificationNotice(status) ? { excerpt: verificationNotice(status) } : {}),
1063
+ });
999
1064
  const decision = decideVerificationAction({
1000
1065
  status,
1001
1066
  mode,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-jev-guard",
3
- "version": "0.7.1",
3
+ "version": "0.7.2",
4
4
  "type": "module",
5
5
  "description": "Jev validation guard for pi: on-demand tool + automatic provider gate. Dual backend: TypeSafe direct + OpenRouter.",
6
6
  "keywords": [
package/src/grounding.ts CHANGED
@@ -21,7 +21,7 @@ import type {
21
21
 
22
22
  export type GroundingMode = "warn" | "repair" | "hold";
23
23
 
24
- export type EvidenceKind = "command" | "test" | "read" | "modify";
24
+ export type EvidenceKind = "command" | "test" | "read" | "modify" | "verification";
25
25
 
26
26
  export interface EvidenceEntry {
27
27
  seq: number;
@@ -49,6 +49,38 @@ export function isTestCommand(command: string): boolean {
49
49
  return TEST_COMMAND.test(command);
50
50
  }
51
51
 
52
+ /**
53
+ * Prove di esito (test o verifica progetto): una modifica successiva le rende
54
+ * stale — non provano più lo stato attuale del codice.
55
+ */
56
+ export function isOutcomeEvidence(kind: EvidenceKind): boolean {
57
+ return kind === "test" || kind === "verification";
58
+ }
59
+
60
+ /**
61
+ * Fatto generato dal guard (footer di verifica, run jev_verify): entra
62
+ * nell'evidenza al pari dei tool_result, così le affermazioni
63
+ * "il guard riporta X" sono verificabili invece di sembrare inventate.
64
+ */
65
+ export function guardEvidenceEntry(args: {
66
+ tool: "jev-guard" | "jev_verify";
67
+ summary: string;
68
+ ok: boolean;
69
+ seq: number;
70
+ at: number;
71
+ excerpt?: string;
72
+ }): EvidenceEntry {
73
+ return {
74
+ seq: args.seq,
75
+ at: args.at,
76
+ tool: args.tool,
77
+ kind: "verification",
78
+ summary: args.summary,
79
+ ok: args.ok,
80
+ ...(args.excerpt ? { excerpt: args.excerpt } : {}),
81
+ };
82
+ }
83
+
52
84
  const EXIT_CODE = /Command exited with code (\d+)/;
53
85
 
54
86
  /** Exit code dal testo d'errore pi ("Command exited with code N"). */
@@ -152,7 +184,7 @@ export function recordEvidence(
152
184
  let invalidated = 0;
153
185
  if (opts.invalidateTestsOnEdit && entry.kind === "modify" && entry.ok) {
154
186
  for (const prior of ledger) {
155
- if (prior.kind === "test" && !prior.stale && prior.seq < entry.seq) {
187
+ if (isOutcomeEvidence(prior.kind) && !prior.stale && prior.seq < entry.seq) {
156
188
  prior.stale = true;
157
189
  invalidated++;
158
190
  }
@@ -197,7 +229,7 @@ export function buildEvidenceDigest(
197
229
  }
198
230
  return {
199
231
  entries,
200
- hasStaleTests: recent.some((e) => e.kind === "test" && e.stale),
232
+ hasStaleTests: recent.some((e) => isOutcomeEvidence(e.kind) && e.stale),
201
233
  };
202
234
  }
203
235
 
package/src/tools.ts CHANGED
@@ -20,6 +20,7 @@ export function registerJevTool(
20
20
  "Use jev_validate when an independent targeted check is useful.",
21
21
  "Do not describe review or unavailable as successful validation.",
22
22
  "Jev validation does not replace compilation or tests.",
23
+ "Do not write your own verification-status sections in replies: the guard appends the only authoritative footer.",
23
24
  ],
24
25
  parameters: Type.Object({
25
26
  requirements: Type.String({ minLength: 1, maxLength: 16000 }),
@@ -34,6 +34,7 @@ export function registerVerificationTool(
34
34
  "After a successful edit/write, call jev_verify with check='recommended' when verification is enabled.",
35
35
  "Use only this tool's declared check ids; never claim code compiles or tests pass without its observed result.",
36
36
  "If a check fails, inspect its actual output, repair the code, then run the check again.",
37
+ "Never write your own verification report or imitate the guard footer in replies: the guard appends the only authoritative footer.",
37
38
  ],
38
39
  parameters: Type.Object({
39
40
  check: Type.Optional(