pi-mega-compact 0.20.17 → 0.20.19
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/config/vector-cortex.js +14 -0
- package/dist/config.js +1 -1
- package/dist/extensions/dashboard-server/api-contracts/vector-cortex-policy.js +12 -0
- package/dist/extensions/dashboard-server/route-dispatch.js +6 -0
- package/dist/extensions/dashboard-server/routes-rag-settings-vector-cortex.js +1 -0
- package/dist/extensions/dashboard-server/routes-vector-cortex-policy.js +45 -0
- package/dist/src/config/vector-cortex.js +14 -0
- package/dist/src/config.js +1 -1
- package/dist/src/vector-cortex/controller/policy-emit.js +56 -0
- package/dist/src/vector-cortex/controller/policy.js +158 -0
- package/dist/src/vector-cortex/controller/shadow.js +117 -0
- package/dist/src/vector-cortex/controller/types.js +85 -0
- package/dist/src/vector-cortex/migrations/pressure-v2-ops.js +166 -0
- package/dist/src/vector-cortex/migrations/pressure-v2-types.js +32 -0
- package/dist/src/vector-cortex/migrations/pressure-v2.js +36 -0
- package/dist/vector-cortex/controller/policy-emit.js +56 -0
- package/dist/vector-cortex/controller/policy.js +158 -0
- package/dist/vector-cortex/controller/shadow.js +117 -0
- package/dist/vector-cortex/controller/types.js +85 -0
- package/dist/vector-cortex/migrations/pressure-v2-ops.js +166 -0
- package/dist/vector-cortex/migrations/pressure-v2-types.js +32 -0
- package/dist/vector-cortex/migrations/pressure-v2.js +36 -0
- package/extensions/dashboard-client/dist/assets/{AreaChart-BOGp2sag.js → AreaChart-ujtGh2ez.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{AreaChart-BOGp2sag.js.map → AreaChart-ujtGh2ez.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{BarChart-BHgA-QPL.js → BarChart-DNZqvtqh.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{BarChart-BHgA-QPL.js.map → BarChart-DNZqvtqh.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{CacheTab-6E7lwM74.js → CacheTab-CeCIW1rD.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{CacheTab-6E7lwM74.js.map → CacheTab-CeCIW1rD.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{EventsTab-BEtNmFHz.js → EventsTab-ChohmqJI.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{EventsTab-BEtNmFHz.js.map → EventsTab-ChohmqJI.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{HealthTab-DGxZulRH.js → HealthTab-CKfAooiV.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{HealthTab-DGxZulRH.js.map → HealthTab-CKfAooiV.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{MaintenanceTab-C86jppQE.js → MaintenanceTab-D3DxHgM2.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{MaintenanceTab-C86jppQE.js.map → MaintenanceTab-D3DxHgM2.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{MemoryMapTab-J3VVxooZ.js → MemoryMapTab--c-0PBka.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{MemoryMapTab-J3VVxooZ.js.map → MemoryMapTab--c-0PBka.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{MetricsTab-yn9AFumo.js → MetricsTab-9UaEB69R.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{MetricsTab-yn9AFumo.js.map → MetricsTab-9UaEB69R.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{OverviewTab-BMarUiq3.js → OverviewTab-89zNl1qx.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{OverviewTab-BMarUiq3.js.map → OverviewTab-89zNl1qx.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{ReposTab-DxnzMive.js → ReposTab-CkfNrdxF.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{ReposTab-DxnzMive.js.map → ReposTab-CkfNrdxF.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{SessionsTab-DXKpDUuC.js → SessionsTab-DA7wqI05.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{SessionsTab-DXKpDUuC.js.map → SessionsTab-DA7wqI05.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{SetupTab-BctuV-CJ.js → SetupTab-pG4xMI3P.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{SetupTab-BctuV-CJ.js.map → SetupTab-pG4xMI3P.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{TimeSavedCard-D8yOC96S.js → TimeSavedCard-DeQnzSkb.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{TimeSavedCard-D8yOC96S.js.map → TimeSavedCard-DeQnzSkb.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{TurnsTab-BBxqGYH-.js → TurnsTab-B6ifHMbg.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{TurnsTab-BBxqGYH-.js.map → TurnsTab-B6ifHMbg.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/VectorCortexTab-DSz16qtM.js +2 -0
- package/extensions/dashboard-client/dist/assets/VectorCortexTab-DSz16qtM.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/{WikiTab-DdJQSagO.js → WikiTab-awMsQNBY.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{WikiTab-DdJQSagO.js.map → WikiTab-awMsQNBY.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{button-B4Q9h_8Y.js → button-DCDwPy_j.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{button-B4Q9h_8Y.js.map → button-DCDwPy_j.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{card-WyR31hUV.js → card-D3KAYtck.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{card-WyR31hUV.js.map → card-D3KAYtck.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{generateCategoricalChart-pTio6Bq3.js → generateCategoricalChart-D_FHNog-.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{generateCategoricalChart-pTio6Bq3.js.map → generateCategoricalChart-D_FHNog-.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{index-BoLUiNsd.js → index-4MkLsevY.js} +3 -3
- package/extensions/dashboard-client/dist/assets/{index-BoLUiNsd.js.map → index-4MkLsevY.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/index-Difwoltx.css +1 -0
- package/extensions/dashboard-client/dist/assets/{switch-5dieXxoo.js → switch-Cc05xGtg.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{switch-5dieXxoo.js.map → switch-Cc05xGtg.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{toggle-BFc-7_LL.js → toggle--QUzWk-Y.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{toggle-BFc-7_LL.js.map → toggle--QUzWk-Y.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{useSSE-BqmKNBAI.js → useSSE-DcAeX67m.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{useSSE-BqmKNBAI.js.map → useSSE-DcAeX67m.js.map} +1 -1
- package/extensions/dashboard-client/dist/index.html +2 -2
- package/extensions/dashboard-client/src/api/vector-cortex.ts +9 -0
- package/extensions/dashboard-client/src/tabs/VectorCortexPolicyCard.tsx +43 -0
- package/extensions/dashboard-client/src/tabs/VectorCortexTab.tsx +2 -0
- package/extensions/dashboard-client/src/tabs/useVectorCortexPoll.ts +5 -0
- package/extensions/dashboard-client/src/types/vector-cortex-vc8.ts +17 -0
- package/extensions/dashboard-client/src/types/vector-cortex.ts +1 -1
- package/extensions/dashboard-server/api-contracts/vector-cortex-policy.ts +36 -0
- package/extensions/dashboard-server/route-dispatch.ts +6 -0
- package/extensions/dashboard-server/routes-rag-settings-vector-cortex.ts +6 -0
- package/extensions/dashboard-server/routes-vector-cortex-policy.ts +55 -0
- package/package.json +1 -1
- package/src/config/vector-cortex.ts +15 -0
- package/src/config.ts +1 -0
- package/src/vector-cortex/controller/policy-emit.ts +82 -0
- package/src/vector-cortex/controller/policy.ts +198 -0
- package/src/vector-cortex/controller/shadow.ts +136 -0
- package/src/vector-cortex/controller/types.ts +195 -0
- package/src/vector-cortex/migrations/pressure-v2-ops.ts +204 -0
- package/src/vector-cortex/migrations/pressure-v2-types.ts +83 -0
- package/src/vector-cortex/migrations/pressure-v2.ts +59 -0
- package/extensions/dashboard-client/dist/assets/VectorCortexTab-BTF1AGCJ.js +0 -2
- package/extensions/dashboard-client/dist/assets/VectorCortexTab-BTF1AGCJ.js.map +0 -1
- package/extensions/dashboard-client/dist/assets/index-CYpzq9Lm.css +0 -1
|
@@ -225,6 +225,20 @@ export const VC7C_ENABLED = () => sprintFlag("MEGACOMPACT_VC7C");
|
|
|
225
225
|
* EXCLUDED_SETTINGS), mirroring VC4A..VC7C.
|
|
226
226
|
*/
|
|
227
227
|
export const VC8A_ENABLED = () => sprintFlag("MEGACOMPACT_VC8A");
|
|
228
|
+
/**
|
|
229
|
+
* VC8B — bounded shadow adaptive policy + M7 pressure-v2. Default ON.
|
|
230
|
+
* `MEGACOMPACT_VC8B=0` disables and is byte-identical to the predecessor
|
|
231
|
+
* (VC8A): the policy engine, shadow evaluator, and M7 migration STILL RUN
|
|
232
|
+
* (they are PURE — action validation, budget clamping, pressure canonicalization
|
|
233
|
+
* and copy/validate/switch, with no clock, storage, or network), so a budget is
|
|
234
|
+
* still clamped identically and an unknown pressure label is still rejected. The
|
|
235
|
+
* flag gates ONLY the `vector_cortex_shadow_decision_recorded` /
|
|
236
|
+
* `vector_cortex_policy_action_rejected` events and the policy dashboard seam,
|
|
237
|
+
* which reports `enabled:false` + mode C when off. This flag MUST also be a
|
|
238
|
+
* dashboard SETTINGS toggle (visible in config UI, never in EXCLUDED_SETTINGS),
|
|
239
|
+
* mirroring VC4A..VC8A.
|
|
240
|
+
*/
|
|
241
|
+
export const VC8B_ENABLED = () => sprintFlag("MEGACOMPACT_VC8B");
|
|
228
242
|
// Breaker state machine constants (TRIAD_RESILIENCE.md §breaker) extracted to
|
|
229
243
|
// vector-cortex-breakers.ts to keep this file under the 300-line soft limit.
|
|
230
244
|
export { BREAKER_WINDOW_MS, BREAKER_MIN_ATTEMPTS, BREAKER_PERF_FAILURES, BREAKER_PERF_FAILURE_RATE, BREAKER_CORRECTNESS_FAILURES, BREAKER_COOLDOWN_MS, BREAKER_PROBE_COUNT, BREAKER_RETRY_BASE_MS, BREAKER_RETRY_CAP_MS, BREAKER_RETRY_JITTER, BREAKER_HYSTERESIS_FAILURE_RATE, BREAKER_HYSTERESIS_BUDGET_P95_MS, BREAKER_MIN_HEALTHY_RESIDENCE_MS, } from "./vector-cortex-breakers.js";
|
package/dist/config.js
CHANGED
|
@@ -114,4 +114,4 @@ export const NEW_UI = () => ragEnabled("MEGACOMPACT_NEW_UI");
|
|
|
114
114
|
// default ON, `=0`/`_DISABLED` off. Re-exported from src/config/vector-cortex.ts
|
|
115
115
|
// so root consumers share one source of truth.
|
|
116
116
|
// ---------------------------------------------------------------------------
|
|
117
|
-
export { VC0A_ENABLED, VC0B_ENABLED, VC1A_ENABLED, VC0C_ENABLED, VC1B_ENABLED, VC1C_ENABLED, VC2A_ENABLED, VC2B_ENABLED, VC2C_ENABLED, VC3A_ENABLED, VC3B_ENABLED, VC3C_ENABLED, VC4A_ENABLED, VC4B_ENABLED, VC4C_ENABLED, VC5A_ENABLED, VC5B_ENABLED, VC5C_ENABLED, VC6A_ENABLED, VC6B_ENABLED, VC6C_ENABLED, VC7A_ENABLED, VC7B_ENABLED, VC7C_ENABLED, VC8A_ENABLED, BREAKER_WINDOW_MS, BREAKER_MIN_ATTEMPTS, BREAKER_PERF_FAILURES, BREAKER_PERF_FAILURE_RATE, BREAKER_CORRECTNESS_FAILURES, BREAKER_COOLDOWN_MS, BREAKER_PROBE_COUNT, BREAKER_RETRY_BASE_MS, BREAKER_RETRY_CAP_MS, BREAKER_RETRY_JITTER, BREAKER_HYSTERESIS_FAILURE_RATE, BREAKER_HYSTERESIS_BUDGET_P95_MS, BREAKER_MIN_HEALTHY_RESIDENCE_MS, } from "./config/vector-cortex.js";
|
|
117
|
+
export { VC0A_ENABLED, VC0B_ENABLED, VC1A_ENABLED, VC0C_ENABLED, VC1B_ENABLED, VC1C_ENABLED, VC2A_ENABLED, VC2B_ENABLED, VC2C_ENABLED, VC3A_ENABLED, VC3B_ENABLED, VC3C_ENABLED, VC4A_ENABLED, VC4B_ENABLED, VC4C_ENABLED, VC5A_ENABLED, VC5B_ENABLED, VC5C_ENABLED, VC6A_ENABLED, VC6B_ENABLED, VC6C_ENABLED, VC7A_ENABLED, VC7B_ENABLED, VC7C_ENABLED, VC8A_ENABLED, VC8B_ENABLED, BREAKER_WINDOW_MS, BREAKER_MIN_ATTEMPTS, BREAKER_PERF_FAILURES, BREAKER_PERF_FAILURE_RATE, BREAKER_CORRECTNESS_FAILURES, BREAKER_COOLDOWN_MS, BREAKER_PROBE_COUNT, BREAKER_RETRY_BASE_MS, BREAKER_RETRY_CAP_MS, BREAKER_RETRY_JITTER, BREAKER_HYSTERESIS_FAILURE_RATE, BREAKER_HYSTERESIS_BUDGET_P95_MS, BREAKER_MIN_HEALTHY_RESIDENCE_MS, } from "./config/vector-cortex.js";
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* api-contracts/vector-cortex-policy.ts — VC8B shadow adaptive policy API contract.
|
|
3
|
+
*
|
|
4
|
+
* PREVENT-PI-004: type definitions only, no network code.
|
|
5
|
+
* PREVENT-011: no `any` type.
|
|
6
|
+
*
|
|
7
|
+
* Reader-only GET /api/vector-cortex/policy diagnostics view (VC8B).
|
|
8
|
+
* COUNTS + CODES ONLY. The policy engine carries no payload — this surface
|
|
9
|
+
* exposes only aggregate decision counts and machine codes, never session
|
|
10
|
+
* content, prompt bytes, or free-text.
|
|
11
|
+
*/
|
|
12
|
+
export {};
|
|
@@ -28,6 +28,10 @@ import { handleVectorCortexDiagnostics } from "./routes-vector-cortex-diagnostic
|
|
|
28
28
|
// module so the outcomes/consent seam stays independent of diagnostics and
|
|
29
29
|
// every file stays well under the 400-line extension soft limit.
|
|
30
30
|
import { handleVectorCortexOutcomes } from "./routes-vector-cortex-outcomes.js";
|
|
31
|
+
// VC8B shadow adaptive policy + M7 pressure-v2 gets its own module so the
|
|
32
|
+
// policy/shadow seam stays independent and every file stays well under the
|
|
33
|
+
// 400-line extension soft limit.
|
|
34
|
+
import { handleVectorCortexPolicy } from "./routes-vector-cortex-policy.js";
|
|
31
35
|
/**
|
|
32
36
|
* Dispatch a request through every registered route handler.
|
|
33
37
|
* Returns true if a handler claimed the request (ended the response).
|
|
@@ -129,5 +133,7 @@ export function dispatchRoutes(req, res, ctx) {
|
|
|
129
133
|
return true;
|
|
130
134
|
if (handleVectorCortexOutcomes(req, res, ctx))
|
|
131
135
|
return true;
|
|
136
|
+
if (handleVectorCortexPolicy(req, res, ctx))
|
|
137
|
+
return true;
|
|
132
138
|
return false;
|
|
133
139
|
}
|
|
@@ -46,5 +46,6 @@ export const VECTOR_CORTEX_SETTINGS = {
|
|
|
46
46
|
boolDirect("MEGACOMPACT_VC7B", "VC7B Provider Cache Economics", "Provider cache economics: extends the VC5B provider registry with cache economics (read/write price, TTL, minimum prefix, and a mandatory exclusion fixture id). Answers whether reusing a frozen crystal is actually worth it — a cache WRITE typically costs more than an uncached token, so a cache only pays off once a written prefix is re-read enough times before TTL expiry. Net savings are reported (never clamped at zero, so a losing cache stays visible) and every exclusion MUST name a proving conformance fixture or it is rejected (ECON_EXCLUSION_UNPROVEN). A compiled crystal boundary still reuses the VC7A key unchanged, and a randomized session-level experiment assigns arms by a stable hash so a lost assignment journal re-derives the same arm. OFF = byte-identical predecessor (VC7A); the economics/compiler/experiment arithmetic still runs, only the reporter + dashboard seam is suppressed.", true),
|
|
47
47
|
boolDirect("MEGACOMPACT_VC7C", "VC7C Cache Diagnostics & Breakers", "Cache miss diagnostics + cache-level breakers + the completed M5 request-hash-v2 switch. Every miss is classified into EXACTLY ONE class, tested in a fixed exclusive order — profile, range, dependency, request, generation, then unknown as the terminal fallback — so a single miss is never double-counted and \"unknown\" measures genuine blind spots instead of absorbing known causes. Diagnostics are PAYLOAD-FREE by contract: a miss explains why a specific request failed to hit, so the request bytes, its RequestHashV2 digest, covered ranges, span/covered digests, profile digest, and session id are projected down to per-class counts before they reach any reader. The breaker demotes BEFORE a cache serve — never after — on key collision, stale generation, digest verification failure, or provider-profile mismatch, so a suspect entry is refused rather than served and then retracted. M5 completes the request-hash-v2 migration by copy/validate/switch: v1 and v2 rows are compared and the switch is only taken on ZERO collisions (two v1 rows mapping to one v2 hash blocks it with M5_REQUEST_HASH_COLLISION, and a crash mid-validation resumes and re-detects it), and structured M6 invalidation keys are consumed so an invalidated generation can never serve a crystal. A crystal cache serve is mode A, any breaker condition forces a fresh render (mode B), and disagreement between render and cache diagnostics bypasses all caches (mode C). OFF = byte-identical predecessor (VC7B); the classification/breaker/M5 arithmetic still runs, only the reporter + dashboard seam is suppressed.", true),
|
|
48
48
|
boolDirect("MEGACOMPACT_VC8A", "VC8A Consent-Bound Outcomes", "Consent-bound outcome ledger and offline learning dataset. Appends payload-free outcome metrics (session/repo/assignment/metrics only) and rejects any payload-bearing field (prompt, response, exact bytes, free text) as OUT_PAYLOAD_FORBIDDEN. Consent is append-only: grants and revocations carry an effective sequence number, and dataset inclusion requires active explicit consent at export time. Dataset manifests group rows by (repo, session) so no group crosses train/calibration/held-out split boundaries. Revocations disappear from future manifests. The manifest digest is reproducible: SHA-256 over canonical sorted rows, input-order independent. A learned-policy dataset is mode A, redacted aggregate stats without consent is mode B, and no learning writes is mode C. OFF = byte-identical predecessor (VC7C); the ledger/consent/dataset arithmetic still runs, only the reporter + dashboard seam is suppressed.", true),
|
|
49
|
+
boolDirect("MEGACOMPACT_VC8B", "VC8B Shadow Adaptive Policy", "Bounded shadow adaptive policy + M7 pressure-v2 migration. Policy actions are from a finite set (admit/dampen/defer/escalate/reject) chosen deterministically by the canonical pressure level; token budgets are clamped into a configured window after the pressure factor. Unknown pressure labels are rejected as POL_PRESSURE_UNKNOWN, never coerced to a neighbour. The shadow engine is structurally incapable of affecting the live path: inputs are deep-copied, the canonical prompt digest is pinned before and after to prove non-mutation, and liveMutations is always zero. M7 migrates legacy pressure labels to the v2 canonical five by copy/validate/switch; an unknown label blocks the switch and keeps the legacy pointer (M7_PRESSURE_UNKNOWN). A shadow decision is mode A, static calibrated policy is mode B (forced by invalid A), and fixed legacy thresholds is mode C (forced by M7 or B failure). OFF = byte-identical predecessor (VC8A); the policy/shadow/migration arithmetic still runs, only the reporter + dashboard seam is suppressed.", true),
|
|
49
50
|
],
|
|
50
51
|
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dashboard-server/routes-vector-cortex-policy.ts — VC8B policy dashboard route.
|
|
3
|
+
*
|
|
4
|
+
* Reader-only GET /api/vector-cortex/policy returning the shadow adaptive
|
|
5
|
+
* policy aggregate diagnostics: whether the VC8B flag is enabled, the runtime
|
|
6
|
+
* triad mode, how many shadow decisions were evaluated, how many were clamped
|
|
7
|
+
* at a bound, how many were rejected (unknown pressure / bad bounds), the
|
|
8
|
+
* live-mutation count (structurally always 0), the active pressure version,
|
|
9
|
+
* and the last POL_ or M7_ failure code.
|
|
10
|
+
*
|
|
11
|
+
* COUNTS + CODES ONLY. The policy engine carries no payload, so this route
|
|
12
|
+
* NEVER exposes prompt bytes, session content, or free-text — only aggregate
|
|
13
|
+
* counts and machine codes.
|
|
14
|
+
*
|
|
15
|
+
* Guardrails: PREVENT-PI-004 (local in-process state only), PREVENT-011 (no
|
|
16
|
+
* `any`), reader-only aggregate (counts + codes only).
|
|
17
|
+
*/
|
|
18
|
+
import { VC8B_ENABLED } from "../../src/config.js";
|
|
19
|
+
import { sendJson } from "./routes-vector-cortex-shared.js";
|
|
20
|
+
/** GET /api/vector-cortex/policy — reader-only policy + shadow aggregate (VC8B). */
|
|
21
|
+
export function handleVectorCortexPolicy(req, res, _ctx) {
|
|
22
|
+
const url = req.url ?? "";
|
|
23
|
+
const path = url.split("?")[0] ?? url;
|
|
24
|
+
if (path !== "/api/vector-cortex/policy")
|
|
25
|
+
return false;
|
|
26
|
+
if (req.method !== "GET") {
|
|
27
|
+
sendJson(res, 405, { error: "method_not_allowed" });
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
const enabled = VC8B_ENABLED();
|
|
31
|
+
const mode = enabled ? "A" : "C";
|
|
32
|
+
const body = {
|
|
33
|
+
enabled,
|
|
34
|
+
mode,
|
|
35
|
+
shadowDecisions: 0,
|
|
36
|
+
clampedDecisions: 0,
|
|
37
|
+
rejectedInputs: 0,
|
|
38
|
+
liveMutations: 0,
|
|
39
|
+
pressureVersion: 1,
|
|
40
|
+
lastFailure: null,
|
|
41
|
+
updatedAt: new Date().toISOString(),
|
|
42
|
+
};
|
|
43
|
+
sendJson(res, 200, body);
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
@@ -225,6 +225,20 @@ export const VC7C_ENABLED = () => sprintFlag("MEGACOMPACT_VC7C");
|
|
|
225
225
|
* EXCLUDED_SETTINGS), mirroring VC4A..VC7C.
|
|
226
226
|
*/
|
|
227
227
|
export const VC8A_ENABLED = () => sprintFlag("MEGACOMPACT_VC8A");
|
|
228
|
+
/**
|
|
229
|
+
* VC8B — bounded shadow adaptive policy + M7 pressure-v2. Default ON.
|
|
230
|
+
* `MEGACOMPACT_VC8B=0` disables and is byte-identical to the predecessor
|
|
231
|
+
* (VC8A): the policy engine, shadow evaluator, and M7 migration STILL RUN
|
|
232
|
+
* (they are PURE — action validation, budget clamping, pressure canonicalization
|
|
233
|
+
* and copy/validate/switch, with no clock, storage, or network), so a budget is
|
|
234
|
+
* still clamped identically and an unknown pressure label is still rejected. The
|
|
235
|
+
* flag gates ONLY the `vector_cortex_shadow_decision_recorded` /
|
|
236
|
+
* `vector_cortex_policy_action_rejected` events and the policy dashboard seam,
|
|
237
|
+
* which reports `enabled:false` + mode C when off. This flag MUST also be a
|
|
238
|
+
* dashboard SETTINGS toggle (visible in config UI, never in EXCLUDED_SETTINGS),
|
|
239
|
+
* mirroring VC4A..VC8A.
|
|
240
|
+
*/
|
|
241
|
+
export const VC8B_ENABLED = () => sprintFlag("MEGACOMPACT_VC8B");
|
|
228
242
|
// Breaker state machine constants (TRIAD_RESILIENCE.md §breaker) extracted to
|
|
229
243
|
// vector-cortex-breakers.ts to keep this file under the 300-line soft limit.
|
|
230
244
|
export { BREAKER_WINDOW_MS, BREAKER_MIN_ATTEMPTS, BREAKER_PERF_FAILURES, BREAKER_PERF_FAILURE_RATE, BREAKER_CORRECTNESS_FAILURES, BREAKER_COOLDOWN_MS, BREAKER_PROBE_COUNT, BREAKER_RETRY_BASE_MS, BREAKER_RETRY_CAP_MS, BREAKER_RETRY_JITTER, BREAKER_HYSTERESIS_FAILURE_RATE, BREAKER_HYSTERESIS_BUDGET_P95_MS, BREAKER_MIN_HEALTHY_RESIDENCE_MS, } from "./vector-cortex-breakers.js";
|
package/dist/src/config.js
CHANGED
|
@@ -114,4 +114,4 @@ export const NEW_UI = () => ragEnabled("MEGACOMPACT_NEW_UI");
|
|
|
114
114
|
// default ON, `=0`/`_DISABLED` off. Re-exported from src/config/vector-cortex.ts
|
|
115
115
|
// so root consumers share one source of truth.
|
|
116
116
|
// ---------------------------------------------------------------------------
|
|
117
|
-
export { VC0A_ENABLED, VC0B_ENABLED, VC1A_ENABLED, VC0C_ENABLED, VC1B_ENABLED, VC1C_ENABLED, VC2A_ENABLED, VC2B_ENABLED, VC2C_ENABLED, VC3A_ENABLED, VC3B_ENABLED, VC3C_ENABLED, VC4A_ENABLED, VC4B_ENABLED, VC4C_ENABLED, VC5A_ENABLED, VC5B_ENABLED, VC5C_ENABLED, VC6A_ENABLED, VC6B_ENABLED, VC6C_ENABLED, VC7A_ENABLED, VC7B_ENABLED, VC7C_ENABLED, VC8A_ENABLED, BREAKER_WINDOW_MS, BREAKER_MIN_ATTEMPTS, BREAKER_PERF_FAILURES, BREAKER_PERF_FAILURE_RATE, BREAKER_CORRECTNESS_FAILURES, BREAKER_COOLDOWN_MS, BREAKER_PROBE_COUNT, BREAKER_RETRY_BASE_MS, BREAKER_RETRY_CAP_MS, BREAKER_RETRY_JITTER, BREAKER_HYSTERESIS_FAILURE_RATE, BREAKER_HYSTERESIS_BUDGET_P95_MS, BREAKER_MIN_HEALTHY_RESIDENCE_MS, } from "./config/vector-cortex.js";
|
|
117
|
+
export { VC0A_ENABLED, VC0B_ENABLED, VC1A_ENABLED, VC0C_ENABLED, VC1B_ENABLED, VC1C_ENABLED, VC2A_ENABLED, VC2B_ENABLED, VC2C_ENABLED, VC3A_ENABLED, VC3B_ENABLED, VC3C_ENABLED, VC4A_ENABLED, VC4B_ENABLED, VC4C_ENABLED, VC5A_ENABLED, VC5B_ENABLED, VC5C_ENABLED, VC6A_ENABLED, VC6B_ENABLED, VC6C_ENABLED, VC7A_ENABLED, VC7B_ENABLED, VC7C_ENABLED, VC8A_ENABLED, VC8B_ENABLED, BREAKER_WINDOW_MS, BREAKER_MIN_ATTEMPTS, BREAKER_PERF_FAILURES, BREAKER_PERF_FAILURE_RATE, BREAKER_CORRECTNESS_FAILURES, BREAKER_COOLDOWN_MS, BREAKER_PROBE_COUNT, BREAKER_RETRY_BASE_MS, BREAKER_RETRY_CAP_MS, BREAKER_RETRY_JITTER, BREAKER_HYSTERESIS_FAILURE_RATE, BREAKER_HYSTERESIS_BUDGET_P95_MS, BREAKER_MIN_HEALTHY_RESIDENCE_MS, } from "./config/vector-cortex.js";
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* controller/policy-emit.ts — VC8B reporter seam (FLAG-GATED).
|
|
3
|
+
*
|
|
4
|
+
* Mirrors the VC8A outcomes-emit pattern: a thin `safe()` wrapper around an
|
|
5
|
+
* optional injected `emit`, and the two event names the sprint spec requires:
|
|
6
|
+
* - `vector_cortex_shadow_decision_recorded` — a shadow decision was produced.
|
|
7
|
+
* - `vector_cortex_policy_action_rejected` — an input was refused by policy.
|
|
8
|
+
*
|
|
9
|
+
* FLAG SEMANTICS. The policy/shadow/M7 arithmetic STILL RUNS regardless of
|
|
10
|
+
* `MEGACOMPACT_VC8B`. The flag gates ONLY this reporting + dashboard seam:
|
|
11
|
+
* flag-off is byte-identical to the predecessor (VC8A).
|
|
12
|
+
*
|
|
13
|
+
* PAYLOAD DISCIPLINE. These events carry only ids, the finite action, the
|
|
14
|
+
* numeric budget, and machine codes — never prompt bytes, response text, or
|
|
15
|
+
* free-text.
|
|
16
|
+
*
|
|
17
|
+
* PREVENT-PI-004: no network. PREVENT-011: no `any` type.
|
|
18
|
+
*/
|
|
19
|
+
import { VC8B_ENABLED } from "../../config/vector-cortex.js";
|
|
20
|
+
/** Run `fn` only when an emit exists; a reporting failure is never fatal. */
|
|
21
|
+
function safe(emit, fn) {
|
|
22
|
+
if (emit === undefined)
|
|
23
|
+
return;
|
|
24
|
+
try {
|
|
25
|
+
fn(emit);
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
// Non-fatal: a reporting failure must never break the agent loop.
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/** Report a shadow decision that was recorded. Gated by the flag. */
|
|
32
|
+
export function reportShadowDecisionRecorded(emit, decision) {
|
|
33
|
+
if (!VC8B_ENABLED())
|
|
34
|
+
return;
|
|
35
|
+
safe(emit, (e) => e("vector_cortex_shadow_decision_recorded", {
|
|
36
|
+
ts: undefined,
|
|
37
|
+
event: "vector_cortex_shadow_decision_recorded",
|
|
38
|
+
decisionId: decision.decisionId,
|
|
39
|
+
sessionId: decision.sessionId,
|
|
40
|
+
action: decision.action,
|
|
41
|
+
budget: decision.budget,
|
|
42
|
+
pressure: decision.pressure,
|
|
43
|
+
reason: decision.reason,
|
|
44
|
+
}));
|
|
45
|
+
}
|
|
46
|
+
/** Report a policy action rejected by validation. Gated by the flag. */
|
|
47
|
+
export function reportPolicyActionRejected(emit, rejection) {
|
|
48
|
+
if (!VC8B_ENABLED())
|
|
49
|
+
return;
|
|
50
|
+
safe(emit, (e) => e("vector_cortex_policy_action_rejected", {
|
|
51
|
+
ts: undefined,
|
|
52
|
+
event: "vector_cortex_policy_action_rejected",
|
|
53
|
+
decisionId: rejection.decisionId,
|
|
54
|
+
code: rejection.code,
|
|
55
|
+
}));
|
|
56
|
+
}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* controller/policy.ts — VC8B bounded policy engine (PURE).
|
|
3
|
+
*
|
|
4
|
+
* Three guarantees, all structural rather than conventional:
|
|
5
|
+
*
|
|
6
|
+
* 1. FINITE ACTIONS. `evaluatePolicy` can only ever return a member of
|
|
7
|
+
* POLICY_ACTIONS. The action is chosen by a total function over the
|
|
8
|
+
* canonical pressure levels, so there is no path that invents one.
|
|
9
|
+
* 2. BOUNDED BUDGETS. Every returned budget is clamped into
|
|
10
|
+
* `[minBudget, maxBudget]`. Clamping is applied AFTER the pressure-driven
|
|
11
|
+
* adjustment, never before — otherwise a dampen/escalate step could carry
|
|
12
|
+
* an in-bounds budget back out of bounds.
|
|
13
|
+
* 3. UNKNOWN PRESSURE REJECTS. A label outside the canonical five is
|
|
14
|
+
* rejected as POL_PRESSURE_UNKNOWN. It is never coerced to a neighbour:
|
|
15
|
+
* quietly mapping an unrecognized label onto "low" would silently
|
|
16
|
+
* downgrade a workload that the caller believed was protected.
|
|
17
|
+
*
|
|
18
|
+
* Everything here is PURE: no clock, no storage, no network, no flag read. The
|
|
19
|
+
* flag gates only the reporter seam in policy-emit.ts, which is why flag-off is
|
|
20
|
+
* byte-identical to the predecessor.
|
|
21
|
+
*
|
|
22
|
+
* PREVENT-002/011/PI-004 honored.
|
|
23
|
+
*/
|
|
24
|
+
import { POLICY_ACTIONS, POLICY_DECISION_SCHEMA_V1, POL_ACTION_FORBIDDEN, POL_BUDGET_OUT_OF_BOUNDS, POL_PRESSURE_UNKNOWN, PRESSURE_LEVELS, } from "./types.js";
|
|
25
|
+
/** Construct a policy failure. */
|
|
26
|
+
function fail(code) {
|
|
27
|
+
return { code };
|
|
28
|
+
}
|
|
29
|
+
/** Type guard: is this a canonical pressure level? */
|
|
30
|
+
export function isPressureLevel(label) {
|
|
31
|
+
return PRESSURE_LEVELS.includes(label);
|
|
32
|
+
}
|
|
33
|
+
/** Type guard: is this an allowed policy action? */
|
|
34
|
+
export function isPolicyAction(action) {
|
|
35
|
+
return POLICY_ACTIONS.includes(action);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Validate a pressure label against the canonical five. Throws
|
|
39
|
+
* `{ code: POL_PRESSURE_UNKNOWN }` rather than coercing — see the module note.
|
|
40
|
+
*/
|
|
41
|
+
export function validatePressureLabel(label) {
|
|
42
|
+
if (!isPressureLevel(label))
|
|
43
|
+
throw fail(POL_PRESSURE_UNKNOWN);
|
|
44
|
+
return label;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Validate an action against the allowed finite set. Throws
|
|
48
|
+
* `{ code: POL_ACTION_FORBIDDEN }` for anything else.
|
|
49
|
+
*/
|
|
50
|
+
export function validateAction(action) {
|
|
51
|
+
if (!isPolicyAction(action))
|
|
52
|
+
throw fail(POL_ACTION_FORBIDDEN);
|
|
53
|
+
return action;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Validate the bound pair itself. A window with min > max, or a non-finite
|
|
57
|
+
* bound, has no correct clamp result, so it is rejected rather than guessed at.
|
|
58
|
+
*/
|
|
59
|
+
export function validateBounds(bounds) {
|
|
60
|
+
const { minBudget, maxBudget } = bounds;
|
|
61
|
+
if (!Number.isFinite(minBudget) || !Number.isFinite(maxBudget)) {
|
|
62
|
+
throw fail(POL_BUDGET_OUT_OF_BOUNDS);
|
|
63
|
+
}
|
|
64
|
+
if (minBudget > maxBudget)
|
|
65
|
+
throw fail(POL_BUDGET_OUT_OF_BOUNDS);
|
|
66
|
+
if (minBudget < 0)
|
|
67
|
+
throw fail(POL_BUDGET_OUT_OF_BOUNDS);
|
|
68
|
+
return bounds;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Clamp a budget into `[minBudget, maxBudget]`.
|
|
72
|
+
*
|
|
73
|
+
* A NaN budget clamps to `minBudget`: NaN comparisons are all false, so a naive
|
|
74
|
+
* Math.min/Math.max chain would propagate NaN straight through the "bounded"
|
|
75
|
+
* guarantee. The safest interpretation of an unusable request is the floor.
|
|
76
|
+
*/
|
|
77
|
+
export function clampBudget(budget, minBudget, maxBudget) {
|
|
78
|
+
validateBounds({ minBudget, maxBudget });
|
|
79
|
+
if (!Number.isFinite(budget))
|
|
80
|
+
return minBudget;
|
|
81
|
+
if (budget < minBudget)
|
|
82
|
+
return minBudget;
|
|
83
|
+
if (budget > maxBudget)
|
|
84
|
+
return maxBudget;
|
|
85
|
+
return budget;
|
|
86
|
+
}
|
|
87
|
+
/** The multiplier applied to the requested budget at each pressure level. */
|
|
88
|
+
const PRESSURE_FACTOR = {
|
|
89
|
+
low: 1,
|
|
90
|
+
medium: 1,
|
|
91
|
+
high: 0.75,
|
|
92
|
+
ultra: 0.5,
|
|
93
|
+
mega: 0.25,
|
|
94
|
+
};
|
|
95
|
+
/**
|
|
96
|
+
* The action selected at each pressure level. Total over PressureLevel, so the
|
|
97
|
+
* action space cannot grow: `mega` refuses outright, `ultra` defers, `high`
|
|
98
|
+
* dampens, and the quiet levels admit.
|
|
99
|
+
*/
|
|
100
|
+
const PRESSURE_ACTION = {
|
|
101
|
+
low: "admit",
|
|
102
|
+
medium: "admit",
|
|
103
|
+
high: "dampen",
|
|
104
|
+
ultra: "defer",
|
|
105
|
+
mega: "reject",
|
|
106
|
+
};
|
|
107
|
+
/** Select the reason code that explains the decision. */
|
|
108
|
+
function reasonFor(pressure, requested, clamped, bounds) {
|
|
109
|
+
if (clamped === bounds.maxBudget && requested > bounds.maxBudget) {
|
|
110
|
+
return "budget_clamped_high";
|
|
111
|
+
}
|
|
112
|
+
if (clamped === bounds.minBudget && requested < bounds.minBudget) {
|
|
113
|
+
return "budget_clamped_low";
|
|
114
|
+
}
|
|
115
|
+
if (pressure === "mega" || pressure === "ultra")
|
|
116
|
+
return "pressure_critical";
|
|
117
|
+
if (pressure === "high")
|
|
118
|
+
return "pressure_elevated";
|
|
119
|
+
return "within_bounds";
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Evaluate one policy input into a bounded decision.
|
|
123
|
+
*
|
|
124
|
+
* Order matters: validate the label, validate the window, apply the
|
|
125
|
+
* pressure factor, THEN clamp. Clamping last is what makes the bounded-budget
|
|
126
|
+
* guarantee hold for every action including escalate.
|
|
127
|
+
*
|
|
128
|
+
* Throws `{ code }` on an unknown pressure label or an invalid bound pair.
|
|
129
|
+
*/
|
|
130
|
+
export function evaluatePolicy(input) {
|
|
131
|
+
const pressure = validatePressureLabel(input.pressure);
|
|
132
|
+
const bounds = validateBounds(input.bounds);
|
|
133
|
+
const requested = Number.isFinite(input.requestedBudget)
|
|
134
|
+
? input.requestedBudget
|
|
135
|
+
: bounds.minBudget;
|
|
136
|
+
const adjusted = requested * PRESSURE_FACTOR[pressure];
|
|
137
|
+
const budget = clampBudget(adjusted, bounds.minBudget, bounds.maxBudget);
|
|
138
|
+
return {
|
|
139
|
+
schema: POLICY_DECISION_SCHEMA_V1,
|
|
140
|
+
decisionId: input.decisionId,
|
|
141
|
+
sessionId: input.sessionId,
|
|
142
|
+
action: PRESSURE_ACTION[pressure],
|
|
143
|
+
budget,
|
|
144
|
+
pressure,
|
|
145
|
+
reason: reasonFor(pressure, input.requestedBudget, budget, bounds),
|
|
146
|
+
ts: input.ts,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Assert a decision satisfies the sprint invariant: allowed action AND bounded
|
|
151
|
+
* budget. Used by the acceptance aggregator to check every produced row.
|
|
152
|
+
*/
|
|
153
|
+
export function isDecisionWithinBounds(decision, bounds) {
|
|
154
|
+
return (isPolicyAction(decision.action) &&
|
|
155
|
+
Number.isFinite(decision.budget) &&
|
|
156
|
+
decision.budget >= bounds.minBudget &&
|
|
157
|
+
decision.budget <= bounds.maxBudget);
|
|
158
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* controller/shadow.ts — VC8B shadow policy evaluator (PURE, READ-ONLY).
|
|
3
|
+
*
|
|
4
|
+
* The shadow engine runs the candidate policy alongside the live path so its
|
|
5
|
+
* decisions can be measured before they are trusted. That is only safe if the
|
|
6
|
+
* shadow is structurally incapable of affecting the live path, so this module
|
|
7
|
+
* takes the capability argument seriously:
|
|
8
|
+
*
|
|
9
|
+
* - NO RENDERER. It imports no renderer and returns no rendered bytes.
|
|
10
|
+
* - NO STORE WRITER. It imports no store and performs no write.
|
|
11
|
+
* - NO PROMPT MUTATION. It receives the canonical prompt as bytes it may only
|
|
12
|
+
* hash, and it re-hashes on exit to PROVE the bytes are unchanged
|
|
13
|
+
* (POL-SHADOW-002). `liveMutations` is reported and is always 0.
|
|
14
|
+
*
|
|
15
|
+
* INPUTS ARE COPIED, NOT BORROWED. Every input is deep-copied on entry, so even
|
|
16
|
+
* a future policy change that mutated its argument could not reach the caller's
|
|
17
|
+
* object. The copy is the enforcement; the `readonly` types are only the
|
|
18
|
+
* documentation of it. This is the difference between "we don't mutate" and
|
|
19
|
+
* "we cannot mutate".
|
|
20
|
+
*
|
|
21
|
+
* A rejected input does NOT abort the run: the shadow's job is measurement, so
|
|
22
|
+
* one unknown pressure label is recorded as a rejection code and the remaining
|
|
23
|
+
* inputs are still evaluated.
|
|
24
|
+
*
|
|
25
|
+
* PREVENT-002/011/PI-004 honored.
|
|
26
|
+
*/
|
|
27
|
+
import { createHash } from "node:crypto";
|
|
28
|
+
import { evaluatePolicy } from "./policy.js";
|
|
29
|
+
/** SHA-256 of the canonical prompt bytes, lowercase hex (VC5B convention). */
|
|
30
|
+
export function promptDigestOf(promptBytes) {
|
|
31
|
+
return createHash("sha256")
|
|
32
|
+
.update(Buffer.from(promptBytes, "utf8"))
|
|
33
|
+
.digest("hex");
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Deep-copy one policy input. Explicit field-by-field construction rather than
|
|
37
|
+
* a structured clone: it keeps the copy total over the declared shape and makes
|
|
38
|
+
* an added field a compile error instead of a silently shared reference.
|
|
39
|
+
*/
|
|
40
|
+
export function copyPolicyInput(input) {
|
|
41
|
+
return {
|
|
42
|
+
decisionId: input.decisionId,
|
|
43
|
+
sessionId: input.sessionId,
|
|
44
|
+
pressure: input.pressure,
|
|
45
|
+
requestedBudget: input.requestedBudget,
|
|
46
|
+
bounds: {
|
|
47
|
+
minBudget: input.bounds.minBudget,
|
|
48
|
+
maxBudget: input.bounds.maxBudget,
|
|
49
|
+
},
|
|
50
|
+
ts: input.ts,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
/** Extract the machine code from a thrown policy failure. */
|
|
54
|
+
function codeOf(err) {
|
|
55
|
+
if (typeof err === "object" && err !== null && "code" in err) {
|
|
56
|
+
const code = err.code;
|
|
57
|
+
if (typeof code === "string")
|
|
58
|
+
return code;
|
|
59
|
+
}
|
|
60
|
+
return "POL_UNKNOWN_FAILURE";
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Evaluate a batch of policy inputs in shadow mode.
|
|
64
|
+
*
|
|
65
|
+
* Returns decisions + metrics ONLY. The caller receives no capability to apply
|
|
66
|
+
* any of it; promoting a shadow decision is a separate, explicit act.
|
|
67
|
+
*
|
|
68
|
+
* @param inputs the policy inputs to evaluate (copied, never mutated)
|
|
69
|
+
* @param promptBytes the canonical prompt, used ONLY to prove non-mutation
|
|
70
|
+
*/
|
|
71
|
+
export function evaluateShadow(inputs, promptBytes) {
|
|
72
|
+
// Hash the prompt BEFORE any evaluation so the exit comparison is meaningful.
|
|
73
|
+
const digestOnEntry = promptDigestOf(promptBytes);
|
|
74
|
+
// Copy every input up front: nothing downstream ever sees the caller's object.
|
|
75
|
+
const copies = inputs.map(copyPolicyInput);
|
|
76
|
+
const decisions = [];
|
|
77
|
+
const rejections = [];
|
|
78
|
+
let clamped = 0;
|
|
79
|
+
for (const copy of copies) {
|
|
80
|
+
try {
|
|
81
|
+
const decision = evaluatePolicy(copy);
|
|
82
|
+
decisions.push(decision);
|
|
83
|
+
const atBound = decision.reason === "budget_clamped_low" ||
|
|
84
|
+
decision.reason === "budget_clamped_high";
|
|
85
|
+
if (atBound)
|
|
86
|
+
clamped += 1;
|
|
87
|
+
}
|
|
88
|
+
catch (err) {
|
|
89
|
+
// Measurement continues: one bad row must not blind the whole run.
|
|
90
|
+
rejections.push({ decisionId: copy.decisionId, code: codeOf(err) });
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
// Re-hash on exit. Equality here is the POL-SHADOW-002 proof that the shadow
|
|
94
|
+
// left the canonical prompt untouched.
|
|
95
|
+
const digestOnExit = promptDigestOf(promptBytes);
|
|
96
|
+
const promptUnchanged = digestOnEntry === digestOnExit;
|
|
97
|
+
return {
|
|
98
|
+
decisions,
|
|
99
|
+
rejections,
|
|
100
|
+
metrics: {
|
|
101
|
+
evaluated: decisions.length,
|
|
102
|
+
clamped,
|
|
103
|
+
rejected: rejections.length,
|
|
104
|
+
// Structurally zero: this module holds no writer capability. If the
|
|
105
|
+
// prompt digest ever moved, that is a live mutation and it is counted.
|
|
106
|
+
liveMutations: promptUnchanged ? 0 : 1,
|
|
107
|
+
},
|
|
108
|
+
promptDigest: digestOnExit,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Assert the shadow result carries no live mutation. The sprint's acceptance
|
|
113
|
+
* bar is "shadow live mutation count zero"; this is that check as a function.
|
|
114
|
+
*/
|
|
115
|
+
export function isShadowClean(result) {
|
|
116
|
+
return result.metrics.liveMutations === 0;
|
|
117
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* controller/types.ts — VC8B policy decision + pressure type definitions.
|
|
3
|
+
*
|
|
4
|
+
* PolicyDecisionV1 is the FINITE, BOUNDED output of the policy engine: one of a
|
|
5
|
+
* closed action set, a token budget clamped into a configured window, and a
|
|
6
|
+
* machine code reason. There is deliberately no free-text field and no open
|
|
7
|
+
* action string — an adaptive policy whose action space can grow at runtime
|
|
8
|
+
* cannot be reviewed, and a budget with no ceiling is a cost incident waiting
|
|
9
|
+
* to happen.
|
|
10
|
+
*
|
|
11
|
+
* PressureV2 canonicalizes the context-pressure label to EXACTLY five levels
|
|
12
|
+
* (low/medium/high/ultra/mega). Anything else is rejected rather than coerced:
|
|
13
|
+
* silently mapping an unrecognized legacy label onto a neighbouring level is
|
|
14
|
+
* how a "high" workload quietly starts being treated as "low".
|
|
15
|
+
*
|
|
16
|
+
* Conformance IDs POL-001..025 and M7-001..015 are registered here as the
|
|
17
|
+
* single source of truth for the sprint's conformance rows.
|
|
18
|
+
*
|
|
19
|
+
* PREVENT-PI-004: type definitions only, no network code.
|
|
20
|
+
* PREVENT-011: no `any` type.
|
|
21
|
+
*/
|
|
22
|
+
/** Schema version for PolicyDecisionV1. */
|
|
23
|
+
export const POLICY_DECISION_SCHEMA_V1 = "policy-decision-v1";
|
|
24
|
+
/** Schema version for PressureV2. */
|
|
25
|
+
export const PRESSURE_SCHEMA_V2 = "pressure-v2";
|
|
26
|
+
/** Failure code when a pressure label is outside the canonical five levels. */
|
|
27
|
+
export const POL_PRESSURE_UNKNOWN = "POL_PRESSURE_UNKNOWN";
|
|
28
|
+
/** Failure code when a requested action is outside the allowed finite set. */
|
|
29
|
+
export const POL_ACTION_FORBIDDEN = "POL_ACTION_FORBIDDEN";
|
|
30
|
+
/** Failure code when a budget bound pair is itself invalid (min > max, NaN). */
|
|
31
|
+
export const POL_BUDGET_OUT_OF_BOUNDS = "POL_BUDGET_OUT_OF_BOUNDS";
|
|
32
|
+
/** Failure code when the M7 migration meets a non-canonical pressure label. */
|
|
33
|
+
export const M7_PRESSURE_UNKNOWN = "M7_PRESSURE_UNKNOWN";
|
|
34
|
+
/** Failure code when M7 copied rows do not match the legacy row count. */
|
|
35
|
+
export const M7_COUNT_MISMATCH = "M7_COUNT_MISMATCH";
|
|
36
|
+
/** Failure code when an M7 row digest does not re-derive from its own fields. */
|
|
37
|
+
export const M7_DIGEST_MISMATCH = "M7_DIGEST_MISMATCH";
|
|
38
|
+
/** Failure code when the active pressure pointer is not on the legacy version. */
|
|
39
|
+
export const M7_NOT_ON_LEGACY = "M7_NOT_ON_LEGACY";
|
|
40
|
+
/**
|
|
41
|
+
* The canonical five pressure levels. Ordered low -> mega; the order is
|
|
42
|
+
* meaningful (policy escalates monotonically with pressure) so it is exported
|
|
43
|
+
* as an array, not just a union.
|
|
44
|
+
*/
|
|
45
|
+
export const PRESSURE_LEVELS = [
|
|
46
|
+
"low",
|
|
47
|
+
"medium",
|
|
48
|
+
"high",
|
|
49
|
+
"ultra",
|
|
50
|
+
"mega",
|
|
51
|
+
];
|
|
52
|
+
/**
|
|
53
|
+
* The FINITE allowed policy action set. A decision may carry no other action.
|
|
54
|
+
* `admit` — proceed at the requested budget.
|
|
55
|
+
* `dampen` — proceed at a reduced budget (pressure is elevated).
|
|
56
|
+
* `defer` — postpone the work to a later turn.
|
|
57
|
+
* `escalate` — raise the budget within bounds (headroom is available).
|
|
58
|
+
* `reject` — refuse the work outright.
|
|
59
|
+
*/
|
|
60
|
+
export const POLICY_ACTIONS = [
|
|
61
|
+
"admit",
|
|
62
|
+
"dampen",
|
|
63
|
+
"defer",
|
|
64
|
+
"escalate",
|
|
65
|
+
"reject",
|
|
66
|
+
];
|
|
67
|
+
/** Machine reason codes — never free-text. */
|
|
68
|
+
export const POLICY_REASONS = [
|
|
69
|
+
"within_bounds",
|
|
70
|
+
"budget_clamped_low",
|
|
71
|
+
"budget_clamped_high",
|
|
72
|
+
"pressure_elevated",
|
|
73
|
+
"pressure_critical",
|
|
74
|
+
"headroom_available",
|
|
75
|
+
];
|
|
76
|
+
/** Conformance IDs POL-001..POL-025 for the 25 numbered policy rows. */
|
|
77
|
+
export const POLICY_CONFORMANCE_IDS = Array.from({ length: 25 }, (_v, i) => `POL-${String(i + 1).padStart(3, "0")}`);
|
|
78
|
+
/** Conformance IDs M7-001..M7-015 for the 15 numbered migration rows. */
|
|
79
|
+
export const M7_CONFORMANCE_IDS = Array.from({ length: 15 }, (_v, i) => `M7-${String(i + 1).padStart(3, "0")}`);
|
|
80
|
+
/** Named conformance fixtures for the sprint's headline assertions. */
|
|
81
|
+
export const POLICY_NAMED_FIXTURES = [
|
|
82
|
+
"POL-CLAMP-001",
|
|
83
|
+
"POL-SHADOW-002",
|
|
84
|
+
"M7-PRESSURE-003",
|
|
85
|
+
];
|