gdk9-cli 0.3.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.
Files changed (82) hide show
  1. examples/01_analyze.py +28 -0
  2. examples/02_profile.py +24 -0
  3. examples/03_assign.py +19 -0
  4. examples/04_dcg.py +41 -0
  5. examples/05_tokenize.py +22 -0
  6. examples/06_synthesize.py +20 -0
  7. examples/07_api.py +57 -0
  8. examples/08_kernel.py +35 -0
  9. examples/09_kernel_search_walkthrough.py +102 -0
  10. examples/10_conserve_vs_naive.py +100 -0
  11. gdk9/__init__.py +5 -0
  12. gdk9/ansi.py +57 -0
  13. gdk9/cli.py +1186 -0
  14. gdk9/crdt.py +88 -0
  15. gdk9/crypto.py +132 -0
  16. gdk9/data/ninefold.json +40 -0
  17. gdk9/data/official.example.json +16 -0
  18. gdk9/data/official.json +132 -0
  19. gdk9/data/subs.json +436 -0
  20. gdk9/dcg.py +356 -0
  21. gdk9/egglog_bridge/__init__.py +6 -0
  22. gdk9/egglog_bridge/dr_egglog.py +107 -0
  23. gdk9/egglog_bridge/fallback.py +34 -0
  24. gdk9/energy.py +211 -0
  25. gdk9/errors.py +15 -0
  26. gdk9/fmt.py +212 -0
  27. gdk9/imply.py +83 -0
  28. gdk9/io_utils.py +24 -0
  29. gdk9/kernel/__init__.py +24 -0
  30. gdk9/kernel/engine.py +123 -0
  31. gdk9/kernel/errors.py +17 -0
  32. gdk9/kernel/expression.py +43 -0
  33. gdk9/kernel/principle.py +43 -0
  34. gdk9/kernel/proof.py +24 -0
  35. gdk9/kernel/rule.py +30 -0
  36. gdk9/kernel/symbol.py +19 -0
  37. gdk9/kernel_cli.py +195 -0
  38. gdk9/log.py +30 -0
  39. gdk9/optimize.py +288 -0
  40. gdk9/parser.py +35 -0
  41. gdk9/plugins/__init__.py +9 -0
  42. gdk9/plugins/loader.py +346 -0
  43. gdk9/plugins/registry.py +42 -0
  44. gdk9/principles.py +131 -0
  45. gdk9/saturate.py +89 -0
  46. gdk9/state.py +138 -0
  47. gdk9/subs.py +108 -0
  48. gdk9/tokenize.py +386 -0
  49. gdk9/tui.py +115 -0
  50. gdk9/utilization.py +15 -0
  51. gdk9_cli-0.3.0.dist-info/METADATA +295 -0
  52. gdk9_cli-0.3.0.dist-info/RECORD +82 -0
  53. gdk9_cli-0.3.0.dist-info/WHEEL +5 -0
  54. gdk9_cli-0.3.0.dist-info/entry_points.txt +3 -0
  55. gdk9_cli-0.3.0.dist-info/licenses/LICENSE +661 -0
  56. gdk9_cli-0.3.0.dist-info/top_level.txt +4 -0
  57. scripts/batch_analyze.py +109 -0
  58. scripts/export_json.py +125 -0
  59. scripts/make_zip.py +57 -0
  60. scripts/profile_compare.py +165 -0
  61. scripts/watch_file.py +124 -0
  62. tests/conftest.py +26 -0
  63. tests/experiment/__init__.py +1 -0
  64. tests/experiment/test_conserve_vs_naive.py +287 -0
  65. tests/kernel/test_kernel_energy.py +18 -0
  66. tests/kernel/test_kernel_rules.py +58 -0
  67. tests/kernel/test_kernel_search_bound.py +34 -0
  68. tests/test_crypto.py +36 -0
  69. tests/test_crypto_secure.py +26 -0
  70. tests/test_dcg.py +318 -0
  71. tests/test_dcg_golden.py +67 -0
  72. tests/test_egglog_bridge.py +41 -0
  73. tests/test_energy.py +34 -0
  74. tests/test_kernel_cli_handbook.py +101 -0
  75. tests/test_optimize.py +21 -0
  76. tests/test_plugins.py +64 -0
  77. tests/test_rules.py +43 -0
  78. tests/test_rules_commit.py +40 -0
  79. tests/test_rules_reversibility.py +50 -0
  80. tests/test_state_crdt.py +87 -0
  81. tests/test_tokenize.py +38 -0
  82. tests/test_tokenize_metrics.py +33 -0
gdk9/crdt.py ADDED
@@ -0,0 +1,88 @@
1
+ from __future__ import annotations
2
+
3
+ import time
4
+ from dataclasses import dataclass
5
+ from typing import Any, Dict, Tuple
6
+
7
+
8
+ @dataclass(frozen=True)
9
+ class LwwEntry:
10
+ value: Any
11
+ timestamp: float
12
+ actor: str
13
+
14
+
15
+ def coerce_entry(raw: Any, default_actor: str = "legacy", default_ts: float = 0.0) -> LwwEntry:
16
+ if isinstance(raw, dict):
17
+ value = raw.get("value", raw)
18
+ try:
19
+ ts = float(raw.get("timestamp", default_ts))
20
+ except (TypeError, ValueError):
21
+ ts = float(default_ts)
22
+ actor_raw = raw.get("actor", default_actor)
23
+ actor = default_actor if actor_raw in {None, ""} else str(actor_raw)
24
+ return LwwEntry(value, ts, actor)
25
+ return LwwEntry(raw, float(default_ts), default_actor)
26
+
27
+
28
+ def lww_merge(a: LwwEntry, b: LwwEntry) -> LwwEntry:
29
+ if b.timestamp > a.timestamp:
30
+ return b
31
+ if b.timestamp < a.timestamp:
32
+ return a
33
+ return b if b.actor >= a.actor else a
34
+
35
+
36
+ def stamp(value: Any, actor: str | None = None, ts: float | None = None) -> Dict[str, Any]:
37
+ return {
38
+ "value": value,
39
+ "timestamp": float(time.time() if ts is None else ts),
40
+ "actor": actor or "anonymous",
41
+ }
42
+
43
+
44
+ def merge_maps(
45
+ left: Dict[str, Any],
46
+ right: Dict[str, Any],
47
+ default_actor: str = "legacy",
48
+ default_ts: float = 0.0,
49
+ ) -> Tuple[Dict[str, Any], Dict[str, int], Dict[str, str]]:
50
+ """Merge two LWW maps.
51
+
52
+ The returned tuple contains the merged map, update statistics, and the winner side for
53
+ each key ("left" or "right"). When inputs omit CRDT metadata, ``default_actor`` and
54
+ ``default_ts`` supply deterministic fallbacks so downstream consumers never see missing
55
+ fields.
56
+ """
57
+ merged: Dict[str, Any] = {}
58
+ stats = {"added": 0, "updated": 0, "unchanged": 0}
59
+ winners: Dict[str, str] = {}
60
+ keys = set(left.keys()) | set(right.keys())
61
+ for key in keys:
62
+ has_left = key in left
63
+ has_right = key in right
64
+ if has_left and not has_right:
65
+ lentry = coerce_entry(left[key], default_actor, default_ts)
66
+ merged[key] = {"value": lentry.value, "timestamp": lentry.timestamp, "actor": lentry.actor}
67
+ winners[key] = "left"
68
+ stats["unchanged"] += 1
69
+ continue
70
+ if has_right and not has_left:
71
+ rentry = coerce_entry(right[key], default_actor, default_ts)
72
+ merged[key] = {"value": rentry.value, "timestamp": rentry.timestamp, "actor": rentry.actor}
73
+ winners[key] = "right"
74
+ stats["added"] += 1
75
+ continue
76
+
77
+ lentry = coerce_entry(left.get(key), default_actor, default_ts)
78
+ rentry = coerce_entry(right.get(key), default_actor, default_ts)
79
+ winner = lww_merge(lentry, rentry)
80
+ merged[key] = {"value": winner.value, "timestamp": winner.timestamp, "actor": winner.actor}
81
+ winners[key] = "left" if winner is lentry else "right"
82
+ if winner is lentry:
83
+ stats["unchanged"] += 1
84
+ elif lentry.value != rentry.value or lentry.timestamp != rentry.timestamp or lentry.actor != rentry.actor:
85
+ stats["updated"] += 1
86
+ else:
87
+ stats["unchanged"] += 1
88
+ return merged, stats, winners
gdk9/crypto.py ADDED
@@ -0,0 +1,132 @@
1
+ from __future__ import annotations
2
+
3
+ """
4
+ Experimental energy-driven cipher (EDPC-9).
5
+
6
+ WARNING: This is not cryptographically secure. It is a reversible
7
+ energy-guided polyalphabetic transform intended for creative/symbolic
8
+ applications, not data security. Do not use for protecting secrets.
9
+ """
10
+
11
+ from typing import List
12
+ import base64
13
+ import os
14
+ from .errors import InputError
15
+
16
+ from .principles import Principle
17
+ from .energy import char_energy
18
+
19
+
20
+ def _keystream(principle: Principle, key: str, length: int) -> List[int]:
21
+ if not key:
22
+ raise ValueError("Key must be non-empty")
23
+ ks = [char_energy(ch, principle) or 1 for ch in key]
24
+ # normalize to 1..9, avoid zeros
25
+ out: List[int] = []
26
+ i = 0
27
+ while len(out) < length:
28
+ out.append(ks[i % len(ks)] or 1)
29
+ i += 1
30
+ return out
31
+
32
+
33
+ def _rotate_letter(ch: str, k: int) -> str:
34
+ if not ch.isalpha():
35
+ return ch
36
+ base = ord('A') if ch.isupper() else ord('a')
37
+ off = (ord(ch) - base + k) % 26
38
+ return chr(base + off)
39
+
40
+
41
+ def _rotate_digit(ch: str, k: int) -> str:
42
+ if not ch.isdigit():
43
+ return ch
44
+ base = ord('0')
45
+ off = (ord(ch) - base + (k % 10)) % 10
46
+ return chr(base + off)
47
+
48
+
49
+ def _rotate_symbol(ch: str, k: int, principle: Principle) -> str:
50
+ # Build a stable ordered list of known symbols; fallback to identity
51
+ symbols = sorted(set(principle.symbol_energy.keys()))
52
+ if ch not in symbols:
53
+ return ch
54
+ idx = symbols.index(ch)
55
+ return symbols[(idx + k) % len(symbols)]
56
+
57
+
58
+ def encrypt(text: str, key: str, principle: Principle) -> str:
59
+ ks = _keystream(principle, key, len(text))
60
+ out = []
61
+ for i, ch in enumerate(text):
62
+ k = ks[i]
63
+ if ch.isalpha():
64
+ out.append(_rotate_letter(ch, k))
65
+ elif ch.isdigit():
66
+ out.append(_rotate_digit(ch, k))
67
+ elif ch.isspace():
68
+ out.append(ch)
69
+ else:
70
+ out.append(_rotate_symbol(ch, k, principle))
71
+ return "".join(out)
72
+
73
+
74
+ def decrypt(cipher: str, key: str, principle: Principle) -> str:
75
+ ks = _keystream(principle, key, len(cipher))
76
+ out = []
77
+ for i, ch in enumerate(cipher):
78
+ k = ks[i]
79
+ if ch.isalpha():
80
+ out.append(_rotate_letter(ch, -k))
81
+ elif ch.isdigit():
82
+ out.append(_rotate_digit(ch, -k))
83
+ elif ch.isspace():
84
+ out.append(ch)
85
+ else:
86
+ out.append(_rotate_symbol(ch, -k, principle))
87
+ return "".join(out)
88
+
89
+
90
+ # Secure mode (optional dependency on 'cryptography')
91
+
92
+ def _derive_key(passphrase: str, salt: bytes) -> bytes:
93
+ if not passphrase:
94
+ raise InputError("Key must be non-empty")
95
+ # 32-byte key via PBKDF2-HMAC-SHA256
96
+ from hashlib import pbkdf2_hmac
97
+ raw = pbkdf2_hmac('sha256', passphrase.encode('utf-8'), salt, 200_000, dklen=32)
98
+ return base64.urlsafe_b64encode(raw)
99
+
100
+
101
+ def encrypt_secure(text: str, key: str) -> str:
102
+ try:
103
+ from cryptography.fernet import Fernet
104
+ except Exception as exc: # pragma: no cover - optional dependency
105
+ raise InputError("Secure mode requires 'cryptography' package. Install it to use --mode secure.") from exc
106
+ salt = os.urandom(16)
107
+ f = Fernet(_derive_key(key, salt))
108
+ token = f.encrypt(text.encode('utf-8'))
109
+ payload = b'G9F' + salt + token
110
+ return base64.urlsafe_b64encode(payload).decode('ascii')
111
+
112
+
113
+ def decrypt_secure(ciphertext: str, key: str) -> str:
114
+ try:
115
+ from cryptography.fernet import Fernet
116
+ except Exception as exc: # pragma: no cover - optional dependency
117
+ raise InputError("Secure mode requires 'cryptography' package. Install it to use --mode secure.") from exc
118
+ try:
119
+ raw = base64.urlsafe_b64decode(ciphertext.encode('ascii'))
120
+ except Exception as exc:
121
+ raise InputError("Invalid secure ciphertext") from exc
122
+ if not raw.startswith(b'G9F') or len(raw) < 19:
123
+ raise InputError("Invalid secure ciphertext")
124
+ salt = raw[3:19]
125
+ token = raw[19:]
126
+ f = Fernet(_derive_key(key, salt))
127
+ try:
128
+ out = f.decrypt(token)
129
+ except Exception as exc:
130
+ raise InputError("Decryption failed; wrong key or corrupted data") from exc
131
+ return out.decode('utf-8')
132
+
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "Ninefold Grid",
3
+ "description": "Default Gdk9 principle: A1Z26, digital-root numbers, curated symbol energies.",
4
+ "letter_mode": "a1z26",
5
+ "number_mode": "digital_root",
6
+ "normalize_zero_to_nine": true,
7
+ "weights": {"letter": 1, "digit": 1, "symbol": 1},
8
+ "harmonics": true,
9
+ "symbol_energy": {
10
+ ".": 1,
11
+ ",": 2,
12
+ "!": 3,
13
+ "?": 4,
14
+ ":": 5,
15
+ ";": 6,
16
+ "-": 7,
17
+ "_": 8,
18
+ "*": 9,
19
+ "#": 5,
20
+ "+": 4,
21
+ "=": 6,
22
+ "@": 8,
23
+ "$": 7,
24
+ "%": 5,
25
+ "&": 9,
26
+ "/": 3,
27
+ "\\": 3,
28
+ "(": 2,
29
+ ")": 2,
30
+ "[": 2,
31
+ "]": 2,
32
+ "{": 2,
33
+ "}": 2,
34
+ "<": 1,
35
+ ">": 1,
36
+ "|": 1,
37
+ "~": 9,
38
+ "^": 8
39
+ }
40
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "name": "Gdk9 Official (Example)",
3
+ "description": "Replace this file with your official mapping and rename to official.json to activate.",
4
+ "letter_mode": "a1z26",
5
+ "number_mode": "digital_root",
6
+ "normalize_zero_to_nine": true,
7
+ "weights": {"letter": 1, "digit": 1, "symbol": 1},
8
+ "harmonics": true,
9
+ "symbol_energy": {
10
+ ".": 1,
11
+ ",": 2,
12
+ "!": 3,
13
+ "?": 4
14
+ }
15
+ }
16
+
@@ -0,0 +1,132 @@
1
+ {
2
+ "name": "Gdk9 Official",
3
+ "description": "Official GDk9 Implication Engine Mapping embedded alongside CLI principle fields.",
4
+ "letter_mode": "a1z26",
5
+ "number_mode": "digital_root",
6
+ "normalize_zero_to_nine": true,
7
+ "weights": {"letter": 1, "digit": 1, "symbol": 1},
8
+ "harmonics": true,
9
+ "symbol_energy": {
10
+ ".": 1,
11
+ ",": 2,
12
+ "!": 3,
13
+ "?": 4,
14
+ ":": 5,
15
+ ";": 6,
16
+ "-": 7,
17
+ "_": 8,
18
+ "*": 9,
19
+ "#": 5,
20
+ "+": 4,
21
+ "=": 6,
22
+ "@": 8,
23
+ "$": 7,
24
+ "%": 5,
25
+ "&": 9,
26
+ "/": 3,
27
+ "\\": 3,
28
+ "(": 2,
29
+ ")": 2,
30
+ "[": 2,
31
+ "]": 2,
32
+ "{": 2,
33
+ "}": 2,
34
+ "<": 1,
35
+ ">": 1,
36
+ "|": 1,
37
+ "~": 9,
38
+ "^": 8
39
+ },
40
+ "gdk9_mapping": {
41
+ "version": "0.1.0",
42
+ "spec": "GDk9 Implication Engine Mapping",
43
+ "updated_utc": "2025-10-22T00:00:00Z",
44
+ "units": {
45
+ "energy": "E",
46
+ "epsilon": 1e-9
47
+ },
48
+ "naming": {
49
+ "symbol_regex": "^[A-Z][A-Za-z0-9_]*$",
50
+ "rule_regex": "^[a-z][a-z0-9_]*$",
51
+ "reserved_symbols": ["E0", "NULL", "ONE"]
52
+ },
53
+ "layers": [
54
+ {"id": "L1", "name": "Physics↔Computation", "concepts": ["rest_energy", "landauer", "bit_energy_boundary"]},
55
+ {"id": "L2", "name": "Language↔Logic", "concepts": ["implication", "parse_tree", "normal_form"]},
56
+ {"id": "L3", "name": "Identity↔Access", "concepts": ["key_invariant", "signature", "policy_rule"]},
57
+ {"id": "L4", "name": "Games↔Economics", "concepts": ["token_energy", "score", "exchange_rule"]}
58
+ ],
59
+ "symbol": {
60
+ "fields": ["name", "energy"],
61
+ "constraints": {
62
+ "name": "must match naming.symbol_regex",
63
+ "energy": "float, finite"
64
+ },
65
+ "examples": [
66
+ {"name": "A", "energy": 2.0},
67
+ {"name": "B", "energy": 3.5}
68
+ ]
69
+ },
70
+ "conservation": {
71
+ "law": "sum(inputs.energy) == sum(outputs.energy)",
72
+ "tolerance": 1e-9,
73
+ "on_violation": "error:ConservationError"
74
+ },
75
+ "rules": {
76
+ "types": ["fusion", "split", "custom"],
77
+ "templates": {
78
+ "fusion": {
79
+ "arity": "n>=2",
80
+ "energy": "sum(inputs)",
81
+ "output": {"name": "concat(inputs.names)", "energy": "sum(inputs.energy)"}
82
+ },
83
+ "split": {
84
+ "arity": 1,
85
+ "params": {"ratio": "0..1"},
86
+ "outputs": [
87
+ {"name": "OUT_A", "energy": "E_in * ratio"},
88
+ {"name": "OUT_B", "energy": "E_in * (1-ratio)"}
89
+ ]
90
+ },
91
+ "custom": {
92
+ "arity": "n>=1",
93
+ "energy": "must conserve",
94
+ "definition": "user function(inputs)->outputs"
95
+ }
96
+ },
97
+ "id_hash": "sha256(name|arity|template|params)"
98
+ },
99
+ "cli_map": {
100
+ "symbol_add": "gdk9 symbol add <NAME> <ENERGY>",
101
+ "symbol_list": "gdk9 symbol list",
102
+ "rule_define_fusion": "gdk9 imply define-fusion <RULE> <OUT_NAME> <ARITY>",
103
+ "rule_define_split": "gdk9 imply define-split <RULE> <OUT_A> <OUT_B> <RATIO>",
104
+ "rule_list": "gdk9 imply list",
105
+ "rule_apply": "gdk9 imply apply <RULE> <INPUTS...>",
106
+ "repl": "gdk9 repl"
107
+ },
108
+ "storage": {
109
+ "path": "~/.gdk9/state.json",
110
+ "format": {
111
+ "symbols": {"<NAME>": "<ENERGY:float>"},
112
+ "rules": ["<RULE_NAME>"]
113
+ }
114
+ },
115
+ "telemetry": {
116
+ "log_fields": ["ts", "cmd", "inputs", "outputs", "ok"],
117
+ "privacy": "local-only in this build"
118
+ },
119
+ "examples": {
120
+ "fusion": {
121
+ "inputs": [{"name":"A","energy":2.0},{"name":"B","energy":3.0}],
122
+ "outputs": [{"name":"AB","energy":5.0}]
123
+ },
124
+ "split": {
125
+ "inputs": [{"name":"AB","energy":5.0}],
126
+ "params": {"ratio": 0.4},
127
+ "outputs": [{"name":"A","energy":2.0},{"name":"B","energy":3.0}]
128
+ }
129
+ }
130
+ }
131
+ }
132
+