pi-fovea 0.29.2 → 0.31.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/LAWS.bend ADDED
@@ -0,0 +1,110 @@
1
+ import Base
2
+ import ./proofs/disclosure.bend as D
3
+ import ./proofs/heat.bend as H
4
+ import ./proofs/kernel.bend as K
5
+ import ./proofs/basis.bend as B
6
+
7
+ # Proposed specifications extracted from render.ts and heat.ts. Review these independently
8
+ # of PROOF.bend; do not weaken a law to make an implementation pass.
9
+
10
+ # Disclosure: these six laws cover the entire five-Boolean input domain.
11
+ law scope_wins:
12
+ for excluded: Bool
13
+ for seen: Bool
14
+ for repeat: Bool
15
+ for nucleus: Bool
16
+ {K.disclosure(False{}, excluded, seen, repeat, nucleus) == D.Drop{} : D.Decision}
17
+
18
+ law exclusion_wins:
19
+ for scope: Bool
20
+ for seen: Bool
21
+ for repeat: Bool
22
+ for nucleus: Bool
23
+ {K.disclosure(scope, True{}, seen, repeat, nucleus) == D.Drop{} : D.Decision}
24
+
25
+ law fresh_revealed:
26
+ for repeat: Bool
27
+ for nucleus: Bool
28
+ {K.disclosure(True{}, False{}, False{}, repeat, nucleus) == D.Reveal{} : D.Decision}
29
+
30
+ law seen_suppressed:
31
+ for nucleus: Bool
32
+ {K.disclosure(True{}, False{}, True{}, False{}, nucleus) == D.Suppress{} : D.Decision}
33
+
34
+ law periphery_suppressed:
35
+ for repeat: Bool
36
+ {K.disclosure(True{}, False{}, True{}, repeat, False{}) == D.Suppress{} : D.Decision}
37
+
38
+ law nucleus_repeated:
39
+ for seen: Bool
40
+ {K.disclosure(True{}, False{}, seen, True{}, True{}) == D.Reveal{} : D.Decision}
41
+
42
+ # Executed natural-number accounting. This does NOT prove token estimation,
43
+ # renderer formatting, or binary-search monotonicity. Keep boundary tests.
44
+ law shown_bounded_by_total:
45
+ for +total: Nat
46
+ for requested: Nat
47
+ {Nat.is_le(K.shownCount(total, requested), total) == True{} : Bool}
48
+
49
+ law shown_bounded_by_request:
50
+ for total: Nat
51
+ for +requested: Nat
52
+ {Nat.is_le(K.shownCount(total, requested), requested) == True{} : Bool}
53
+
54
+ law counts_conserved:
55
+ for +total: Nat
56
+ for +requested: Nat
57
+ {Nat.add(K.shownCount(total, requested), K.remainingCount(total, requested)) == total : Nat}
58
+
59
+ law full_prefix_omits_nothing:
60
+ for +total: Nat
61
+ {K.remainingCount(total, total) == 0n : Nat}
62
+
63
+ law cap_idempotent:
64
+ for +total: Nat
65
+ for +cap: Nat
66
+ {K.shownCount(K.shownCount(total, cap), cap) == K.shownCount(total, cap) : Nat}
67
+
68
+ # Exact nonnegative rational transport model, not IEEE-754 or the exponential.
69
+ law heat_vector_add_mass:
70
+ for +xs: +List<Nat>
71
+ for +ys: +List<Nat>
72
+ {H.sum(H.add(xs, ys)) == Nat.add(H.sum(xs), H.sum(ys)) : Nat}
73
+
74
+ law heat_scale_mass:
75
+ for +mass: Nat
76
+ for +weights: +List<Nat>
77
+ {H.sum(H.scale(mass, weights)) == Nat.mul(mass, H.sum(weights)) : Nat}
78
+
79
+ # D = 1 + denominator is strictly positive. For source numerators a over Q,
80
+ # transport returns numerators over D*Q; this equality conserves rational mass.
81
+ law heat_transport_mass:
82
+ for +columns: +List<H.Column>
83
+ for +denominator: Nat
84
+ for normalized: H.Normalized(columns, 1n+denominator)
85
+ {H.sum(H.transport(columns)) == Nat.mul(H.input_mass(columns), 1n+denominator) : Nat}
86
+
87
+ law heat_singleton_mass:
88
+ for +mass: Nat
89
+ {H.sum(H.transport([H.Source{mass, [1n]}])) == mass : Nat}
90
+
91
+ # Executed basis-extension commands. Float64 evaluation remains host code.
92
+ law basis_empty_refused:
93
+ for order: Nat
94
+ {K.basisStep(0n, order) == B.Empty{} : B.Step}
95
+
96
+ law basis_complete_retained:
97
+ for +have: Nat
98
+ for +order: Nat
99
+ for complete: {Nat.is_le(1n+have, order) == False{} : Bool}
100
+ {K.basisStep(1n+have, order) == B.Done{} : B.Step}
101
+
102
+ law basis_first_order:
103
+ for order: Nat
104
+ {K.basisStep(1n, 1n+order) == B.First{} : B.Step}
105
+
106
+ law basis_next_exact:
107
+ for +have: Nat
108
+ for +order: Nat
109
+ for missing: {Nat.is_le(2n+have, order) == True{} : Bool}
110
+ {K.basisStep(2n+have, order) == B.Next{2n+have, 1n+have, have} : B.Step}
package/PROOF.bend ADDED
@@ -0,0 +1,118 @@
1
+ import Base
2
+ import ./LAWS.bend as Laws
3
+ import ./proofs/disclosure.bend as D
4
+ import ./proofs/counts.bend as C
5
+ import ./proofs/counts-proof.bend as CP
6
+ import ./proofs/heat.bend as H
7
+ import ./proofs/nat-proof.bend as N
8
+ import ./proofs/kernel.bend as K
9
+ import ./proofs/basis.bend as B
10
+
11
+ def Laws.scope_wins(excluded, seen, repeat, nucleus):
12
+ {==}
13
+
14
+ def Laws.exclusion_wins(scope, seen, repeat, nucleus):
15
+ match scope:
16
+ case False{}:
17
+ {==}
18
+ case True{}:
19
+ {==}
20
+
21
+ def Laws.fresh_revealed(repeat, nucleus):
22
+ {==}
23
+
24
+ def Laws.seen_suppressed(nucleus):
25
+ {==}
26
+
27
+ def Laws.periphery_suppressed(repeat):
28
+ match repeat:
29
+ case False{}:
30
+ {==}
31
+ case True{}:
32
+ {==}
33
+
34
+ def Laws.nucleus_repeated(seen):
35
+ match seen:
36
+ case False{}:
37
+ {==}
38
+ case True{}:
39
+ {==}
40
+
41
+ def Laws.shown_bounded_by_total(total, requested):
42
+ +requested = requested
43
+ %Equal.sym(Nat, C.fastShown(total, requested), C.shown(total, requested), CP.shown_equiv(total, requested)) : {Nat.is_le(_, total) == True{} : Bool}
44
+ CP.bounded_total(total, requested)
45
+
46
+ def Laws.shown_bounded_by_request(total, requested):
47
+ +total = total
48
+ %Equal.sym(Nat, C.fastShown(total, requested), C.shown(total, requested), CP.shown_equiv(total, requested)) : {Nat.is_le(_, requested) == True{} : Bool}
49
+ CP.bounded_request(total, requested)
50
+
51
+ def Laws.counts_conserved(total, requested):
52
+ %Equal.sym(Nat, C.fastShown(total, requested), C.shown(total, requested), CP.shown_equiv(total, requested)) : {Nat.add(_, C.fastRemaining(total, requested)) == total : Nat}
53
+ %Equal.sym(Nat, C.fastRemaining(total, requested), C.remaining(total, requested), CP.remaining_equiv(total, requested)) : {Nat.add(C.shown(total, requested), _) == total : Nat}
54
+ CP.conserved(total, requested)
55
+
56
+ def Laws.full_prefix_omits_nothing(total):
57
+ %Equal.sym(Nat, C.fastRemaining(total, total), C.remaining(total, total), CP.remaining_equiv(total, total)) : {_ == 0n : Nat}
58
+ CP.full(total)
59
+
60
+ def Laws.cap_idempotent(total, cap):
61
+ %Equal.sym(Nat, C.fastShown(C.fastShown(total, cap), cap), C.shown(C.fastShown(total, cap), cap), CP.shown_equiv(C.fastShown(total, cap), cap)) : {_ == C.fastShown(total, cap) : Nat}
62
+ %Equal.sym(Nat, C.fastShown(total, cap), C.shown(total, cap), CP.shown_equiv(total, cap)) : {C.shown(_, cap) == C.fastShown(total, cap) : Nat}
63
+ %Equal.sym(Nat, C.fastShown(total, cap), C.shown(total, cap), CP.shown_equiv(total, cap)) : {C.shown(C.shown(total, cap), cap) == _ : Nat}
64
+ CP.idempotent(total, cap)
65
+
66
+ def Laws.heat_vector_add_mass(xs, ys):
67
+ match xs ys:
68
+ case Nil{} _:
69
+ {==}
70
+ case x <> tail Nil{}:
71
+ Equal.sym(Nat, Nat.add(H.sum(x <> tail), 0n), H.sum(x <> tail), N.add_zero(H.sum(x <> tail)))
72
+ case x <> xt y <> yt:
73
+ %N.add_shuffle(x, y, H.sum(xt), H.sum(yt)) : {Nat.add(Nat.add(x, y), H.sum(H.add(xt, yt))) == _ : Nat}
74
+ %Laws.heat_vector_add_mass(xt, yt) : {Nat.add(Nat.add(x, y), H.sum(H.add(xt, yt))) == Nat.add(Nat.add(x, y), _) : Nat}
75
+ {==}
76
+
77
+ def Laws.heat_scale_mass(mass, weights):
78
+ match mass:
79
+ case 0n:
80
+ {==}
81
+ case 1n+p:
82
+ %Laws.heat_scale_mass(p, weights) : {H.sum(H.add(weights, H.scale(p, weights))) == Nat.add(H.sum(weights), _) : Nat}
83
+ Laws.heat_vector_add_mass(weights, H.scale(p, weights))
84
+
85
+ def Laws.heat_transport_mass(columns, denominator, normalized):
86
+ match columns:
87
+ case Nil{}:
88
+ {==}
89
+ case H.Source{mass, weights} <> tail:
90
+ (column_norm, tail_norm) = normalized
91
+ %N.mul_add(mass, H.input_mass(tail), 1n+denominator) : {H.sum(H.add(H.scale(mass, weights), H.transport(tail))) == _ : Nat}
92
+ %Equal.cong(Nat, Nat, x => Nat.mul(mass, x), H.sum(weights), 1n+denominator, column_norm) : {H.sum(H.add(H.scale(mass, weights), H.transport(tail))) == Nat.add(_, Nat.mul(H.input_mass(tail), 1n+denominator)) : Nat}
93
+ %Laws.heat_scale_mass(mass, weights) : {H.sum(H.add(H.scale(mass, weights), H.transport(tail))) == Nat.add(_, Nat.mul(H.input_mass(tail), 1n+denominator)) : Nat}
94
+ %Laws.heat_transport_mass(tail, denominator, tail_norm) : {H.sum(H.add(H.scale(mass, weights), H.transport(tail))) == Nat.add(H.sum(H.scale(mass, weights)), _) : Nat}
95
+ Laws.heat_vector_add_mass(H.scale(mass, weights), H.transport(tail))
96
+
97
+ def Laws.heat_singleton_mass(mass):
98
+ %N.mul_one(mass) : {H.sum(H.transport([H.Source{mass, [1n]}])) == _ : Nat}
99
+ %N.add_zero(mass) : {H.sum(H.transport([H.Source{mass, [1n]}])) == Nat.mul(_, 1n) : Nat}
100
+ Laws.heat_transport_mass([H.Source{mass, [1n]}], 0n, ({==}, Unit{}))
101
+
102
+ def Laws.basis_empty_refused(order):
103
+ {==}
104
+
105
+ def Laws.basis_complete_retained(have, order, complete):
106
+ %Equal.sym(Bool, Nat.is_le(1n+have, order), False{}, complete) : {B.choose(_, 1n+have) == B.Done{} : B.Step}
107
+ {==}
108
+
109
+ def Laws.basis_first_order(order):
110
+ match order:
111
+ case 0n:
112
+ {==}
113
+ case 1n+p:
114
+ {==}
115
+
116
+ def Laws.basis_next_exact(have, order, missing):
117
+ %Equal.sym(Bool, Nat.is_le(2n+have, order), True{}, missing) : {B.choose(_, 2n+have) == B.Next{2n+have, 1n+have, have} : B.Step}
118
+ {==}
package/README.md CHANGED
@@ -132,7 +132,7 @@ pi install /absolute/path/to/pi-fovea
132
132
 
133
133
  </details>
134
134
 
135
- There is also a package for any agent shell or CI:
135
+ The standalone **`@monotykamary/fovea`** package provides the same engine for any agent shell or CI, without installing Pi:
136
136
 
137
137
  ```sh
138
138
  fovea sketch /path/to/repo 900
@@ -142,13 +142,19 @@ fovea rules /path/to/repo
142
142
  fovea status /path/to/repo
143
143
  ```
144
144
 
145
- Install the CLI globally — the published bin is a single self-contained bundle, so it runs on plain Node.js (no tsx, no node_modules):
145
+ Install the CLI globally — its bundled JavaScript runs on plain Node.js (no Pi or tsx required):
146
146
 
147
147
  ```sh
148
- npm i -g pi-fovea # or: bun add -g pi-fovea, bun add -g pi-fovea
148
+ npm i -g @monotykamary/fovea # or: bun add -g @monotykamary/fovea
149
149
  ```
150
150
 
151
+ The executable is still `fovea`. The legacy `npm i -g pi-fovea` remains supported;
152
+ choose one global package because both provide that executable. Installing the
153
+ Pi extension through `pi install npm:pi-fovea` can coexist with the standalone CLI.
154
+
151
155
  From a checkout, `bun run fovea` runs the live source via `tsx`, and `bun run build:cli` rebuilds `dist/cli.mjs` (the `prepack` hook keeps the published bundle in sync).
156
+ `bun run pack:cli` produces the scoped CLI tarball at `dist/fovea-cli.tgz` using the
157
+ root package version. See [CLI usage](docs/cli.md) and [release checks and publishing](docs/releasing.md).
152
158
 
153
159
  ## Many projects, one conversation
154
160
 
@@ -544,12 +550,22 @@ calls and proof checking are not modeled; Bend 1/HVM syntax is not supported.
544
550
 
545
551
  ```sh
546
552
  bun install
547
- bun run check:fast # typecheck + tests your working tree affects
553
+ bun run check:fast # Bend laws + artifact parity + typecheck + affected tests
548
554
  bun run test:smoke # curated scan-to-render floor, seconds
549
555
  bun run bench # rate–distortion and refresh bench against ../pi-fabric
550
556
  bun run bench tests/fixtures/mini # self-contained smoke run
551
557
  ```
552
558
 
559
+ Development checks require **Bend 2.0.26** (`bend guide`). See [laws, proofs, and
560
+ which tests they can replace](docs/proofs.md). Bend is development-only; the
561
+ extension and published CLI need no Bend installation or generation step.
562
+ Production executes the committed Bend-compiled disclosure, prefix-accounting,
563
+ and heat-basis command kernels. After changing their sources or adapters, run
564
+ `bun run proofs:generate`; `bun run proofs:artifact` checks the receipt without
565
+ Bend. CLI builds check that receipt; publishing also proves and reproduces the
566
+ artifact. The Float64 heat solver still relies on numerical tests, not a proof
567
+ of its approximation or rounding errors.
568
+
553
569
  The [warm-path performance report](docs/performance.md) compares the coverage-complete implementation with the optimized version. `bun run corpus:performance <coverage-work> <before-source> <after-source> [rounds]` runs alternating, isolated comparisons with a shared extraction snapshot, independent Git indexes, and exact graph, focus/dwell/sketch, and overflow-content checks. It does not trade fewer candidates or weaker freshness checks for speed. For Node, run `NODE_OPTIONS='--import tsx' node scripts/performance-corpus.mjs ...`; development dependencies are required.
554
570
 
555
571
  Warm matching derives query terms once and caches normalized symbol names by weak node identity, checking renames before reuse. Focus builds only the heat orders currently needed; dwell appends missing orders without replacing its existing basis. No new kernel or review state is introduced. Independent cold extraction can select different literal-join locations when capture order varies, so this paired benchmark deliberately makes no cold-extraction speedup claim.
@@ -0,0 +1,12 @@
1
+ # Third-party notices
2
+
3
+ ## Bend 2.0.26
4
+
5
+ The generated policy kernel contains Bend's emitted JavaScript trampoline and
6
+ compiled Base primitives from [Bend 2.0.26](https://github.com/bendlang/bend/tree/v2.0.26),
7
+ Copyright 2026 HigherOrderCO, licensed under Apache-2.0. Pi Fovea removes the
8
+ CLI invocations, adds library exports, and tree-shakes/minifies unused runtime
9
+ code using the bridge pattern developed in Pi Fabric.
10
+
11
+ The complete license is distributed at
12
+ [docs/licenses/bend-apache-2.0.txt](docs/licenses/bend-apache-2.0.txt).