pi-fovea 0.25.0 → 0.26.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/README.md +6 -0
- package/docs/substrate.md +23 -0
- package/package.json +4 -2
- package/src/substrate.ts +46 -0
package/README.md
CHANGED
|
@@ -520,6 +520,12 @@ special case of this field. [docs/heat-diffusion.md](docs/heat-diffusion.md)
|
|
|
520
520
|
walks through conductance tiers, specificity bridges, hub gravity, and inferred
|
|
521
521
|
regions.
|
|
522
522
|
|
|
523
|
+
## Snapshot substrate API
|
|
524
|
+
|
|
525
|
+
`pi-fovea/substrate` exposes a versioned, session-free graph and heat interface for
|
|
526
|
+
consumers such as pi-contour. It assembles caller-supplied immutable facts without
|
|
527
|
+
discovering files or reading a live worktree. See [docs/substrate.md](docs/substrate.md).
|
|
528
|
+
|
|
523
529
|
## Languages
|
|
524
530
|
|
|
525
531
|
Full symbol and call extraction: **TypeScript, TSX, JavaScript, Python, Go, and Rust**.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Snapshot substrate v1
|
|
2
|
+
|
|
3
|
+
`pi-fovea/substrate` is a small, session-free entrypoint for graph consumers. It does not enroll roots, discover files, read project rules, perform extraction, or inspect the current working tree.
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
import { SUBSTRATE_VERSION, assembleFactGraph, diffuseMass, type SnapshotFacts } from "pi-fovea/substrate";
|
|
7
|
+
|
|
8
|
+
if (SUBSTRATE_VERSION !== 1) throw new Error("Unsupported substrate version");
|
|
9
|
+
const facts = new Map<string, SnapshotFacts>();
|
|
10
|
+
// Populate immutable per-file symbol/import/call/literal/anchor facts.
|
|
11
|
+
const graph = await assembleFactGraph(facts);
|
|
12
|
+
const source = new Float64Array(graph.nodes.length);
|
|
13
|
+
// Assign nonnegative source mass to graph node indices.
|
|
14
|
+
const fields = diffuseMass(graph, source, [0.5, 2, 8]);
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
`SnapshotFacts` is the explicit projection of Fovea's extraction-fact shape. Its legacy `sha1` field is caller-owned content identity; graph assembly does not interpret that digest. Every site must name its owning repository-relative file. Unsafe paths and cross-file witnesses are rejected. Callers own source coverage and must keep supplied facts immutable during assembly.
|
|
18
|
+
|
|
19
|
+
The resulting `Graph` retains nodes, typed relationship evidence, file/name indexes, and import coverage. Callers can select or reweight relationships without introducing a second graph resolver. Navigation conductances are not calibrated defect probabilities.
|
|
20
|
+
|
|
21
|
+
`diffuseMass` accepts a graph, a finite nonnegative source vector matching the node count, and finite times in [0,64]. Results are `{ time, mass: Float64Array }` in requested order. It shares Chebyshev recurrence vectors across times and applies the forward random-walk conjugation of the symmetric heat kernel. Isolates retain their original mass. Floating-point conservation is approximate; tests cover it against existing solver paths, whose independent reference tests remain in the core suite.
|
|
22
|
+
|
|
23
|
+
No session, disclosure, snapshot cache, or quality policy is implied by this API. Consumers such as pi-contour supply immutable Git/index snapshots, analysis-specific projections, source evidence, and their own bounded caches.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-fovea",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.26.0",
|
|
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",
|
|
@@ -34,10 +34,12 @@
|
|
|
34
34
|
"image": "https://raw.githubusercontent.com/monotykamary/pi-fovea/main/media/cover.svg"
|
|
35
35
|
},
|
|
36
36
|
"exports": {
|
|
37
|
-
"./ops": "./src/core/ops.ts"
|
|
37
|
+
"./ops": "./src/core/ops.ts",
|
|
38
|
+
"./substrate": "./src/substrate.ts"
|
|
38
39
|
},
|
|
39
40
|
"files": [
|
|
40
41
|
"src",
|
|
42
|
+
"docs/substrate.md",
|
|
41
43
|
"cli.ts",
|
|
42
44
|
"dist",
|
|
43
45
|
"skills",
|
package/src/substrate.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// Versioned, session-free substrate for consumers of immutable extraction facts.
|
|
2
|
+
// This module never discovers files, reads project configuration, or loads a session.
|
|
3
|
+
import { assembleGraphWithIndex } from "./core/graph.js";
|
|
4
|
+
import { buildCsr, chebyshevVectors, chooseOrder, heatField } from "./core/heat.js";
|
|
5
|
+
import type { FileFacts } from "./core/build.js";
|
|
6
|
+
import type { Graph } from "./core/types.js";
|
|
7
|
+
|
|
8
|
+
export const SUBSTRATE_VERSION = 1 as const;
|
|
9
|
+
export type SnapshotFacts = Pick<FileFacts, "sha1" | "symbols" | "imports" | "calls" | "literals" | "anchors">;
|
|
10
|
+
export type { Graph, Edge, NodeRec, EdgeEvidence } from "./core/types.js";
|
|
11
|
+
|
|
12
|
+
/** Assemble only the supplied files. Paths and witnesses must belong to this snapshot. */
|
|
13
|
+
export async function assembleFactGraph(facts: ReadonlyMap<string, SnapshotFacts>): Promise<Graph> {
|
|
14
|
+
const files = [...facts.keys()].sort();
|
|
15
|
+
for (const file of files) {
|
|
16
|
+
if (!file || file.startsWith("/") || file.includes("\\") || file.split("/").some(p => p === ".." || p === "." || !p)) {
|
|
17
|
+
throw new Error(`Invalid snapshot path: ${file}`);
|
|
18
|
+
}
|
|
19
|
+
const fact = facts.get(file)!;
|
|
20
|
+
for (const site of [...fact.symbols, ...fact.imports, ...fact.calls, ...fact.literals, ...fact.anchors]) {
|
|
21
|
+
if (site.file !== file) throw new Error(`Witness outside its snapshot file: ${site.file}`);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return (await assembleGraphWithIndex("", files, new Map(facts))).graph;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Column-mass heat D^(1/2) exp(-t Lsym) D^(-1/2) q, sharing one Chebyshev basis.
|
|
28
|
+
* Times are analysis scales, not wall-clock ages. Isolates retain their source mass.
|
|
29
|
+
* Results are exposure, not calibrated risk. Callers may project/reweight the graph.
|
|
30
|
+
*/
|
|
31
|
+
export function diffuseMass(graph: Graph, source: Float64Array, times: readonly number[]): Array<{ time: number; mass: Float64Array }> {
|
|
32
|
+
if (source.length !== graph.nodes.length || source.some(v => !Number.isFinite(v) || v < 0)) {
|
|
33
|
+
throw new Error("Mass must be finite, nonnegative, and match the graph");
|
|
34
|
+
}
|
|
35
|
+
if (times.some(t => !Number.isFinite(t) || t < 0 || t > 64)) throw new Error("Diffusion scales must lie in [0, 64]");
|
|
36
|
+
if (graph.edges.some(e => !Number.isFinite(e.w) || e.w < 0 || !Number.isInteger(e.a) || !Number.isInteger(e.b)
|
|
37
|
+
|| e.a < 0 || e.b < 0 || e.a >= source.length || e.b >= source.length)) throw new Error("Invalid conductance edge");
|
|
38
|
+
if (!times.length) return [];
|
|
39
|
+
const csr = buildCsr(graph);
|
|
40
|
+
const seed = Float64Array.from(source, (mass, i) => csr.deg[i]! > 0 ? mass / Math.sqrt(csr.deg[i]!) : 0);
|
|
41
|
+
const vectors = chebyshevVectors(csr, seed, chooseOrder(Math.max(...times)));
|
|
42
|
+
return times.map(time => {
|
|
43
|
+
const field = heatField(vectors, time, csr.n);
|
|
44
|
+
return { time, mass: Float64Array.from(source, (mass, i) => csr.deg[i]! > 0 ? Math.sqrt(csr.deg[i]!) * field[i]! : mass) };
|
|
45
|
+
});
|
|
46
|
+
}
|