trunkit 0.1.0__py3-none-any.whl
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.
- calx/__init__.py +13 -0
- calx/cli.py +400 -0
- calx/curry_adapter.py +142 -0
- calx/db.py +164 -0
- calx/generate.py +66 -0
- calx/primesieve.py +55 -0
- calx/sql/00_rehome_to_calx.sql +54 -0
- calx/sql/01_schema.sql +63 -0
- calx/sql/02_views.sql +78 -0
- calx/sql/03_generate.sql +199 -0
- calx/sql/04_crt.sql +244 -0
- calx/sql/05_dynamics.sql +529 -0
- calx/sql/06_oeis_match.sql +216 -0
- calx/sql/07_compositions.sql +75 -0
- calx/sql/10_curry.sql +148 -0
- calx/sql/20_kan.sql +141 -0
- calx/sql/21_kan_functors.sql +92 -0
- calx/sql/22_kan_elements.sql +188 -0
- calx/sql/23_kan_monoidal.sql +189 -0
- calx/sql/24_kan_natural_transformations.sql +273 -0
- calx/sql/25_kan_extensions.sql +206 -0
- calx/sql/26_kan_enrichment.sql +205 -0
- calx/sql/27_kan_profunctors.sql +241 -0
- calx/sql/28_kan_adjunctions.sql +223 -0
- calx/sql/30_kan_corpus.sql +76 -0
- calx/sql/40_cert.sql +231 -0
- calx/sql/41_cert_formal.sql +90 -0
- calx/sql/42_cert_gap_homology.sql +25 -0
- calx/sql/43_kan_sequence_homology.sql +68 -0
- calx/sql/44_cert_seq_homology.sql +26 -0
- calx/sql/45_kan_factorial_homology.sql +61 -0
- calx/sql/46_cert_factorial_homology.sql +26 -0
- calx/sql/47_kan_combined_signature.sql +62 -0
- calx/sql/48_cert_combined.sql +27 -0
- calx/sql/49_kan_shared_prime_betti.sql +62 -0
- calx/sql/50_cert_combined_scale.sql +24 -0
- calx/sql/51_cert_shared_prime_h2.sql +25 -0
- calx/sql/52_cert_developed_sequence.sql +31 -0
- calx/sql/53_cert_omega_family.sql +32 -0
- calx/sql/54_cert_omega_family_succ.sql +34 -0
- calx/sql/55_kan_prime_members.sql +54 -0
- calx/sql/56_cert_prime_members_functor.sql +33 -0
- calx/sql/57_kan_strata_tower.sql +61 -0
- calx/sql/58_cert_strata_tower.sql +31 -0
- calx/sql/59_kan_grading.sql +70 -0
- calx/sql/60_cert_grading.sql +34 -0
- calx/sql/61_kan_identity_decomposition.sql +57 -0
- calx/sql/62_cert_identity_decomposition.sql +34 -0
- calx/sql/63_kan_bigrading.sql +66 -0
- calx/sql/64_cert_bigrading.sql +39 -0
- calx/sql/65_kan_chromatic.sql +72 -0
- calx/sql/66_cert_chromatic.sql +39 -0
- calx/sql/67_kan_lithon.sql +93 -0
- calx/sql/68_cert_lithon.sql +41 -0
- calx/sql/69_kan_shadow.sql +67 -0
- calx/sql/70_cert_shadow.sql +42 -0
- calx/sql/71_kan_self_syzygy.sql +70 -0
- calx/sql/72_cert_self_syzygy.sql +35 -0
- calx/sql/73_kan_self_shadow.sql +83 -0
- calx/sql/74_cert_self_shadow.sql +35 -0
- calx/sql/75_kan_f1_radix.sql +86 -0
- calx/sql/76_cert_f1_radix.sql +36 -0
- calx/sql/77_kan_moonshine.sql +88 -0
- calx/sql/78_cert_moonshine.sql +38 -0
- calx/sql/79_cert_kan_engines.sql +86 -0
- calx/sql/80_kan_colimit_closure.sql +67 -0
- calx/sql/81_cert_colimit_closure.sql +44 -0
- calx/sql/82_kan_equipment.sql +75 -0
- calx/sql/83_cert_equipment.sql +38 -0
- calx/sql/84_cert_witness.sql +56 -0
- calx/sql/85_cert_derivation.sql +66 -0
- calx/sql/86_cert_verify.sql +74 -0
- calx/sql/87_cert_export_bundle.sql +85 -0
- calx/sql/88_cert_witness_carry.sql +141 -0
- calx/sql/examples/crt_examples.sql +66 -0
- calx/sql/examples/dynamics_examples.sql +77 -0
- calx/sql/examples/queries.sql +91 -0
- calx/validate.py +70 -0
- trunkit-0.1.0.data/data/share/trunkit/proofs/bigrading.py +248 -0
- trunkit-0.1.0.data/data/share/trunkit/proofs/chromatic.py +254 -0
- trunkit-0.1.0.data/data/share/trunkit/proofs/colimit_closure.py +242 -0
- trunkit-0.1.0.data/data/share/trunkit/proofs/combined_scale.py +346 -0
- trunkit-0.1.0.data/data/share/trunkit/proofs/combined_signature.py +272 -0
- trunkit-0.1.0.data/data/share/trunkit/proofs/developed_sequence.py +433 -0
- trunkit-0.1.0.data/data/share/trunkit/proofs/equipment.py +262 -0
- trunkit-0.1.0.data/data/share/trunkit/proofs/f1_radix.py +181 -0
- trunkit-0.1.0.data/data/share/trunkit/proofs/factorial_homology_signature.py +261 -0
- trunkit-0.1.0.data/data/share/trunkit/proofs/gap_homology_primes.py +150 -0
- trunkit-0.1.0.data/data/share/trunkit/proofs/grading.py +237 -0
- trunkit-0.1.0.data/data/share/trunkit/proofs/identity_decomposition.py +235 -0
- trunkit-0.1.0.data/data/share/trunkit/proofs/lithon.py +190 -0
- trunkit-0.1.0.data/data/share/trunkit/proofs/moonshine.py +192 -0
- trunkit-0.1.0.data/data/share/trunkit/proofs/omega_family.py +215 -0
- trunkit-0.1.0.data/data/share/trunkit/proofs/omega_family_succ.py +269 -0
- trunkit-0.1.0.data/data/share/trunkit/proofs/perfect_28.py +30 -0
- trunkit-0.1.0.data/data/share/trunkit/proofs/prime_members_functor.py +188 -0
- trunkit-0.1.0.data/data/share/trunkit/proofs/self_shadow.py +176 -0
- trunkit-0.1.0.data/data/share/trunkit/proofs/self_syzygy.py +162 -0
- trunkit-0.1.0.data/data/share/trunkit/proofs/seq_homology_signature.py +200 -0
- trunkit-0.1.0.data/data/share/trunkit/proofs/shadow.py +185 -0
- trunkit-0.1.0.data/data/share/trunkit/proofs/shared_prime_h2.py +220 -0
- trunkit-0.1.0.data/data/share/trunkit/proofs/strata_tower.py +219 -0
- trunkit-0.1.0.data/data/share/trunkit/tools/build_bigrading.py +178 -0
- trunkit-0.1.0.data/data/share/trunkit/tools/build_chromatic.py +195 -0
- trunkit-0.1.0.data/data/share/trunkit/tools/build_colimit_closure.py +194 -0
- trunkit-0.1.0.data/data/share/trunkit/tools/build_equipment.py +204 -0
- trunkit-0.1.0.data/data/share/trunkit/tools/build_f1_radix.py +148 -0
- trunkit-0.1.0.data/data/share/trunkit/tools/build_grading.py +182 -0
- trunkit-0.1.0.data/data/share/trunkit/tools/build_identity_decomposition.py +178 -0
- trunkit-0.1.0.data/data/share/trunkit/tools/build_lithon.py +223 -0
- trunkit-0.1.0.data/data/share/trunkit/tools/build_moonshine.py +222 -0
- trunkit-0.1.0.data/data/share/trunkit/tools/build_self_shadow.py +144 -0
- trunkit-0.1.0.data/data/share/trunkit/tools/build_self_syzygy.py +124 -0
- trunkit-0.1.0.data/data/share/trunkit/tools/build_shadow.py +170 -0
- trunkit-0.1.0.data/data/share/trunkit/tools/build_strata_tower.py +163 -0
- trunkit-0.1.0.data/data/share/trunkit/tools/cert_formal.py +312 -0
- trunkit-0.1.0.data/data/share/trunkit/tools/compose_match.py +476 -0
- trunkit-0.1.0.data/data/share/trunkit/tools/cryptanalysis.py +341 -0
- trunkit-0.1.0.data/data/share/trunkit/tools/develop_omega_family.py +255 -0
- trunkit-0.1.0.data/data/share/trunkit/tools/develop_sequence.py +191 -0
- trunkit-0.1.0.data/data/share/trunkit/tools/diagnostic_tests.py +353 -0
- trunkit-0.1.0.data/data/share/trunkit/tools/factorial_homology.py +243 -0
- trunkit-0.1.0.data/data/share/trunkit/tools/kan_in_kan.py +300 -0
- trunkit-0.1.0.data/data/share/trunkit/tools/load_kan_corpus.py +153 -0
- trunkit-0.1.0.data/data/share/trunkit/tools/oeis_loader.py +422 -0
- trunkit-0.1.0.data/data/share/trunkit/tools/oeis_match.py +647 -0
- trunkit-0.1.0.data/data/share/trunkit/tools/omega_equal_control.py +155 -0
- trunkit-0.1.0.data/data/share/trunkit/tools/port_curry_sqlite_to_pg.py +152 -0
- trunkit-0.1.0.data/data/share/trunkit/tools/prime_members_functor.py +125 -0
- trunkit-0.1.0.data/data/share/trunkit/tools/register_calx_fn_deps.py +116 -0
- trunkit-0.1.0.data/data/share/trunkit/tools/register_calx_in_curry.py +424 -0
- trunkit-0.1.0.data/data/share/trunkit/tools/relationship_report.py +372 -0
- trunkit-0.1.0.data/data/share/trunkit/tools/run_compose_discovery.py +102 -0
- trunkit-0.1.0.data/data/share/trunkit/tools/run_discovery.py +115 -0
- trunkit-0.1.0.data/data/share/trunkit/tools/seed_oeis_classics.py +239 -0
- trunkit-0.1.0.data/data/share/trunkit/tools/seed_sequences.py +321 -0
- trunkit-0.1.0.data/data/share/trunkit/tools/seq_homology.py +188 -0
- trunkit-0.1.0.data/data/share/trunkit/tools/shared_prime_betti.py +226 -0
- trunkit-0.1.0.data/data/share/trunkit/tools/smoke_curry_calx.py +114 -0
- trunkit-0.1.0.dist-info/METADATA +195 -0
- trunkit-0.1.0.dist-info/RECORD +144 -0
- trunkit-0.1.0.dist-info/WHEEL +4 -0
- trunkit-0.1.0.dist-info/entry_points.txt +2 -0
- trunkit-0.1.0.dist-info/licenses/LICENSE +21 -0
calx/__init__.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""calx: a computational blackboard for the integers, on PostgreSQL."""
|
|
2
|
+
|
|
3
|
+
import sysconfig
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
__version__ = "0.1.0"
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def get_shared_data_dir(name: str) -> Path:
|
|
10
|
+
data_path = Path(sysconfig.get_path("data")) / "share" / "trunkit" / name
|
|
11
|
+
if data_path.exists():
|
|
12
|
+
return data_path
|
|
13
|
+
return Path(__file__).resolve().parents[2] / name
|
calx/cli.py
ADDED
|
@@ -0,0 +1,400 @@
|
|
|
1
|
+
"""trunkit command-line entry point.
|
|
2
|
+
|
|
3
|
+
Consumer commands (read-only, safe for LLM use):
|
|
4
|
+
trunkit verify <claim_id>
|
|
5
|
+
trunkit standing [--method M] [--status S]
|
|
6
|
+
trunkit export <id> [<id> ...]
|
|
7
|
+
|
|
8
|
+
Prover commands (require --write to record; dry-run otherwise):
|
|
9
|
+
trunkit check <claim_id> [--write]
|
|
10
|
+
trunkit attest [--write]
|
|
11
|
+
trunkit close [--write]
|
|
12
|
+
trunkit witness <claim_id> --kind KIND --body JSON [--write]
|
|
13
|
+
|
|
14
|
+
calx data commands:
|
|
15
|
+
trunkit init
|
|
16
|
+
trunkit generate --limit N [--backend primesieve|pure]
|
|
17
|
+
trunkit validate [--limit N]
|
|
18
|
+
trunkit reset
|
|
19
|
+
trunkit oeis-load [--family F] [--seq-id A000000] [--backfill-only]
|
|
20
|
+
trunkit oeis-match [--orbit-id ID | --all] [--min-length N] [--prefix N]
|
|
21
|
+
trunkit compose-match [--static-only] [--orbit-only] [--no-oeis] [--prefix N]
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
from __future__ import annotations
|
|
25
|
+
|
|
26
|
+
import argparse
|
|
27
|
+
import json
|
|
28
|
+
import sys
|
|
29
|
+
|
|
30
|
+
from . import db, generate, validate
|
|
31
|
+
|
|
32
|
+
# ---------------------------------------------------------------------------
|
|
33
|
+
# Consumer commands — read-only, no --write gate
|
|
34
|
+
# ---------------------------------------------------------------------------
|
|
35
|
+
|
|
36
|
+
def _cmd_verify(args: argparse.Namespace) -> int:
|
|
37
|
+
with db.connect(args.dsn) as conn, conn.cursor() as cur:
|
|
38
|
+
cur.execute(
|
|
39
|
+
"SELECT ok, evidence, witness FROM cert.verify(%s)",
|
|
40
|
+
(args.claim_id,),
|
|
41
|
+
)
|
|
42
|
+
row = cur.fetchone()
|
|
43
|
+
if row is None:
|
|
44
|
+
print(f" claim {args.claim_id} not found")
|
|
45
|
+
return 1
|
|
46
|
+
ok, evidence, witness = row
|
|
47
|
+
status = "VALID" if ok is True else "REFUTED" if ok is False else "UNVERIFIED"
|
|
48
|
+
mark = "✓" if ok is True else "✗" if ok is False else "?"
|
|
49
|
+
print(f" [{mark}] claim {args.claim_id} → {status}")
|
|
50
|
+
if evidence:
|
|
51
|
+
print(f" evidence : {json.dumps(evidence, indent=4)}")
|
|
52
|
+
if witness:
|
|
53
|
+
print(f" witness : {json.dumps(witness, indent=4)}")
|
|
54
|
+
return 0 if ok is True else 1
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _cmd_standing(args: argparse.Namespace) -> int:
|
|
58
|
+
where, params = [], []
|
|
59
|
+
if args.method:
|
|
60
|
+
where.append("method = %s")
|
|
61
|
+
params.append(args.method)
|
|
62
|
+
if args.status:
|
|
63
|
+
where.append("status = %s")
|
|
64
|
+
params.append(args.status)
|
|
65
|
+
sql = "SELECT id, statement, method, status, checked_at FROM cert.standing"
|
|
66
|
+
if where:
|
|
67
|
+
sql += " WHERE " + " AND ".join(where)
|
|
68
|
+
sql += " ORDER BY id"
|
|
69
|
+
with db.connect(args.dsn) as conn, conn.cursor() as cur:
|
|
70
|
+
cur.execute(sql, params)
|
|
71
|
+
rows = cur.fetchall()
|
|
72
|
+
if not rows:
|
|
73
|
+
print(" (no claims match)")
|
|
74
|
+
return 0
|
|
75
|
+
for id_, stmt, method, status, checked_at in rows:
|
|
76
|
+
mark = "✓" if status == "valid" else "✗" if status == "refuted" else "?"
|
|
77
|
+
ts = checked_at.strftime("%Y-%m-%d %H:%M") if checked_at else "—"
|
|
78
|
+
print(f" [{mark}] #{id_:>3} {method:<20} {status:<12} {ts} {stmt[:55]}")
|
|
79
|
+
return 0
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _cmd_export(args: argparse.Namespace) -> int:
|
|
83
|
+
with db.connect(args.dsn) as conn, conn.cursor() as cur:
|
|
84
|
+
cur.execute(
|
|
85
|
+
"SELECT cert.export_bundle(%s::bigint[])",
|
|
86
|
+
(list(args.claim_ids),),
|
|
87
|
+
)
|
|
88
|
+
bundle = cur.fetchone()[0]
|
|
89
|
+
print(json.dumps(bundle, indent=2, default=str))
|
|
90
|
+
return 0
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
# ---------------------------------------------------------------------------
|
|
94
|
+
# Prover commands — require --write to record; dry-run otherwise
|
|
95
|
+
# ---------------------------------------------------------------------------
|
|
96
|
+
|
|
97
|
+
def _cmd_check(args: argparse.Namespace) -> int:
|
|
98
|
+
if not args.write:
|
|
99
|
+
with db.connect(args.dsn) as conn, conn.cursor() as cur:
|
|
100
|
+
cur.execute(
|
|
101
|
+
"SELECT ok, evidence FROM cert.verify(%s)",
|
|
102
|
+
(args.claim_id,),
|
|
103
|
+
)
|
|
104
|
+
row = cur.fetchone()
|
|
105
|
+
if row is None:
|
|
106
|
+
print(f" claim {args.claim_id} not found")
|
|
107
|
+
return 1
|
|
108
|
+
ok, evidence = row
|
|
109
|
+
status = "valid" if ok is True else "refuted" if ok is False else "unverified"
|
|
110
|
+
print(f" [dry-run] claim {args.claim_id} would attest as: {status}")
|
|
111
|
+
print(" pass --write to record a certificate")
|
|
112
|
+
return 0
|
|
113
|
+
|
|
114
|
+
with db.connect(args.dsn) as conn, conn.cursor() as cur:
|
|
115
|
+
cur.execute(
|
|
116
|
+
"SELECT method FROM cert.claim WHERE id = %s",
|
|
117
|
+
(args.claim_id,),
|
|
118
|
+
)
|
|
119
|
+
row = cur.fetchone()
|
|
120
|
+
if row is None:
|
|
121
|
+
print(f" claim {args.claim_id} not found")
|
|
122
|
+
return 1
|
|
123
|
+
method = row[0]
|
|
124
|
+
fn = "cert.check_with_witness" if method == "witness_carry" else "cert.check"
|
|
125
|
+
cur.execute(f"SELECT {fn}(%s)", (args.claim_id,))
|
|
126
|
+
cur.execute(
|
|
127
|
+
"SELECT status, seq FROM cert.certificate "
|
|
128
|
+
"WHERE claim_id = %s ORDER BY seq DESC LIMIT 1",
|
|
129
|
+
(args.claim_id,),
|
|
130
|
+
)
|
|
131
|
+
status, seq = cur.fetchone()
|
|
132
|
+
|
|
133
|
+
mark = "✓" if status == "valid" else "✗" if status == "refuted" else "?"
|
|
134
|
+
print(f" [{mark}] claim {args.claim_id} → {status} (cert seq={seq})")
|
|
135
|
+
return 0 if status == "valid" else 1
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def _cmd_attest(args: argparse.Namespace) -> int:
|
|
139
|
+
if not args.write:
|
|
140
|
+
print(" [dry-run] would run tools/cert_formal.py against all formal-tier claims")
|
|
141
|
+
print(" pass --write to execute and record certificates")
|
|
142
|
+
return 0
|
|
143
|
+
mod = _load_tools_module("cert_formal", "cert_formal.py")
|
|
144
|
+
mod.main()
|
|
145
|
+
return 0
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def _cmd_close(args: argparse.Namespace) -> int:
|
|
149
|
+
if not args.write:
|
|
150
|
+
print(" [dry-run] would compute reflexive closure")
|
|
151
|
+
print(" curry fixed points + kan Perron-Frobenius attractor")
|
|
152
|
+
print(" pass --write to execute and record eigenform claims")
|
|
153
|
+
return 0
|
|
154
|
+
mod = _load_tools_module("kan_in_kan", "kan_in_kan.py")
|
|
155
|
+
mod.main()
|
|
156
|
+
return 0
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def _cmd_witness(args: argparse.Namespace) -> int:
|
|
160
|
+
try:
|
|
161
|
+
body = json.loads(args.body)
|
|
162
|
+
except json.JSONDecodeError as exc:
|
|
163
|
+
print(f" error: --body is not valid JSON: {exc}")
|
|
164
|
+
return 2
|
|
165
|
+
|
|
166
|
+
if not args.write:
|
|
167
|
+
print(f" [dry-run] would attach witness to claim {args.claim_id}")
|
|
168
|
+
print(f" kind : {args.kind}")
|
|
169
|
+
print(f" body : {json.dumps(body, indent=4)}")
|
|
170
|
+
print(" pass --write to record")
|
|
171
|
+
return 0
|
|
172
|
+
|
|
173
|
+
with db.connect(args.dsn) as conn, conn.cursor() as cur:
|
|
174
|
+
cur.execute(
|
|
175
|
+
"SELECT cert.attach_witness(%s, %s, %s::jsonb)",
|
|
176
|
+
(args.claim_id, args.kind, json.dumps(body)),
|
|
177
|
+
)
|
|
178
|
+
wit_id = cur.fetchone()[0]
|
|
179
|
+
print(f" witness {wit_id} attached to claim {args.claim_id}")
|
|
180
|
+
return 0
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
# ---------------------------------------------------------------------------
|
|
184
|
+
# calx data commands (unchanged)
|
|
185
|
+
# ---------------------------------------------------------------------------
|
|
186
|
+
|
|
187
|
+
def _cmd_init(args: argparse.Namespace) -> int:
|
|
188
|
+
with db.connect(args.dsn) as conn:
|
|
189
|
+
db.apply_schema(conn)
|
|
190
|
+
print("schema applied")
|
|
191
|
+
return 0
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def _cmd_generate(args: argparse.Namespace) -> int:
|
|
195
|
+
with db.connect(args.dsn) as conn:
|
|
196
|
+
db.apply_schema(conn)
|
|
197
|
+
if args.backend == "pure":
|
|
198
|
+
generate.generate_pure(conn, args.limit)
|
|
199
|
+
else:
|
|
200
|
+
generate.generate_with_primesieve(conn, args.limit)
|
|
201
|
+
return 0
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def _cmd_validate(args: argparse.Namespace) -> int:
|
|
205
|
+
with db.connect(args.dsn) as conn:
|
|
206
|
+
results = [
|
|
207
|
+
validate.check_omega(conn, args.limit),
|
|
208
|
+
validate.check_big_omega(conn, args.limit),
|
|
209
|
+
]
|
|
210
|
+
bad = [r for r in results if not r.ok]
|
|
211
|
+
for r in results:
|
|
212
|
+
status = "OK " if r.ok else "FAIL"
|
|
213
|
+
print(f" [{status}] {r.sequence} checked={r.checked} mismatches={len(r.mismatches)}")
|
|
214
|
+
return 0 if not bad else 1
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def _cmd_reset(args: argparse.Namespace) -> int:
|
|
218
|
+
with db.connect(args.dsn) as conn, conn.cursor() as cur:
|
|
219
|
+
cur.execute("DROP TABLE IF EXISTS factorizations, primes, integers CASCADE")
|
|
220
|
+
print("dropped factorizations, primes, integers")
|
|
221
|
+
return 0
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
def _cmd_oeis_load(args: argparse.Namespace) -> int:
|
|
225
|
+
mod = _load_tools_module("oeis_loader", "oeis_loader.py")
|
|
226
|
+
with db.connect(args.dsn) as conn:
|
|
227
|
+
with conn.cursor() as cur:
|
|
228
|
+
cur.execute("SELECT MAX(n) FROM integers")
|
|
229
|
+
lim = cur.fetchone()[0]
|
|
230
|
+
if not lim:
|
|
231
|
+
print("error: no integers populated; run `trunkit generate` first")
|
|
232
|
+
return 1
|
|
233
|
+
print(f"DB range: 1..{lim:,}")
|
|
234
|
+
updated = mod.backfill_local_families(conn)
|
|
235
|
+
print(f"backfilled family on {updated} existing sequence rows")
|
|
236
|
+
if args.backfill_only:
|
|
237
|
+
return 0
|
|
238
|
+
mod.fetch_whitelist(conn, lim, only_family=args.family, only_seq=args.seq_id)
|
|
239
|
+
return 0
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def _cmd_oeis_match(args: argparse.Namespace) -> int:
|
|
243
|
+
if not args.orbit_id and not args.all:
|
|
244
|
+
print("error: specify --orbit-id ID or --all")
|
|
245
|
+
return 2
|
|
246
|
+
mod = _load_tools_module("oeis_match", "oeis_match.py")
|
|
247
|
+
with db.connect(args.dsn) as conn:
|
|
248
|
+
db.apply_schema(conn)
|
|
249
|
+
sync = not args.no_sync_membership
|
|
250
|
+
if args.orbit_id:
|
|
251
|
+
hits = mod.search_orbit(conn, args.orbit_id, args.prefix, sync_membership=sync)
|
|
252
|
+
for m in hits:
|
|
253
|
+
print(
|
|
254
|
+
f" #{m.candidate_id} {m.oeis_id} [{m.match_kind}] "
|
|
255
|
+
f"conf={m.confidence:.3f} prefix={m.prefix_len} — {m.oeis_name[:72]}"
|
|
256
|
+
)
|
|
257
|
+
if not hits:
|
|
258
|
+
print(f" orbit {args.orbit_id}: no OEIS candidates stored")
|
|
259
|
+
else:
|
|
260
|
+
n = mod.search_all_orbits(conn, args.min_length, args.prefix, sync_membership=sync)
|
|
261
|
+
print(f"searched {n} orbits (min_length={args.min_length}, prefix={args.prefix})")
|
|
262
|
+
return 0
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
def _cmd_compose_match(args: argparse.Namespace) -> int:
|
|
266
|
+
mod = _load_tools_module("compose_match", "compose_match.py")
|
|
267
|
+
with db.connect(args.dsn) as conn:
|
|
268
|
+
stats = mod.run_compose_pass(
|
|
269
|
+
conn,
|
|
270
|
+
static=not args.orbit_only,
|
|
271
|
+
orbits=not args.static_only,
|
|
272
|
+
search_oeis=not args.no_oeis,
|
|
273
|
+
prefix_len=args.prefix,
|
|
274
|
+
)
|
|
275
|
+
print(
|
|
276
|
+
f"run {stats['run_id']}: catalog={stats['catalog_size']} "
|
|
277
|
+
f"composed={stats['composed']} (static={stats['static_specs']} "
|
|
278
|
+
f"orbit={stats['orbit_specs']}) searched={stats['searched']} "
|
|
279
|
+
f"identifications={stats['identifications']}"
|
|
280
|
+
)
|
|
281
|
+
return 0
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
# ---------------------------------------------------------------------------
|
|
285
|
+
# Parser
|
|
286
|
+
# ---------------------------------------------------------------------------
|
|
287
|
+
|
|
288
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
289
|
+
p = argparse.ArgumentParser(
|
|
290
|
+
prog="trunkit",
|
|
291
|
+
description="Proof-carrying code middleware on PostgreSQL.",
|
|
292
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
293
|
+
epilog=(
|
|
294
|
+
"Consumer commands are read-only and safe for LLM use.\n"
|
|
295
|
+
"Prover commands require --write to record; they dry-run without it."
|
|
296
|
+
),
|
|
297
|
+
)
|
|
298
|
+
p.add_argument("--dsn", help="PostgreSQL DSN (overrides $CALX_DSN / $TRUNK_DSN)")
|
|
299
|
+
|
|
300
|
+
sub = p.add_subparsers(dest="command", required=True)
|
|
301
|
+
|
|
302
|
+
# --- consumer ---
|
|
303
|
+
v = sub.add_parser("verify", help="side-effect-free re-verification of a claim")
|
|
304
|
+
v.add_argument("claim_id", type=int)
|
|
305
|
+
v.set_defaults(func=_cmd_verify)
|
|
306
|
+
|
|
307
|
+
st = sub.add_parser("standing", help="latest attestation status for all claims")
|
|
308
|
+
st.add_argument("--method", help="filter by method (comp_sql, struct_kan, …)")
|
|
309
|
+
st.add_argument("--status", help="filter by status (valid, refuted, unverified)")
|
|
310
|
+
st.set_defaults(func=_cmd_standing)
|
|
311
|
+
|
|
312
|
+
ex = sub.add_parser("export", help="export portable proof bundle to stdout (JSONB)")
|
|
313
|
+
ex.add_argument("claim_ids", type=int, nargs="+", metavar="claim_id")
|
|
314
|
+
ex.set_defaults(func=_cmd_export)
|
|
315
|
+
|
|
316
|
+
# --- prover ---
|
|
317
|
+
ck = sub.add_parser("check", help="attest a claim and record a certificate")
|
|
318
|
+
ck.add_argument("claim_id", type=int)
|
|
319
|
+
ck.add_argument("--write", action="store_true", help="record the certificate (dry-run without)")
|
|
320
|
+
ck.set_defaults(func=_cmd_check)
|
|
321
|
+
|
|
322
|
+
at = sub.add_parser("attest", help="run formal-tier artifact attestation")
|
|
323
|
+
at.add_argument("--write", action="store_true", help="record certificates (dry-run without)")
|
|
324
|
+
at.set_defaults(func=_cmd_attest)
|
|
325
|
+
|
|
326
|
+
cl = sub.add_parser("close", help="reflexive closure — curry fixed points + kan eigenform")
|
|
327
|
+
cl.add_argument("--write", action="store_true",
|
|
328
|
+
help="record eigenform claims (dry-run without)")
|
|
329
|
+
cl.set_defaults(func=_cmd_close)
|
|
330
|
+
|
|
331
|
+
wi = sub.add_parser("witness", help="attach a structured proof witness to a claim")
|
|
332
|
+
wi.add_argument("claim_id", type=int)
|
|
333
|
+
wi.add_argument("--kind", required=True,
|
|
334
|
+
choices=["term", "trace", "counterexample", "hash_chain", "kan_diagram"])
|
|
335
|
+
wi.add_argument("--body", required=True, help="witness body as JSON string")
|
|
336
|
+
wi.add_argument("--write", action="store_true", help="record the witness (dry-run without)")
|
|
337
|
+
wi.set_defaults(func=_cmd_witness)
|
|
338
|
+
|
|
339
|
+
# --- calx data ---
|
|
340
|
+
sub.add_parser("init", help="apply schema/views/procedures").set_defaults(func=_cmd_init)
|
|
341
|
+
|
|
342
|
+
g = sub.add_parser("generate", help="populate integer tables up to --limit")
|
|
343
|
+
g.add_argument("--limit", type=int, required=True)
|
|
344
|
+
g.add_argument("--backend", choices=("primesieve", "pure"), default="primesieve")
|
|
345
|
+
g.set_defaults(func=_cmd_generate)
|
|
346
|
+
|
|
347
|
+
vl = sub.add_parser("validate", help="compare derived columns against OEIS")
|
|
348
|
+
vl.add_argument("--limit", type=int, default=100_000)
|
|
349
|
+
vl.set_defaults(func=_cmd_validate)
|
|
350
|
+
|
|
351
|
+
sub.add_parser("reset", help="drop all calx tables").set_defaults(func=_cmd_reset)
|
|
352
|
+
|
|
353
|
+
ol = sub.add_parser("oeis-load", help="fetch curated OEIS b-files")
|
|
354
|
+
ol.add_argument("--family")
|
|
355
|
+
ol.add_argument("--seq-id")
|
|
356
|
+
ol.add_argument("--backfill-only", action="store_true")
|
|
357
|
+
ol.set_defaults(func=_cmd_oeis_load)
|
|
358
|
+
|
|
359
|
+
om = sub.add_parser("oeis-match", help="match orbit prefixes against OEIS")
|
|
360
|
+
om.add_argument("--orbit-id", type=int)
|
|
361
|
+
om.add_argument("--all", action="store_true")
|
|
362
|
+
om.add_argument("--min-length", type=int, default=4)
|
|
363
|
+
om.add_argument("--prefix", type=int, default=8)
|
|
364
|
+
om.add_argument("--no-sync-membership", action="store_true")
|
|
365
|
+
om.set_defaults(func=_cmd_oeis_match)
|
|
366
|
+
|
|
367
|
+
cm = sub.add_parser("compose-match", help="Tier 3 compose_index + OEIS search")
|
|
368
|
+
cm.add_argument("--static-only", action="store_true")
|
|
369
|
+
cm.add_argument("--orbit-only", action="store_true")
|
|
370
|
+
cm.add_argument("--no-oeis", action="store_true")
|
|
371
|
+
cm.add_argument("--prefix", type=int, default=8)
|
|
372
|
+
cm.set_defaults(func=_cmd_compose_match)
|
|
373
|
+
|
|
374
|
+
return p
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
# ---------------------------------------------------------------------------
|
|
378
|
+
# Helpers
|
|
379
|
+
# ---------------------------------------------------------------------------
|
|
380
|
+
|
|
381
|
+
def _load_tools_module(module_name: str, filename: str):
|
|
382
|
+
import importlib.util
|
|
383
|
+
|
|
384
|
+
from calx import get_shared_data_dir
|
|
385
|
+
|
|
386
|
+
path = get_shared_data_dir("tools") / filename
|
|
387
|
+
spec = importlib.util.spec_from_file_location(module_name, path)
|
|
388
|
+
mod = importlib.util.module_from_spec(spec)
|
|
389
|
+
sys.modules[module_name] = mod
|
|
390
|
+
spec.loader.exec_module(mod)
|
|
391
|
+
return mod
|
|
392
|
+
|
|
393
|
+
|
|
394
|
+
def main(argv: list[str] | None = None) -> int:
|
|
395
|
+
args = build_parser().parse_args(argv)
|
|
396
|
+
return args.func(args)
|
|
397
|
+
|
|
398
|
+
|
|
399
|
+
if __name__ == "__main__":
|
|
400
|
+
sys.exit(main())
|
calx/curry_adapter.py
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"""Curry adapter for calx.
|
|
2
|
+
|
|
3
|
+
Each calx SQL procedure / function is declared as a Curry function whose body
|
|
4
|
+
returns a "call plan" dict:
|
|
5
|
+
|
|
6
|
+
{
|
|
7
|
+
"sql": "function" | "procedure",
|
|
8
|
+
"proc": "<sql_identifier>",
|
|
9
|
+
"args": [...],
|
|
10
|
+
"returns": "scalar" | "rows" | "none",
|
|
11
|
+
"schema_version": <bound calx_schema_version constant>,
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
This module reads the plan via ``CurrySession.call_function`` and dispatches
|
|
15
|
+
the actual SQL execution against PostgreSQL through psycopg. The Curry layer
|
|
16
|
+
owns versioning, dependency binding, expected_args, and per-argument
|
|
17
|
+
descriptions; psycopg handles parameter binding and result shaping.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
import importlib.util
|
|
23
|
+
import json
|
|
24
|
+
import re
|
|
25
|
+
import sys
|
|
26
|
+
from collections.abc import Iterator
|
|
27
|
+
from contextlib import contextmanager
|
|
28
|
+
from pathlib import Path
|
|
29
|
+
from typing import Any
|
|
30
|
+
|
|
31
|
+
_IDENT_RE = re.compile(r"^[A-Za-z_][A-Za-z0-9_]*$")
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _curry_project_dir() -> Path:
|
|
35
|
+
"""Return the calx project root (the directory containing ``.curry``)."""
|
|
36
|
+
return Path(__file__).resolve().parents[2]
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _load_curry_core():
|
|
40
|
+
"""Load the standalone ``curry_core`` module from the path in config.json.
|
|
41
|
+
|
|
42
|
+
Curry is not on the Python import path; the config file declares where its
|
|
43
|
+
sources live so we can import without installing.
|
|
44
|
+
"""
|
|
45
|
+
project_dir = _curry_project_dir()
|
|
46
|
+
config_path = project_dir / ".curry" / "config.json"
|
|
47
|
+
with open(config_path, encoding="utf-8") as fh:
|
|
48
|
+
config = json.load(fh)
|
|
49
|
+
|
|
50
|
+
curry_path = config.get("curry_module_path")
|
|
51
|
+
if not curry_path:
|
|
52
|
+
raise RuntimeError(
|
|
53
|
+
f"{config_path} is missing 'curry_module_path' — cannot locate curry_core.py"
|
|
54
|
+
)
|
|
55
|
+
if "curry_core" in sys.modules:
|
|
56
|
+
return sys.modules["curry_core"]
|
|
57
|
+
|
|
58
|
+
src = Path(curry_path) / "curry_core.py"
|
|
59
|
+
if not src.is_file():
|
|
60
|
+
raise FileNotFoundError(f"curry_core.py not found at {src}")
|
|
61
|
+
spec = importlib.util.spec_from_file_location("curry_core", src)
|
|
62
|
+
module = importlib.util.module_from_spec(spec)
|
|
63
|
+
sys.modules["curry_core"] = module
|
|
64
|
+
assert spec.loader is not None
|
|
65
|
+
spec.loader.exec_module(module)
|
|
66
|
+
return module
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@contextmanager
|
|
70
|
+
def curry_session() -> Iterator[Any]:
|
|
71
|
+
"""Open a CurrySession scoped to the calx project root."""
|
|
72
|
+
curry_core = _load_curry_core()
|
|
73
|
+
project_dir = str(_curry_project_dir())
|
|
74
|
+
session = curry_core.CurrySession.from_project(project_dir)
|
|
75
|
+
try:
|
|
76
|
+
yield session
|
|
77
|
+
finally:
|
|
78
|
+
session.close()
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _safe_ident(name: str) -> str:
|
|
82
|
+
if not _IDENT_RE.match(name):
|
|
83
|
+
raise ValueError(f"unsafe SQL identifier from Curry plan: {name!r}")
|
|
84
|
+
return name
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def execute_plan(conn, plan: dict[str, Any]) -> Any:
|
|
88
|
+
"""Execute a call plan returned by a Curry function body against ``conn``.
|
|
89
|
+
|
|
90
|
+
Procedure / function names are validated against an identifier regex
|
|
91
|
+
before being interpolated, because psycopg cannot parameterize identifiers.
|
|
92
|
+
Argument values are always passed as parameters.
|
|
93
|
+
"""
|
|
94
|
+
sql_kind = plan["sql"]
|
|
95
|
+
proc = _safe_ident(plan["proc"])
|
|
96
|
+
args = list(plan.get("args", []))
|
|
97
|
+
returns = plan.get("returns", "scalar")
|
|
98
|
+
placeholders = ", ".join(["%s"] * len(args))
|
|
99
|
+
|
|
100
|
+
with conn.cursor() as cur:
|
|
101
|
+
if sql_kind == "function":
|
|
102
|
+
cur.execute(f"SELECT * FROM {proc}({placeholders})", args)
|
|
103
|
+
if returns == "scalar":
|
|
104
|
+
row = cur.fetchone()
|
|
105
|
+
return None if row is None else row[0]
|
|
106
|
+
if returns == "rows":
|
|
107
|
+
cols = [d.name for d in cur.description]
|
|
108
|
+
return [dict(zip(cols, r, strict=True)) for r in cur.fetchall()]
|
|
109
|
+
raise ValueError(f"unsupported 'returns' for function plan: {returns!r}")
|
|
110
|
+
if sql_kind == "procedure":
|
|
111
|
+
cur.execute(f"CALL {proc}({placeholders})", args)
|
|
112
|
+
return None
|
|
113
|
+
raise ValueError(f"unknown 'sql' kind in plan: {sql_kind!r}")
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def run(
|
|
117
|
+
name: str,
|
|
118
|
+
version: int,
|
|
119
|
+
args: dict[str, Any],
|
|
120
|
+
*,
|
|
121
|
+
dsn: str | None = None,
|
|
122
|
+
) -> Any:
|
|
123
|
+
"""End-to-end: open Curry session, resolve plan, execute against Postgres."""
|
|
124
|
+
from . import db # lazy: psycopg is only required when actually executing
|
|
125
|
+
with curry_session() as session:
|
|
126
|
+
plan = session.call_function(name, version, args)
|
|
127
|
+
with db.connect(dsn) as conn:
|
|
128
|
+
return execute_plan(conn, plan)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def plan_only(name: str, version: int, args: dict[str, Any]) -> dict[str, Any]:
|
|
132
|
+
"""Resolve the call plan without executing — useful for tests / inspection."""
|
|
133
|
+
with curry_session() as session:
|
|
134
|
+
return session.call_function(name, version, args)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
__all__ = [
|
|
138
|
+
"curry_session",
|
|
139
|
+
"execute_plan",
|
|
140
|
+
"plan_only",
|
|
141
|
+
"run",
|
|
142
|
+
]
|