failproofai 1.0.8 → 1.0.9-beta.0
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/.next/standalone/.next/BUILD_ID +1 -1
- package/.next/standalone/.next/build-manifest.json +3 -3
- package/.next/standalone/.next/prerender-manifest.json +4 -4
- package/.next/standalone/.next/required-server-files.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_global-error.html +1 -1
- package/.next/standalone/.next/server/app/_global-error.rsc +7 -7
- package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +6 -6
- package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
- package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_not-found.html +1 -1
- package/.next/standalone/.next/server/app/_not-found.rsc +15 -15
- package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +15 -15
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +14 -14
- package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/api/audit/invite/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/audit/run/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/audit/status/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/login-request/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/login-verify/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/logout/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/status/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/audit/page/server-reference-manifest.json +2 -2
- package/.next/standalone/.next/server/app/audit/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/audit/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/index.html +1 -1
- package/.next/standalone/.next/server/app/index.rsc +15 -15
- package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +14 -14
- package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +15 -15
- package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +14 -14
- package/.next/standalone/.next/server/app/policies/page.js +3 -3
- package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +2 -2
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/projects/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/settings/page/server-reference-manifest.json +8 -8
- package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0o07qi9._.js +1 -1
- package/.next/standalone/.next/server/chunks/_09dz7xv._.js +20 -20
- package/.next/standalone/.next/server/chunks/_0tovk6q._.js +1 -1
- package/.next/standalone/.next/server/chunks/_0trp3yc._.js +1 -1
- package/.next/standalone/.next/server/chunks/_1ek68ln._.js +2 -2
- package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4v._.js +1 -1
- package/.next/standalone/.next/server/chunks/src_hooks_1aveq0u._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__04usis8._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__056wjo4._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0n0xg95._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0rwtwpm._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0tmz862._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__11mayhe._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__15578wp._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1m_svbe._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1pprgri._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1q4p5b8._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/_042cgl1._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_08x1r5t._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_0bqoto4._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{_0-vcssj._.js → _0n0lja6._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_1gb0ifp._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/_1p3iz2d._.js +23 -0
- package/.next/standalone/.next/server/chunks/ssr/_1zopuov._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_next-internal_server_app_policies_page_actions_1sp2-yo.js +3 -3
- package/.next/standalone/.next/server/chunks/ssr/app_actions_get-scheduled-audit_ts_0ei9sni._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_1kp6l3x._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_19dqvpc._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_settings_02tf1h4._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_effective-reviewers_ts_1h4wtvo._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_semantic_pack-policies_ts_0gh_bu_._.js +1 -1
- package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
- package/.next/standalone/.next/server/pages/404.html +1 -1
- package/.next/standalone/.next/server/pages/500.html +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.json +23 -23
- package/.next/standalone/.next/static/chunks/{3c3qbmosdjl6w.js → 0247hc9ath5t-.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0m-9d6yn9hx4j.js → 03001k9jy-kst.js} +1 -1
- package/.next/standalone/.next/static/chunks/{2qdpj67x6ifk_.js → 06k7gsfnb7jkh.js} +1 -1
- package/.next/standalone/.next/static/chunks/0qejem4-zt-hy.js +1 -0
- package/.next/standalone/.next/static/chunks/{21uv-uusw329x.js → 0snylg3y1i47a.js} +1 -1
- package/.next/standalone/.next/static/chunks/1h0if8ky362as.js +1 -0
- package/.next/standalone/.next/static/chunks/{0wbjy0zo-m7is.js → 2qsrrrl2maqsb.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0bhidk90e-07f.css → 3_e_wg5_oay87.css} +1 -1
- package/.next/standalone/.next/static/chunks/3ns-egfh8i8i5.js +6 -0
- package/.next/standalone/.next/static/chunks/3rq7fjj9we0v3.js +1 -0
- package/.next/standalone/.next/static/chunks/42m0f2hh8vfb1.js +1 -0
- package/.next/standalone/app/actions/get-jev-config.ts +2 -2
- package/.next/standalone/app/actions/update-jev-config.ts +12 -9
- package/.next/standalone/app/components/jev-notices.tsx +10 -10
- package/.next/standalone/app/settings/jev-panel.tsx +11 -11
- package/.next/standalone/fp-cloud-cli/CHANGELOG.md +2 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/output.py +26 -0
- package/.next/standalone/fp-cloud-cli/tests/test_output.py +27 -0
- package/.next/standalone/hermes-plugin/README.md +18 -8
- package/.next/standalone/package.json +10 -10
- package/.next/standalone/server.js +1 -1
- package/bin/failproofai.mjs +33 -7
- package/dist/cli.mjs +556 -521
- package/dist/worker.mjs +56 -375
- package/hermes-plugin/README.md +18 -8
- package/package.json +10 -10
- package/scripts/build-policy-pack.mjs +6 -9
- package/src/hooks/cloud-connection.ts +12 -6
- package/src/hooks/custom-hooks-loader.ts +1 -1
- package/src/hooks/effective-reviewers.ts +50 -43
- package/src/hooks/handler.ts +25 -16
- package/src/hooks/hermes-update.ts +74 -0
- package/src/hooks/hook-activity-store.ts +3 -3
- package/src/hooks/integrations.ts +349 -55
- package/src/hooks/jev-activity.ts +3 -3
- package/src/hooks/jev-cli.ts +17 -17
- package/src/hooks/jev-cloud-connection.ts +4 -4
- package/src/hooks/manager.ts +3 -2
- package/src/hooks/pack-cli.ts +24 -23
- package/src/hooks/pack-manifest.ts +25 -2
- package/src/hooks/pack-store.ts +2 -2
- package/src/hooks/policy-authority.ts +51 -32
- package/src/hooks/policy-evaluator.ts +5 -5
- package/src/hooks/policy-registry.ts +5 -7
- package/src/hooks/policy-reviewability.ts +26 -10
- package/src/hooks/policy-types.ts +4 -4
- package/src/hooks/semantic/combine.ts +13 -13
- package/src/hooks/semantic/evaluator.ts +5 -5
- package/src/hooks/semantic/jev-client.ts +1 -1
- package/src/hooks/semantic/jev-config.ts +21 -12
- package/src/hooks/semantic/jev-review.ts +3 -3
- package/src/hooks/semantic/jev-stats.ts +17 -17
- package/src/hooks/semantic/pack-policies.ts +48 -48
- package/src/hooks/semantic/policies.ts +13 -425
- package/src/hooks/semantic/precondition-names.ts +1 -1
- package/src/hooks/semantic/preconditions.ts +1 -1
- package/src/hooks/semantic/types.ts +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__13t1zkw._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_13kfn90._.js +0 -23
- package/.next/standalone/.next/static/chunks/06dnzbolj00mc.js +0 -6
- package/.next/standalone/.next/static/chunks/16f3fa-lx38hk.js +0 -1
- package/.next/standalone/.next/static/chunks/2g2tki08kdhie.js +0 -1
- package/.next/standalone/.next/static/chunks/3-nbtkhg9y-1j.js +0 -1
- package/.next/standalone/.next/static/chunks/355km0ihuqo1p.js +0 -1
- /package/.next/standalone/.next/static/{5yKM8NoTOpUI0MoQl8eo5 → z1C0kJVF0NCtWn5R88hW3}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{5yKM8NoTOpUI0MoQl8eo5 → z1C0kJVF0NCtWn5R88hW3}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{5yKM8NoTOpUI0MoQl8eo5 → z1C0kJVF0NCtWn5R88hW3}/_ssgManifest.js +0 -0
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* `config --token` with a key that carries `jev:evaluate` stores the key in the
|
|
5
5
|
* `jev` slot of `credentials.json` (`cloud-connection.ts`) and then, ONLY when
|
|
6
6
|
* this machine has no `jev.json` at all, writes one that turns Jev on through
|
|
7
|
-
* FailproofAI Cloud in
|
|
7
|
+
* FailproofAI Cloud in observe mode — logged, never enforced, until its owner
|
|
8
8
|
* says otherwise. Never under `--no-transcripts`: that connection asked for
|
|
9
9
|
* decisions only, so it stores the key and says Jev is available, and
|
|
10
10
|
* `jev setup --provider failproofai` is the opt-in. `config --disconnect`
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
* status screen. A BYOK file keeps working without the Cloud and is not the
|
|
30
30
|
* Cloud's to delete, so it stays; so does a file this build cannot read, and a
|
|
31
31
|
* Cloud file switched off — `--mode off` is the owner's opt-out, and deleting
|
|
32
|
-
* it would let the next connect write a fresh
|
|
32
|
+
* it would let the next connect write a fresh observe-mode file. The
|
|
33
33
|
* file is moved aside before it is judged, so the file judged is the file
|
|
34
34
|
* deleted — see `removeCloudJevConfig`.
|
|
35
35
|
*
|
|
@@ -67,7 +67,7 @@ import {
|
|
|
67
67
|
} from "./semantic/jev-config";
|
|
68
68
|
|
|
69
69
|
/** The mode a Cloud `jev.json` starts in (the user's decision: log first, enforce later). */
|
|
70
|
-
export const CLOUD_JEV_INITIAL_MODE = "
|
|
70
|
+
export const CLOUD_JEV_INITIAL_MODE = "observe" as const;
|
|
71
71
|
|
|
72
72
|
export type CloudJevConfigWrite =
|
|
73
73
|
| { status: "written"; path: string; mode: typeof CLOUD_JEV_INITIAL_MODE; baseUrl: string }
|
|
@@ -145,7 +145,7 @@ export function existingJevConfig(cloudBase: string): CloudJevConfigWrite | null
|
|
|
145
145
|
* Asked the way a hook asks (`inspectJevConfig`), so "on" here means a tool
|
|
146
146
|
* call's next Jev request really goes to FailproofAI Cloud.
|
|
147
147
|
*/
|
|
148
|
-
export function cloudJevRunningMode(): "
|
|
148
|
+
export function cloudJevRunningMode(): "observe" | "enforce" | null {
|
|
149
149
|
try {
|
|
150
150
|
const r = inspectJevConfig();
|
|
151
151
|
if (r.status !== "ok" || r.config.provider !== JEV_CLOUD_PROVIDER) return null;
|
package/src/hooks/manager.ts
CHANGED
|
@@ -28,7 +28,7 @@ import { customPoliciesDir, globalPolicyConfigFile } from "./fp-home";
|
|
|
28
28
|
import { readActiveCloudManagedPolicies } from "./cloud-managed-policies";
|
|
29
29
|
import { CORE_SOURCE, addPack, setPackPolicyEnabled } from "./pack-store";
|
|
30
30
|
import type { ResolvedPack } from "./pack-manifest";
|
|
31
|
-
import {
|
|
31
|
+
import { hasInstalledRegexPacks, readInstalledPacks } from "./pack-manifest";
|
|
32
32
|
import { packPolicyParamKey } from "./policy-evaluator";
|
|
33
33
|
import { probeDaemonPolicyEvaluation } from "./daemon-service";
|
|
34
34
|
import {
|
|
@@ -654,7 +654,8 @@ async function installHooksImpl(
|
|
|
654
654
|
//
|
|
655
655
|
// With a pack already installed the names are switched on individually
|
|
656
656
|
// instead, which is additive and touches nothing else.
|
|
657
|
-
|
|
657
|
+
// A Jev-only pack has no policy to switch on, so it does not count here.
|
|
658
|
+
if (!hasInstalledRegexPacks()) {
|
|
658
659
|
// Fetched, not unpacked from this package: there is no copy in here any
|
|
659
660
|
// more. That makes this the one path in `policies --install` that needs
|
|
660
661
|
// the network, so its failure is reported rather than thrown — the names
|
package/src/hooks/pack-cli.ts
CHANGED
|
@@ -26,7 +26,7 @@ import { compareVersions, parseSemver } from "./semver-precedence";
|
|
|
26
26
|
// budget, which means reaching the semantic side. This is a CLI module — loaded
|
|
27
27
|
// by `failproofai publish`, never by a hook — so the rule that keeps those
|
|
28
28
|
// modules off an unconfigured machine's hook path does not apply here.
|
|
29
|
-
import {
|
|
29
|
+
import { JEV_POLICIES_QUESTION_CHARS, MAX_PACK_QUESTION_CHARS, questionChars, semanticPoliciesFromPacks } from "./semantic/pack-policies";
|
|
30
30
|
import {
|
|
31
31
|
AmbiguousPackId,
|
|
32
32
|
PACK_CHECKSUMS_ASSET,
|
|
@@ -484,19 +484,20 @@ async function build(rest: string[]): Promise<PackCliResult> {
|
|
|
484
484
|
// the user's machine, in manifest order — a pack that enforces less than it
|
|
485
485
|
// says, which is the failure this whole lane is built to avoid.
|
|
486
486
|
//
|
|
487
|
-
// A pack from outside FailproofAI
|
|
488
|
-
//
|
|
489
|
-
//
|
|
487
|
+
// A pack from outside FailproofAI shares one request with FailproofAI's own
|
|
488
|
+
// checks wherever both are installed, and those spend the budget first
|
|
489
|
+
// (`semanticPoliciesFromPacks`), so it is held to what they leave.
|
|
490
490
|
const questionCost = semantic.reduce((total, entry) => total + questionChars(entry), 0);
|
|
491
491
|
const firstParty = isFirstPartyPack(packSource);
|
|
492
|
-
const questionBudget = MAX_PACK_QUESTION_CHARS - (firstParty ? 0 :
|
|
492
|
+
const questionBudget = MAX_PACK_QUESTION_CHARS - (firstParty ? 0 : JEV_POLICIES_QUESTION_CHARS);
|
|
493
493
|
if (questionCost > questionBudget) {
|
|
494
494
|
return fail([
|
|
495
495
|
`This pack's ${semantic.length} semantic policies compile to ${questionCost} characters of questions, ` +
|
|
496
496
|
(firstParty
|
|
497
497
|
? `over the ${MAX_PACK_QUESTION_CHARS} one Jev request has room for.`
|
|
498
498
|
: `over the ${questionBudget} a machine leaves a pack from outside FailproofAI: one Jev request has room ` +
|
|
499
|
-
`for ${MAX_PACK_QUESTION_CHARS}, and
|
|
499
|
+
`for ${MAX_PACK_QUESTION_CHARS}, and FailproofAI/jev-policies' 16 checks take ${JEV_POLICIES_QUESTION_CHARS} of it first ` +
|
|
500
|
+
`where both are installed.`),
|
|
500
501
|
"Shorten the probe instructions and criteria, or ship fewer policies per pack.",
|
|
501
502
|
]);
|
|
502
503
|
}
|
|
@@ -511,9 +512,9 @@ async function build(rest: string[]): Promise<PackCliResult> {
|
|
|
511
512
|
// Judged against the reviewers the PACK SHIPS WITH, not this build's: a pack
|
|
512
513
|
// that carries both tiers replaces the compiled semantic set on every machine
|
|
513
514
|
// that installs it, so `reviewedBy: ["its-own-check"]` is exactly right and
|
|
514
|
-
//
|
|
515
|
-
// with no semantic entries
|
|
516
|
-
// machines
|
|
515
|
+
// FailproofAI's names would call it a name "this build does not have". A pack
|
|
516
|
+
// with no semantic entries answers to FailproofAI's sixteen names, which is
|
|
517
|
+
// what `FailproofAI/jev-policies` brings to the machines that install both.
|
|
517
518
|
const reviewers = semantic.length > 0 ? new Set(semantic.map((s) => s.name)) : undefined;
|
|
518
519
|
const authorityProblems = hooks.flatMap((hook) => {
|
|
519
520
|
const problem = authorityProblem({ authority: hook.authority, reviewedBy: hook.reviewedBy }, reviewers);
|
|
@@ -590,8 +591,8 @@ async function build(rest: string[]): Promise<PackCliResult> {
|
|
|
590
591
|
//
|
|
591
592
|
// `minCliVersion` and `semantic` follow the same rule, and for `semantic` it is
|
|
592
593
|
// load-bearing rather than tidy: an EMPTY array would still be "a pack that
|
|
593
|
-
// declares semantic entries" to a careless reader,
|
|
594
|
-
//
|
|
594
|
+
// declares semantic entries" to a careless reader, which would count it as a
|
|
595
|
+
// pack giving Jev checks when it gives none.
|
|
595
596
|
const manifest =
|
|
596
597
|
JSON.stringify(
|
|
597
598
|
{
|
|
@@ -631,7 +632,7 @@ async function build(rest: string[]): Promise<PackCliResult> {
|
|
|
631
632
|
`(${questionCost} characters of questions), ` +
|
|
632
633
|
(identity.effect === "observe"
|
|
633
634
|
? "not asked where it installs: Jev asks only the checks of packs that enforce."
|
|
634
|
-
:
|
|
635
|
+
: "asked by Jev wherever it installs."),
|
|
635
636
|
]
|
|
636
637
|
: []),
|
|
637
638
|
...(requiredCli ? [` Requires failproofai ${requiredCli} or newer.`] : []),
|
|
@@ -2651,17 +2652,17 @@ function semanticPhrase(count: number): string {
|
|
|
2651
2652
|
}
|
|
2652
2653
|
|
|
2653
2654
|
/**
|
|
2654
|
-
*
|
|
2655
|
-
*
|
|
2656
|
-
*
|
|
2657
|
-
*
|
|
2658
|
-
*
|
|
2655
|
+
* Whether a pack's Jev checks are asked, for add, show and the picker alike:
|
|
2656
|
+
* only where `jevPacks` lets the pack take part — never for an observe pack,
|
|
2657
|
+
* only for its agents when scoped. The package ships no checks of its own, so
|
|
2658
|
+
* a pack's are the whole set, beside any other pack's. One phrase, so the
|
|
2659
|
+
* three cannot drift apart again.
|
|
2659
2660
|
*/
|
|
2660
|
-
function
|
|
2661
|
+
function howJevAsksChecks(pack: { source?: string; effect?: PolicyEffect; clis?: string[] | null }): string {
|
|
2661
2662
|
if (jevPacks([{ effect: pack.effect ?? "enforce", clis: null }]).length === 0) {
|
|
2662
2663
|
return "not asked: this pack only observes, and Jev asks only the checks of packs that enforce";
|
|
2663
2664
|
}
|
|
2664
|
-
const how =
|
|
2665
|
+
const how = "asked by Jev on every tool call they apply to";
|
|
2665
2666
|
return pack.clis && pack.clis.length > 0 ? `${how}, for ${pack.clis.join(", ")} only` : how;
|
|
2666
2667
|
}
|
|
2667
2668
|
|
|
@@ -2716,7 +2717,7 @@ async function pickFromSource(
|
|
|
2716
2717
|
if (preview.semantic.length > 0) {
|
|
2717
2718
|
io.stdout.write(
|
|
2718
2719
|
`\n This pack also brings ${semanticPhrase(preview.semantic.length)} (not selectable), ` +
|
|
2719
|
-
`${
|
|
2720
|
+
`${howJevAsksChecks(preview)}.\n See: failproofai policies show ${source}\n\n`,
|
|
2720
2721
|
);
|
|
2721
2722
|
}
|
|
2722
2723
|
const picked = await multiSelect<string>({
|
|
@@ -2851,7 +2852,7 @@ async function add(rest: string[]): Promise<PackCliResult> {
|
|
|
2851
2852
|
// the half somebody installed did nothing and nothing said so.
|
|
2852
2853
|
if (result.semantic > 0) {
|
|
2853
2854
|
lines.push(
|
|
2854
|
-
` ${semanticPhrase(result.semantic)}, ${
|
|
2855
|
+
` ${semanticPhrase(result.semantic)}, ${howJevAsksChecks(result)}. ` +
|
|
2855
2856
|
"They apply only where you configured Jev (`failproofai jev status`).",
|
|
2856
2857
|
);
|
|
2857
2858
|
}
|
|
@@ -3133,7 +3134,7 @@ function relativeAge(iso: string): string {
|
|
|
3133
3134
|
*
|
|
3134
3135
|
* Judged with `resolvePolicyAuthority` against the pack's OWN check names,
|
|
3135
3136
|
* because that is the rule the installing machine applies: a pack declaring
|
|
3136
|
-
* semantic entries
|
|
3137
|
+
* semantic entries supplies the only checks it can name (`effectiveReviewerNames`), so
|
|
3137
3138
|
* its `reviewedBy` may name only its own checks, and a declaration that names
|
|
3138
3139
|
* anything else — or one sitting under `authority: "hard"`, which a manifest may
|
|
3139
3140
|
* carry because the two fields are parsed independently — registers HARD. A
|
|
@@ -3247,7 +3248,7 @@ export function jevChecksSection(
|
|
|
3247
3248
|
"",
|
|
3248
3249
|
...note(
|
|
3249
3250
|
"Nothing toggles them: `--policy` cannot name one and `failproofai policies` never lists them. " +
|
|
3250
|
-
`They arrive whole, ${
|
|
3251
|
+
`They arrive whole, ${howJevAsksChecks(pack)}.`,
|
|
3251
3252
|
opts,
|
|
3252
3253
|
),
|
|
3253
3254
|
...note(
|
|
@@ -152,8 +152,8 @@ export interface InstalledPackRecord {
|
|
|
152
152
|
*
|
|
153
153
|
* `unknown` like `policies`, and optional like it is not: a pack published
|
|
154
154
|
* before this field existed carries none, and every one of them must keep
|
|
155
|
-
* parsing.
|
|
156
|
-
*
|
|
155
|
+
* parsing. The package ships no Jev checks, so these are the only ones a
|
|
156
|
+
* machine asks — see `semantic/pack-policies.ts`.
|
|
157
157
|
*/
|
|
158
158
|
semantic?: unknown;
|
|
159
159
|
/**
|
|
@@ -339,6 +339,29 @@ export function hasInstalledPacks(): boolean {
|
|
|
339
339
|
}
|
|
340
340
|
}
|
|
341
341
|
|
|
342
|
+
/**
|
|
343
|
+
* Whether an installed pack carries regex policies — the question the
|
|
344
|
+
* migration shim and `policies add <name>` actually mean to ask.
|
|
345
|
+
*
|
|
346
|
+
* `hasInstalledPacks` is the wrong question for them since the package stopped
|
|
347
|
+
* shipping Jev's checks: `FailproofAI/jev-policies` is now the normal way to
|
|
348
|
+
* turn Jev on, and it carries no regex policies. Counting it as "a pack is
|
|
349
|
+
* installed" switched off a legacy machine's `enabledPolicies` and sent a later
|
|
350
|
+
* `policies add block-rm-rf` to a pack that cannot enable it — both silently.
|
|
351
|
+
*/
|
|
352
|
+
export function hasInstalledRegexPacks(): boolean {
|
|
353
|
+
try {
|
|
354
|
+
const raw = JSON.parse(readFileSync(installedFilePath(), "utf8")) as { packs?: unknown };
|
|
355
|
+
if (!Array.isArray(raw.packs)) return false;
|
|
356
|
+
return raw.packs.some(
|
|
357
|
+
(p) => !!p && typeof p === "object" && Array.isArray((p as { policies?: unknown }).policies)
|
|
358
|
+
&& ((p as { policies: unknown[] }).policies.length > 0),
|
|
359
|
+
);
|
|
360
|
+
} catch {
|
|
361
|
+
return false;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
342
365
|
export function installedFilePath(): string {
|
|
343
366
|
return process.env.FAILPROOFAI_PACK_DIR
|
|
344
367
|
? resolve(process.env.FAILPROOFAI_PACK_DIR, "installed.json")
|
package/src/hooks/pack-store.ts
CHANGED
|
@@ -1103,8 +1103,8 @@ export async function addPack(
|
|
|
1103
1103
|
// installed did nothing.
|
|
1104
1104
|
//
|
|
1105
1105
|
// Omitted when empty for the same reason the manifest omits it: an empty
|
|
1106
|
-
// array reads as "this pack declares semantic entries"
|
|
1107
|
-
//
|
|
1106
|
+
// array reads as "this pack declares semantic entries" — a pack giving Jev
|
|
1107
|
+
// checks when it gives none.
|
|
1108
1108
|
...(fetched.semantic.length > 0 ? { semantic: fetched.semantic } : {}),
|
|
1109
1109
|
// Recorded so the READER re-checks it. This CLI has already satisfied it or
|
|
1110
1110
|
// refused the install, but the record outlives this CLI: a downgrade, or a
|
|
@@ -10,7 +10,9 @@
|
|
|
10
10
|
*
|
|
11
11
|
* - `hard` is the default and the answer to anything unclear. Absent, a value
|
|
12
12
|
* that is not one of the two, a `reviewedBy` that is empty or malformed or
|
|
13
|
-
* names a check
|
|
13
|
+
* names a check no installed pack declares, or an `alwaysOn` policy: all hard.
|
|
14
|
+
* With no pack declaring Jev checks there are no reviewers at all, and every
|
|
15
|
+
* policy is hard.
|
|
14
16
|
* - `reviewable` means Jev may clear the policy's verdict — and only through the
|
|
15
17
|
* semantic policies it names, every one of which must have been asked.
|
|
16
18
|
*
|
|
@@ -55,16 +57,23 @@ export interface ResolvedAuthority {
|
|
|
55
57
|
}
|
|
56
58
|
|
|
57
59
|
/**
|
|
58
|
-
* The names
|
|
59
|
-
* `
|
|
60
|
+
* The names of the sixteen Jev checks FailproofAI publishes in the
|
|
61
|
+
* `FailproofAI/jev-policies` pack. Not the probes (`INJECTION_PROBE`,
|
|
60
62
|
* `SCOPE_PROBE`, the task probes) — those are inputs to Jev's decision, not
|
|
61
63
|
* checks a regex verdict can be cleared by.
|
|
62
64
|
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
65
|
+
* Names only: the package ships none of the checks themselves, and this list is
|
|
66
|
+
* NOT a set of reviewers any machine has. It is used for two things:
|
|
67
|
+
*
|
|
68
|
+
* - **Reserved names.** Declared by a pack not installed from a FailproofAI
|
|
69
|
+
* repository, one of these is void (`isReservedClaim`), so a third party
|
|
70
|
+
* cannot become the question that clears the core pack's policies.
|
|
71
|
+
* - **Build-time vocabulary.** The core pack (`scripts/build-policy-pack.mjs`)
|
|
72
|
+
* and a regex-only pack being published name these checks in `reviewedBy`
|
|
73
|
+
* while carrying none of them; they live in `FailproofAI/jev-policies`.
|
|
74
|
+
*
|
|
75
|
+
* Pinned to the reference copy of that pack (`__tests__/fixtures/jev-policies.ts`)
|
|
76
|
+
* by `policy-authority.test.ts`.
|
|
68
77
|
*/
|
|
69
78
|
export const SEMANTIC_POLICY_NAMES = [
|
|
70
79
|
"destructive-deletion",
|
|
@@ -85,17 +94,16 @@ export const SEMANTIC_POLICY_NAMES = [
|
|
|
85
94
|
"external-data-egress",
|
|
86
95
|
] as const;
|
|
87
96
|
|
|
97
|
+
/** {@link SEMANTIC_POLICY_NAMES} as a set. Reserved names and build-time vocabulary — never a machine's live reviewers. */
|
|
98
|
+
export const SEMANTIC_REVIEWER_NAMES: ReadonlySet<string> = new Set(SEMANTIC_POLICY_NAMES);
|
|
99
|
+
|
|
88
100
|
/**
|
|
89
|
-
* The
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
* `
|
|
93
|
-
* a `reviewedBy` may use there are the pack's. Callers that know which set is
|
|
94
|
-
* live pass it (`effectiveReviewerNames()` in `effective-reviewers.ts`, which reads
|
|
95
|
-
* the manifest and imports nothing from `semantic/`); everyone else gets this
|
|
96
|
-
* one, which is what a machine with no pack runs.
|
|
101
|
+
* The reviewer set of a machine with no installed pack declaring Jev checks —
|
|
102
|
+
* the vanilla install — and the default wherever a caller does not say which
|
|
103
|
+
* set is live. Empty: with nothing to ask, nothing can clear, and every
|
|
104
|
+
* `reviewable` declaration registers `hard`.
|
|
97
105
|
*/
|
|
98
|
-
export const
|
|
106
|
+
export const NO_REVIEWERS: ReadonlySet<string> = new Set<string>();
|
|
99
107
|
|
|
100
108
|
const isName = (n: unknown): n is string => typeof n === "string" && n.length > 0;
|
|
101
109
|
|
|
@@ -103,7 +111,7 @@ const isName = (n: unknown): n is string => typeof n === "string" && n.length >
|
|
|
103
111
|
* Judge one declaration.
|
|
104
112
|
*
|
|
105
113
|
* Stricter than `effectiveAuthority`, which is the §7 contract and needs only
|
|
106
|
-
* one usable name: here EVERY entry must be a
|
|
114
|
+
* one usable name: here EVERY entry must be a check this machine can ask,
|
|
107
115
|
* and anything else makes the whole declaration hard rather than being dropped.
|
|
108
116
|
* `reviewedBy` is a conjunction, so dropping a name the author wrote — a typo, a
|
|
109
117
|
* stray `7` — would let Jev clear the policy on fewer checks than they asked
|
|
@@ -114,16 +122,23 @@ const isName = (n: unknown): n is string => typeof n === "string" && n.length >
|
|
|
114
122
|
* What this returns is what `registerPolicy` stores, so a registered policy's
|
|
115
123
|
* `reviewedBy` is always clean and the two functions agree on it.
|
|
116
124
|
*
|
|
125
|
+
* With NO known reviewers — no installed pack declares a Jev check — a
|
|
126
|
+
* well-formed `reviewable` resolves `hard` WITHOUT a `downgraded` reason
|
|
127
|
+
* (unless a name it uses is contested between packs, which is still said). It is
|
|
128
|
+
* not a declaration going wrong but the machine having no Jev checks, which
|
|
129
|
+
* `jev status` and the dashboard say once; a warning per policy per hook event
|
|
130
|
+
* would say it fifteen times on every tool call of a core-pack machine.
|
|
131
|
+
*
|
|
117
132
|
* @param knownReviewers - the semantic policies that CAN be asked on this
|
|
118
|
-
* machine
|
|
119
|
-
*
|
|
120
|
-
* than a lookup because this module is imported by the
|
|
121
|
-
* which set is live means reading a file — a cost
|
|
122
|
-
* pay once and this judgement must not pay per call.
|
|
133
|
+
* machine (`effectiveReviewerNames()` on the hook path). Defaults to
|
|
134
|
+
* {@link NO_REVIEWERS}, so a caller that does not say is fail-safe. It is a
|
|
135
|
+
* parameter rather than a lookup because this module is imported by the
|
|
136
|
+
* registry, and reading which set is live means reading a file — a cost
|
|
137
|
+
* registration is willing to pay once and this judgement must not pay per call.
|
|
123
138
|
*/
|
|
124
139
|
export function resolvePolicyAuthority(
|
|
125
140
|
decl: AuthorityDeclaration | undefined,
|
|
126
|
-
knownReviewers: ReadonlySet<string> =
|
|
141
|
+
knownReviewers: ReadonlySet<string> = NO_REVIEWERS,
|
|
127
142
|
/** Names left out of `knownReviewers` because packs declare them differently, with those packs. */
|
|
128
143
|
contested?: ReadonlyMap<string, string[]>,
|
|
129
144
|
): ResolvedAuthority {
|
|
@@ -137,6 +152,8 @@ export function resolvePolicyAuthority(
|
|
|
137
152
|
return { authority: "hard", downgraded: "reviewedBy is not a list of semantic policy names" };
|
|
138
153
|
}
|
|
139
154
|
const unknown = names.filter((n) => !knownReviewers.has(n));
|
|
155
|
+
// No checks at all, and no contest to explain: quietly hard (see above).
|
|
156
|
+
if (knownReviewers.size === 0 && !unknown.some((n) => contested?.has(n))) return { authority: "hard" };
|
|
140
157
|
if (unknown.length > 0) {
|
|
141
158
|
return { authority: "hard", downgraded: `reviewedBy names ${unknown.map((n) => whyUnknown(n, knownReviewers, contested)).join("; ")}` };
|
|
142
159
|
}
|
|
@@ -144,8 +161,8 @@ export function resolvePolicyAuthority(
|
|
|
144
161
|
}
|
|
145
162
|
|
|
146
163
|
/**
|
|
147
|
-
* The rule that kept one name out. "Not in this build" is only true of
|
|
148
|
-
*
|
|
164
|
+
* The rule that kept one name out. "Not in this build" is only true of
|
|
165
|
+
* FailproofAI's own names, judged at build or publish time: a pack's own checks, or a name two packs disagree on, is a
|
|
149
166
|
* different reason, and the author reading it has a different fix.
|
|
150
167
|
*/
|
|
151
168
|
function whyUnknown(name: string, known: ReadonlySet<string>, contested?: ReadonlyMap<string, string[]>): string {
|
|
@@ -205,9 +222,11 @@ export function authorityProblem(
|
|
|
205
222
|
decl: AuthorityDeclaration,
|
|
206
223
|
/**
|
|
207
224
|
* The reviewers the pack being built will SHIP WITH — its own `semantic`
|
|
208
|
-
* entries when it declares any,
|
|
209
|
-
*
|
|
210
|
-
* that
|
|
225
|
+
* entries when it declares any, FailproofAI's sixteen names otherwise (a
|
|
226
|
+
* regex-only pack names checks that ship in `FailproofAI/jev-policies`). A
|
|
227
|
+
* pack that carries both halves of the two-tier set names its own checks, and
|
|
228
|
+
* refusing that would make the feature unpublishable by the tool that
|
|
229
|
+
* implements it.
|
|
211
230
|
*/
|
|
212
231
|
knownReviewers: ReadonlySet<string> = SEMANTIC_REVIEWER_NAMES,
|
|
213
232
|
): string | undefined {
|
|
@@ -283,13 +302,13 @@ export function manifestAuthority(
|
|
|
283
302
|
*
|
|
284
303
|
* @param knownReviewers - the checks that can be asked on this machine. Callers
|
|
285
304
|
* that merge what will be REGISTERED must pass `effectiveReviewerNames()`;
|
|
286
|
-
* the default is
|
|
287
|
-
*
|
|
305
|
+
* the default is {@link NO_REVIEWERS}, which is what a machine with no pack
|
|
306
|
+
* declaring Jev checks has.
|
|
288
307
|
*/
|
|
289
308
|
export function withMergedAuthority<T extends AuthorityFields>(
|
|
290
309
|
record: T,
|
|
291
310
|
declarations: ReadonlyArray<AuthorityFields>,
|
|
292
|
-
knownReviewers: ReadonlySet<string> =
|
|
311
|
+
knownReviewers: ReadonlySet<string> = NO_REVIEWERS,
|
|
293
312
|
): { merged: T; overruled: boolean; refused?: string } {
|
|
294
313
|
const resolved = declarations.map((d) => resolvePolicyAuthority(d, knownReviewers));
|
|
295
314
|
const allReviewable = resolved.length > 0 && resolved.every((r) => r.authority === "reviewable");
|
|
@@ -24,7 +24,7 @@ import {
|
|
|
24
24
|
type JevActivityFields,
|
|
25
25
|
type JevReview,
|
|
26
26
|
type RegexVerdict,
|
|
27
|
-
type
|
|
27
|
+
type ObserveVerdict,
|
|
28
28
|
type TwoTierReview,
|
|
29
29
|
} from "./semantic/combine";
|
|
30
30
|
// The code, not a copy of the string: the log line below turns on it, and a
|
|
@@ -62,8 +62,8 @@ export interface EvaluationResult {
|
|
|
62
62
|
decidedByJev: boolean;
|
|
63
63
|
/** The pack a deciding Jev check came from; only with `decidedByJev`. */
|
|
64
64
|
jevOrigin?: { packId: string; packVersion?: string };
|
|
65
|
-
/**
|
|
66
|
-
|
|
65
|
+
/** Observe mode: Jev's own deny/instruct, recorded as a "would have" (see `ObserveVerdict`). */
|
|
66
|
+
observeVerdict?: ObserveVerdict;
|
|
67
67
|
};
|
|
68
68
|
}
|
|
69
69
|
|
|
@@ -219,7 +219,7 @@ export async function evaluatePolicies(
|
|
|
219
219
|
}
|
|
220
220
|
const combined = combineTwoTier(collected.verdicts, review, twoTier.mode);
|
|
221
221
|
if (combined.cleared.length > 0) {
|
|
222
|
-
hookLogInfo(`jev ${twoTier.mode === "
|
|
222
|
+
hookLogInfo(`jev ${twoTier.mode === "observe" ? "would clear" : "cleared"}: ${combined.cleared.join(", ")}`);
|
|
223
223
|
}
|
|
224
224
|
if (combined.activity.evaluator === "jev-fallback") {
|
|
225
225
|
// Info, not warn: during an outage this is every gated call, and warn-level
|
|
@@ -251,7 +251,7 @@ export async function evaluatePolicies(
|
|
|
251
251
|
activity: combined.activity,
|
|
252
252
|
decidedByJev: combined.decidedByJev,
|
|
253
253
|
...(combined.decidedByJev && review.kind === "answered" && review.origin ? { jevOrigin: review.origin } : {}),
|
|
254
|
-
...(combined.
|
|
254
|
+
...(combined.observeVerdict ? { observeVerdict: combined.observeVerdict } : {}),
|
|
255
255
|
},
|
|
256
256
|
};
|
|
257
257
|
}
|
|
@@ -73,13 +73,11 @@ export function registerPolicy(
|
|
|
73
73
|
const canonical = normalizePolicyName(name);
|
|
74
74
|
const registry = getRegistry();
|
|
75
75
|
const idx = registry.findIndex((p) => p.name === canonical);
|
|
76
|
-
// Judged against the reviewers this MACHINE can ask
|
|
77
|
-
//
|
|
78
|
-
//
|
|
79
|
-
// `
|
|
80
|
-
//
|
|
81
|
-
// warning. `effectiveReviewerNames` reads the manifest — already where a
|
|
82
|
-
// pack's `reviewedBy` itself comes from — once per registration pass.
|
|
76
|
+
// Judged against the reviewers this MACHINE can ask: the checks its installed
|
|
77
|
+
// packs declare, and none when no pack declares any (the package ships no
|
|
78
|
+
// Jev checks), in which case every policy registers `hard`.
|
|
79
|
+
// `effectiveReviewerNames` reads the manifest — already where a pack's
|
|
80
|
+
// `reviewedBy` itself comes from — once per registration pass.
|
|
83
81
|
const authority = meta ? resolvePolicyAuthority(meta, effectiveReviewerNames()) : undefined;
|
|
84
82
|
const entry: RegisteredPolicy = {
|
|
85
83
|
name: canonical, description, fn, match, priority,
|
|
@@ -62,7 +62,7 @@ import { discoverPolicyFiles } from "./custom-hooks-loader";
|
|
|
62
62
|
import { customPoliciesDir } from "./fp-home";
|
|
63
63
|
import { configuredCustomPolicyPaths, findProjectConfigDir, readMergedHooksConfig } from "./hooks-config";
|
|
64
64
|
import { hasInstalledPacks, readInstalledPacks } from "./pack-manifest";
|
|
65
|
-
import { resolvePolicyAuthority
|
|
65
|
+
import { resolvePolicyAuthority } from "./policy-authority";
|
|
66
66
|
import { POLICY_CATALOG } from "./policy-catalog";
|
|
67
67
|
import { normalizePolicyName } from "./policy-registry";
|
|
68
68
|
import type { HooksConfig } from "./policy-types";
|
|
@@ -87,11 +87,23 @@ export interface ReviewableCoverage {
|
|
|
87
87
|
* user convention files — whose policies are not in the counts above.
|
|
88
88
|
*/
|
|
89
89
|
customFiles: number;
|
|
90
|
+
/**
|
|
91
|
+
* Jev checks the installed packs give it to ask. Zero on a machine with no
|
|
92
|
+
* pack declaring any — the vanilla install — and then Jev is never called.
|
|
93
|
+
*/
|
|
94
|
+
jevChecks: number;
|
|
90
95
|
}
|
|
91
96
|
|
|
92
97
|
/** The command that replaces a pack with one built by this release. */
|
|
93
98
|
export const RETAKE_PACK_COMMAND = "failproofai policies add FailproofAI/policies";
|
|
94
99
|
|
|
100
|
+
/** The command that gives Jev its checks. The package ships none. */
|
|
101
|
+
export const JEV_POLICIES_ADD_COMMAND = "failproofai policies add FailproofAI/jev-policies";
|
|
102
|
+
|
|
103
|
+
/** Said wherever Jev is configured but has nothing to ask. Kept short: it is a status line. */
|
|
104
|
+
export const NO_JEV_CHECKS_PROBLEM =
|
|
105
|
+
`Jev has no checks installed, so it asks nothing and clears nothing. To enable it: \`${JEV_POLICIES_ADD_COMMAND}\`.`;
|
|
106
|
+
|
|
95
107
|
/**
|
|
96
108
|
* Count a policy set by the authority it would REGISTER with —
|
|
97
109
|
* `resolvePolicyAuthority`, the rule `registerPolicy` applies — so this cannot
|
|
@@ -112,11 +124,9 @@ export const RETAKE_PACK_COMMAND = "failproofai policies add FailproofAI/policie
|
|
|
112
124
|
export function countReviewable(
|
|
113
125
|
policies: Iterable<AuthorityRecord>,
|
|
114
126
|
/**
|
|
115
|
-
* The semantic checks that can be asked on this machine
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
-
* reviewable" on exactly the machines the feature was built for — the ones
|
|
119
|
-
* running a pack that carries both tiers.
|
|
127
|
+
* The semantic checks that can be asked on this machine — the ones installed
|
|
128
|
+
* packs declare. Defaults to none, which is what a machine with no such pack
|
|
129
|
+
* has: nothing is reviewable there.
|
|
120
130
|
*/
|
|
121
131
|
knownReviewers?: ReadonlySet<string>,
|
|
122
132
|
): {
|
|
@@ -169,10 +179,11 @@ export function surveyReviewableCoverage(cwd?: string): ReviewableCoverage {
|
|
|
169
179
|
// hook path's manifest-only `reviewerNamesFor` cannot see. Anything else and
|
|
170
180
|
// the panel and `jev status` promise a clear that cannot happen.
|
|
171
181
|
const asked = semanticPoliciesFromPacks(jevPacks(packs));
|
|
172
|
-
reviewers =
|
|
182
|
+
reviewers = new Set(asked.policies.map((p) => p.name));
|
|
173
183
|
} catch {
|
|
174
|
-
// An unreadable manifest enforces nothing
|
|
175
|
-
// reports that to the hook log on the path
|
|
184
|
+
// An unreadable manifest enforces nothing and gives Jev nothing to ask;
|
|
185
|
+
// `readInstalledPacks` already reports that to the hook log on the path
|
|
186
|
+
// that cares.
|
|
176
187
|
}
|
|
177
188
|
|
|
178
189
|
let config: HooksConfig;
|
|
@@ -212,7 +223,7 @@ export function surveyReviewableCoverage(cwd?: string): ReviewableCoverage {
|
|
|
212
223
|
customFiles = 0;
|
|
213
224
|
}
|
|
214
225
|
|
|
215
|
-
return { ...countReviewable(records, reviewers), customFiles };
|
|
226
|
+
return { ...countReviewable(records, reviewers), customFiles, jevChecks: reviewers?.size ?? 0 };
|
|
216
227
|
}
|
|
217
228
|
|
|
218
229
|
/**
|
|
@@ -244,8 +255,13 @@ export function reviewableSummary(coverage: ReviewableCoverage): string {
|
|
|
244
255
|
* Nothing is diagnosed for an empty policy set: there is no clear to lose, and
|
|
245
256
|
* an authority warning to someone who enforces nothing answers a question they
|
|
246
257
|
* did not ask.
|
|
258
|
+
*
|
|
259
|
+
* Except the one thing that comes first: no installed pack gives Jev any checks.
|
|
260
|
+
* Then Jev asks nothing whatever the policy set is, and the fix is to install
|
|
261
|
+
* them, not to re-mark anything.
|
|
247
262
|
*/
|
|
248
263
|
export function reviewableProblem(coverage: ReviewableCoverage): string | null {
|
|
264
|
+
if (coverage.jevChecks === 0) return NO_JEV_CHECKS_PROBLEM;
|
|
249
265
|
// Policies from the user's own files were not counted and may be reviewable,
|
|
250
266
|
// so "never" cannot be said honestly; the summary already says what was skipped.
|
|
251
267
|
if (coverage.enabled === 0 || coverage.reviewable > 0 || coverage.customFiles > 0) return null;
|
|
@@ -46,9 +46,9 @@ export interface RegisteredPolicy {
|
|
|
46
46
|
/** Whether Jev may clear this policy's deny/instruct; see {@link effectiveAuthority}. */
|
|
47
47
|
authority?: PolicyAuthority;
|
|
48
48
|
/**
|
|
49
|
-
* The
|
|
50
|
-
* asked, and each clear (see {@link PolicyAuthority}),
|
|
51
|
-
* policy.
|
|
49
|
+
* The Jev checks (declared by an installed pack — the package ships none)
|
|
50
|
+
* that must all be asked, and each clear (see {@link PolicyAuthority}),
|
|
51
|
+
* before Jev clears this policy.
|
|
52
52
|
*/
|
|
53
53
|
reviewedBy?: string[];
|
|
54
54
|
}
|
|
@@ -188,7 +188,7 @@ export interface SemanticProbeDeclaration {
|
|
|
188
188
|
/**
|
|
189
189
|
* A semantic (Jev) policy as its author DECLARES it — a question set, not code.
|
|
190
190
|
*
|
|
191
|
-
* The
|
|
191
|
+
* The runtime form is `SemanticPolicy` in `semantic/types.ts`, and
|
|
192
192
|
* the one difference is the whole reason both exist: there, `precondition` is a
|
|
193
193
|
* function over the computed facts; here it is the NAME of one, because a
|
|
194
194
|
* declaration has to survive a trip through a JSON manifest and because a
|