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.
- examples/01_analyze.py +28 -0
- examples/02_profile.py +24 -0
- examples/03_assign.py +19 -0
- examples/04_dcg.py +41 -0
- examples/05_tokenize.py +22 -0
- examples/06_synthesize.py +20 -0
- examples/07_api.py +57 -0
- examples/08_kernel.py +35 -0
- examples/09_kernel_search_walkthrough.py +102 -0
- examples/10_conserve_vs_naive.py +100 -0
- gdk9/__init__.py +5 -0
- gdk9/ansi.py +57 -0
- gdk9/cli.py +1186 -0
- gdk9/crdt.py +88 -0
- gdk9/crypto.py +132 -0
- gdk9/data/ninefold.json +40 -0
- gdk9/data/official.example.json +16 -0
- gdk9/data/official.json +132 -0
- gdk9/data/subs.json +436 -0
- gdk9/dcg.py +356 -0
- gdk9/egglog_bridge/__init__.py +6 -0
- gdk9/egglog_bridge/dr_egglog.py +107 -0
- gdk9/egglog_bridge/fallback.py +34 -0
- gdk9/energy.py +211 -0
- gdk9/errors.py +15 -0
- gdk9/fmt.py +212 -0
- gdk9/imply.py +83 -0
- gdk9/io_utils.py +24 -0
- gdk9/kernel/__init__.py +24 -0
- gdk9/kernel/engine.py +123 -0
- gdk9/kernel/errors.py +17 -0
- gdk9/kernel/expression.py +43 -0
- gdk9/kernel/principle.py +43 -0
- gdk9/kernel/proof.py +24 -0
- gdk9/kernel/rule.py +30 -0
- gdk9/kernel/symbol.py +19 -0
- gdk9/kernel_cli.py +195 -0
- gdk9/log.py +30 -0
- gdk9/optimize.py +288 -0
- gdk9/parser.py +35 -0
- gdk9/plugins/__init__.py +9 -0
- gdk9/plugins/loader.py +346 -0
- gdk9/plugins/registry.py +42 -0
- gdk9/principles.py +131 -0
- gdk9/saturate.py +89 -0
- gdk9/state.py +138 -0
- gdk9/subs.py +108 -0
- gdk9/tokenize.py +386 -0
- gdk9/tui.py +115 -0
- gdk9/utilization.py +15 -0
- gdk9_cli-0.3.0.dist-info/METADATA +295 -0
- gdk9_cli-0.3.0.dist-info/RECORD +82 -0
- gdk9_cli-0.3.0.dist-info/WHEEL +5 -0
- gdk9_cli-0.3.0.dist-info/entry_points.txt +3 -0
- gdk9_cli-0.3.0.dist-info/licenses/LICENSE +661 -0
- gdk9_cli-0.3.0.dist-info/top_level.txt +4 -0
- scripts/batch_analyze.py +109 -0
- scripts/export_json.py +125 -0
- scripts/make_zip.py +57 -0
- scripts/profile_compare.py +165 -0
- scripts/watch_file.py +124 -0
- tests/conftest.py +26 -0
- tests/experiment/__init__.py +1 -0
- tests/experiment/test_conserve_vs_naive.py +287 -0
- tests/kernel/test_kernel_energy.py +18 -0
- tests/kernel/test_kernel_rules.py +58 -0
- tests/kernel/test_kernel_search_bound.py +34 -0
- tests/test_crypto.py +36 -0
- tests/test_crypto_secure.py +26 -0
- tests/test_dcg.py +318 -0
- tests/test_dcg_golden.py +67 -0
- tests/test_egglog_bridge.py +41 -0
- tests/test_energy.py +34 -0
- tests/test_kernel_cli_handbook.py +101 -0
- tests/test_optimize.py +21 -0
- tests/test_plugins.py +64 -0
- tests/test_rules.py +43 -0
- tests/test_rules_commit.py +40 -0
- tests/test_rules_reversibility.py +50 -0
- tests/test_state_crdt.py +87 -0
- tests/test_tokenize.py +38 -0
- 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
|
+
|
gdk9/data/ninefold.json
ADDED
|
@@ -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
|
+
|
gdk9/data/official.json
ADDED
|
@@ -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
|
+
|