@pablozrrrr/jevguard 0.2.0 → 0.2.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/README.md CHANGED
@@ -52,6 +52,8 @@ JevGuard makes those questions versioned, scoped, and machine-actionable.
52
52
 
53
53
  severity: error
54
54
  scope: backend/**
55
+ scope: !backend/generated/**
56
+ evidence: code
55
57
 
56
58
  ### Rule
57
59
 
@@ -66,6 +68,15 @@ A controller performs domain decisions, calculations, or state mutations directl
66
68
  Validation, HTTP mapping and delegation to services.
67
69
  ```
68
70
 
71
+ `scope:` may repeat. Each line adds an inclusion glob, and a leading `!` adds an
72
+ exclusion, so a path is in scope when it matches at least one inclusion and no
73
+ exclusion. `evidence:` chooses which files the rule is evaluated against — `code`
74
+ (the default), `docs`, or `any`. Because the default is `code`, prose documentation
75
+ (`.md`, `.mdx`, `.txt`) no longer participates in a rule's evidence unless the rule
76
+ opts in with `evidence: docs` or `evidence: any`; this is intentional. Config and
77
+ data formats (`.json`, `.yaml`, `.toml`, `.xml`, `.csv`, and others) stay in the
78
+ `code` class, so config rules keep their coverage.
79
+
69
80
  The rule stays in the repository beside the code it governs. `Allowed` is a real
70
81
  exception, not a suggestion. JevGuard evaluates it as part of the same rule.
71
82
 
@@ -86,11 +97,18 @@ applicable rules
86
97
  one semantic judgment per rule
87
98
  ```
88
99
 
89
- If complete attributed evidence is unavailable, too large, invalid, or contains a
90
- blocked sensitive file, JevGuard marks the affected rule `UNAVAILABLE`. It never
91
- turns incomplete evidence into a reassuring verdict. Evidence problems are scoped
92
- to the rules they affect: a rule whose applicable files are all safe still runs
93
- even when another rule's evidence is blocked.
100
+ If complete attributed evidence is unavailable, invalid, or contains a blocked
101
+ sensitive file, JevGuard marks the affected rule `UNAVAILABLE`. It never turns
102
+ incomplete evidence into a reassuring verdict. Evidence problems are scoped to the
103
+ rules they affect: a rule whose applicable files are all safe still runs even when
104
+ another rule's evidence is blocked.
105
+
106
+ Oversized evidence is no longer refused outright. A scoped diff above
107
+ `maxDiffLength` is split, in attributed file order, into one slice per file; an
108
+ individually oversized file is split into its ordered unified-diff hunks with the file
109
+ preamble repeated in every slice. Nothing is truncated, summarized, or dropped: an
110
+ unparseable oversized file or an indivisible oversized hunk makes the whole rule
111
+ `UNAVAILABLE/OVERSIZED_DIFF`.
94
112
 
95
113
  Some failures happen before any policy rule is evaluated. If the attributed diff
96
114
  cannot be built, the review is a single synthetic `UNAVAILABLE` entry with no rule
@@ -114,7 +132,15 @@ policy that translates it into an outcome.
114
132
  | `warning` | `< 60%` | `≥ 60%` | never |
115
133
 
116
134
  Those thresholds are per repository rule and locally configurable in
117
- `.jev/config.yaml`.
135
+ `.jev/config.yaml`. Slicing does not change them: each slice is gated independently,
136
+ the rule outcome is the highest slice outcome (`FAIL > WARN > PASS`), and the rule's
137
+ `violationProbability` is the maximum across its slices. `PASS` requires every planned
138
+ slice to pass. A rule may produce at most 16 slices, and slicing may add at most 32
139
+ repository-rule Jev calls per turn beyond one per applicable rule; a rule that does not
140
+ fit is `UNAVAILABLE/SLICE_LIMIT_EXCEEDED` with no Jev call and later rules still use the
141
+ remaining budget. Any failed, blocked, or out-of-range slice makes the whole rule
142
+ `UNAVAILABLE`, and no sibling probability is presented as a verdict. The built-ins do
143
+ not slice.
118
144
 
119
145
  JevGuard also runs two product-owned built-ins on every attributed turn,
120
146
  `SCOPE-CREEP` and `COMPLEXITY`. Both receive the turn's complete, safe attributed
@@ -166,13 +192,18 @@ proposal request to a hidden `jevguard-proposer` subagent:
166
192
  `jevguard-proposer` subagent has wildcard-deny permissions and wildcard-disabled
167
193
  tools, so it cannot call a tool, edit a file, or produce a patch. It returns a
168
194
  strategy and one manual apply instruction only.
195
+ - `remediation.model` is optional. When set in `.jev/config.yaml`, it selects the
196
+ proposer model; when it is absent, the proposer inherits the host's model.
169
197
  - A generic toast reports that the proposal is ready in the child session. It carries
170
198
  no rule, task, diff, finding, or credential.
171
199
 
172
200
  The plugin creates at most one proposal per evaluated turn, and the child session is
173
201
  excluded from review for the plugin lifetime, so a proposal can never recurse or
174
202
  trigger a second one. A host, model, or toast failure is contained and never changes
175
- the review.
203
+ the review. A contained proposal failure — an invalid model specifier, a child-session
204
+ failure, or a prompt failure — shows a generic error toast and writes one structured
205
+ `error` log entry carrying only a typed reason code, never any task, diff, finding,
206
+ path, or credential.
176
207
 
177
208
  The proposer is a strategy step, not an apply step: it never edits code, and nothing
178
209
  is applied automatically. You read the proposal, copy its manual apply instruction
@@ -187,8 +218,29 @@ or secret is logged.
187
218
 
188
219
  ## Install
189
220
 
190
- Once `@pablozrrrr/jevguard` is installed, the intended setup is one line in
191
- `opencode.json`:
221
+ Installing `@pablozrrrr/jevguard` does **not** register it with OpenCode by itself.
222
+ OpenCode only loads plugins listed in `plugin: [...]` in `opencode.json`, so register
223
+ the plugin from the installed CLI:
224
+
225
+ ```sh
226
+ # global config: ~/.config/opencode/opencode.json
227
+ jevguard install
228
+
229
+ # project config: ./opencode.json
230
+ jevguard install --project
231
+ ```
232
+
233
+ The installer asks for confirmation, edits only the `plugin` array, and is
234
+ idempotent. It only edits a `plugin` array of plain strings, so OpenCode's
235
+ `[["pkg", { "options": {} }]]` tuple form is a known limitation: the installer leaves
236
+ the file unchanged and prints the manual snippet instead. If `jevguard` is not on
237
+ `PATH`, run it through `bunx`:
238
+
239
+ ```sh
240
+ bunx --package @pablozrrrr/jevguard jevguard install
241
+ ```
242
+
243
+ The manual alternative is one entry in `opencode.json`:
192
244
 
193
245
  ```json
194
246
  { "plugin": ["@pablozrrrr/jevguard"] }
@@ -347,6 +399,34 @@ synthetic entry a review-level failure produces.
347
399
  rule had no applicable change to evaluate. `UNAVAILABLE` means JevGuard could not
348
400
  safely or completely evaluate that rule.
349
401
 
402
+ ## Review history
403
+
404
+ JevGuard keeps a bounded, local, content-free history of your reviews so you can see
405
+ how turns fared without grepping the host log. Every review appends the same safe
406
+ structured-log projection plus an ISO 8601 timestamp to one JSON Lines file:
407
+
408
+ ```text
409
+ <XDG_DATA_HOME or ~/.local/share>/jevguard/reviews.jsonl
410
+ ```
411
+
412
+ The file holds the aggregate counts, each result's rule or check ID, its outcome, and
413
+ its typed reason or raw probability — the same allowlist as the structured log. It
414
+ never holds a task, diff, prompt, model output, credential, or environment value. It
415
+ is local-only, bounded (it rewrites itself once it passes 5 MB, keeping the newest
416
+ records), and safe to delete at any time; the next review recreates it.
417
+
418
+ ```sh
419
+ # human-readable summary
420
+ jevguard report
421
+
422
+ # the same aggregation as JSON
423
+ jevguard report --json
424
+ ```
425
+
426
+ The report prints aggregated counts, rule IDs, check IDs, and reason codes only —
427
+ never file paths, tasks, diffs, or credentials. A missing or empty history is a
428
+ normal empty report, not an error.
429
+
350
430
  ## Implemented behavior
351
431
 
352
432
  The current release implements the full local review path:
@@ -357,10 +437,13 @@ The current release implements the full local review path:
357
437
  - Every `## <RULE-ID>` block in `.jev/rules.md` is parsed in source order. Each
358
438
  block is validated independently, so one invalid block does not suppress its
359
439
  valid siblings, and every occurrence of a duplicated ID is invalid.
360
- - Each valid rule is processed independently. It asks Jev at most once, and only
361
- when the gate config is valid, the rule is applicable, and its scoped evidence is
362
- complete and safe; it produces one typed violation probability or an operational
363
- outcome.
440
+ - Each valid rule is processed independently. Its complete scoped evidence is planned
441
+ locally, then every planned slice across all rules is dispatched through one batched
442
+ Jev entry that resolves the credential once and uses bounded concurrency; each slice
443
+ asks one Noul. The rule result is the deterministic maximum of its slice judgments,
444
+ and a fitting rule still issues exactly one call. The rule asks Jev only when the
445
+ gate config is valid, the rule is applicable, and its scoped evidence is complete and
446
+ safe; it produces one typed violation probability or an operational outcome.
364
447
  - A failure before rule evaluation — no attributed diff, unreadable policy files, or
365
448
  a missing `.jev/rules.md` — is reported by the rule lane as one synthetic
366
449
  `UNAVAILABLE` entry with no rule ID, not one result per declared rule. When the
@@ -375,12 +458,14 @@ The current release implements the full local review path:
375
458
  - Both built-ins are sent as one batch request with two independent named answers.
376
459
  One malformed or missing answer fails only its own check; the valid sibling still
377
460
  gates. A failed or malformed batch envelope makes both checks `UNAVAILABLE`.
378
- - The built-in batch runs concurrently with the sequential rule lane. One shared FIFO
379
- concurrency limit allows at most two Jev requests in flight across the plugin
380
- instance, counting the batch as one request. With no attributed patch a built-in is
381
- `SKIPPED`; blocked or oversized evidence is `UNAVAILABLE`; a rule, policy-load, or
382
- config failure never suppresses the batch, and one built-in's answer never
383
- suppresses the other.
461
+ - The built-in batch runs concurrently with the rule lane. The rule lane dispatches
462
+ one batched request for all of the turn's planned slices with a bounded adapter
463
+ concurrency cap of four; the built-in batch keeps its own single request. One shared
464
+ FIFO concurrency limit allows at most two Jev entries in flight across the plugin
465
+ instance, counting the rule batch and the built-in batch as one entry each. With no
466
+ attributed patch a built-in is `SKIPPED`; blocked or oversized evidence is
467
+ `UNAVAILABLE`; a rule, policy-load, or config failure never suppresses the batch, and
468
+ one built-in's answer never suppresses the other.
384
469
  - Reviews run in the background. The idle event resolves as soon as the serialized
385
470
  attribution step is scheduled, so policy reads, Jev calls, and presentation do not
386
471
  block the agent.