pi-fovea 0.22.1 → 0.22.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -552,9 +552,28 @@ Exact contract topology: **Protocol Buffers (`.proto`) and GraphQL (`.graphql`,
552
552
  ```sh
553
553
  bun install
554
554
  bun run check # typecheck + full vitest suite
555
- bun run bench # rate–distortion bench against ../pi-fabric
555
+ bun run bench # rate–distortion and refresh bench against ../pi-fabric
556
+ bun run bench tests/fixtures/mini # self-contained smoke run
556
557
  ```
557
558
 
559
+ The developer benchmark gates timings on semantic equivalence: cold versus
560
+ cached builds, and forced refresh versus clean rebuild after unchanged,
561
+ location-only, semantic, added-file, and deleted-file scenarios in disposable
562
+ copies of the cross-language fixture. It checks facts, weighted edges and
563
+ evidence, coverage, operator contents, and fixture navigation; extraction-order
564
+ permutations and opaque rebuild hashes are not semantic differences. Runtime
565
+ ordering and generation invalidation are unchanged.
566
+
567
+ Reports include focus/dwell and refresh median/p95 samples, actual estimated
568
+ output tokens, and process peak RSS (including the validation work). Cold and
569
+ disk-warm target builds are single samples; three-sample refresh p95s are only
570
+ smoke diagnostics. The outline gets no more tokens than Fovea actually used.
571
+ `fidelity@16k` measures disclosed node IDs against a finite larger Fovea response,
572
+ **not independently labeled relevance**. Timing results are informational, never
573
+ a flaky CI gate; deterministic equivalence tests run in `bun run check`.
574
+ The bench clears the target's disposable facts cache to measure cold loading,
575
+ but edits only temporary fixture copies.
576
+
558
577
  pi loads the extension straight from `src/` through jiti, so nothing needs
559
578
  building. Per-repo JSONL caches live in `$TMPDIR`, guarded by per-file content
560
579
  sha1 values and stat manifests. Cache I/O streams. Only dirty files re-run
package/dist/cli.mjs CHANGED
@@ -403,10 +403,13 @@ var buildCsr = (g) => {
403
403
  }
404
404
  return { n, rowPtr, col, w, deg };
405
405
  };
406
- var applyNegP = (csr, x) => {
407
- const { n, rowPtr, col, w, deg } = csr;
408
- const invSqrt = new Float64Array(n);
409
- for (let i = 0; i < n; i++) invSqrt[i] = deg[i] > 0 ? 1 / Math.sqrt(deg[i]) : 0;
406
+ var inverseDegrees = ({ n, deg }) => {
407
+ const out = new Float64Array(n);
408
+ for (let i = 0; i < n; i++) out[i] = deg[i] > 0 ? 1 / Math.sqrt(deg[i]) : 0;
409
+ return out;
410
+ };
411
+ var applyNegP = (csr, x, invSqrt = inverseDegrees(csr)) => {
412
+ const { n, rowPtr, col, w } = csr;
410
413
  const y = new Float64Array(n);
411
414
  for (let i = 0; i < n; i++) {
412
415
  let acc = 0;
@@ -464,13 +467,14 @@ var heatCoeff = (k, t) => {
464
467
  var chebyshevVectors = (csr, s, K) => {
465
468
  const tk = new Array(K + 1);
466
469
  tk[0] = Float64Array.from(s);
467
- if (K >= 1) tk[1] = applyNegP(csr, tk[0]);
470
+ if (K === 0) return tk;
471
+ const invSqrt = inverseDegrees(csr);
472
+ if (K >= 1) tk[1] = applyNegP(csr, tk[0], invSqrt);
468
473
  for (let k = 2; k <= K; k++) {
469
474
  const prev = tk[k - 1];
470
- const mv = applyNegP(csr, prev);
471
- const out = new Float64Array(csr.n);
475
+ const out = applyNegP(csr, prev, invSqrt);
472
476
  const p2 = tk[k - 2];
473
- for (let i = 0; i < csr.n; i++) out[i] = 2 * mv[i] - p2[i];
477
+ for (let i = 0; i < csr.n; i++) out[i] = 2 * out[i] - p2[i];
474
478
  tk[k] = out;
475
479
  }
476
480
  return tk;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-fovea",
3
- "version": "0.22.1",
3
+ "version": "0.22.2",
4
4
  "description": "Token-budgeted repo mapping for agent sessions: foveated heat diffusion over a cross-language code graph, with progressive disclosure.",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/src/core/heat.ts CHANGED
@@ -64,11 +64,15 @@ export const buildCsr = (g: Graph): Csr => {
64
64
  return { n, rowPtr, col, w, deg };
65
65
  };
66
66
 
67
- // y = -P~ x where P~ = D^{-1/2} W D^{-1/2}. Isolated nodes (deg 0) map to themselves under heat.
68
- const applyNegP = (csr: Csr, x: Float64Array): Float64Array => {
69
- const { n, rowPtr, col, w, deg } = csr;
70
- const invSqrt = new Float64Array(n);
71
- for (let i = 0; i < n; i++) invSqrt[i] = deg[i]! > 0 ? 1 / Math.sqrt(deg[i]!) : 0;
67
+ const inverseDegrees = ({ n, deg }: Csr): Float64Array => {
68
+ const out = new Float64Array(n);
69
+ for (let i = 0; i < n; i++) out[i] = deg[i]! > 0 ? 1 / Math.sqrt(deg[i]!) : 0;
70
+ return out;
71
+ };
72
+
73
+ // y = -P~ x where P~ = D^{-1/2} W D^{-1/2}.
74
+ const applyNegP = (csr: Csr, x: Float64Array, invSqrt = inverseDegrees(csr)): Float64Array => {
75
+ const { n, rowPtr, col, w } = csr;
72
76
  const y = new Float64Array(n);
73
77
  for (let i = 0; i < n; i++) {
74
78
  let acc = 0;
@@ -130,13 +134,16 @@ const heatCoeff = (k: number, t: number): number => {
130
134
  export const chebyshevVectors = (csr: Csr, s: Float64Array, K: number): Float64Array[] => {
131
135
  const tk: Float64Array[] = new Array(K + 1);
132
136
  tk[0] = Float64Array.from(s);
133
- if (K >= 1) tk[1] = applyNegP(csr, tk[0]!);
137
+ // Invocation-local normalization preserves compatibility with mutable/synthetic CSR
138
+ // callers without adding a persistent cache or a new invalidation boundary.
139
+ if (K === 0) return tk;
140
+ const invSqrt = inverseDegrees(csr);
141
+ if (K >= 1) tk[1] = applyNegP(csr, tk[0]!, invSqrt);
134
142
  for (let k = 2; k <= K; k++) {
135
143
  const prev = tk[k - 1]!;
136
- const mv = applyNegP(csr, prev);
137
- const out = new Float64Array(csr.n);
144
+ const out = applyNegP(csr, prev, invSqrt);
138
145
  const p2 = tk[k - 2]!;
139
- for (let i = 0; i < csr.n; i++) out[i] = 2 * mv[i]! - p2[i]!;
146
+ for (let i = 0; i < csr.n; i++) out[i] = 2 * out[i]! - p2[i]!;
140
147
  tk[k] = out;
141
148
  }
142
149
  return tk;