quilt-spreadsheet 0.1.0__tar.gz
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.
- quilt_spreadsheet-0.1.0/LICENSE +3 -0
- quilt_spreadsheet-0.1.0/PKG-INFO +181 -0
- quilt_spreadsheet-0.1.0/README.md +166 -0
- quilt_spreadsheet-0.1.0/pyproject.toml +20 -0
- quilt_spreadsheet-0.1.0/quilt_spreadsheet/__init__.py +50 -0
- quilt_spreadsheet-0.1.0/quilt_spreadsheet/__main__.py +71 -0
- quilt_spreadsheet-0.1.0/quilt_spreadsheet/backend.py +183 -0
- quilt_spreadsheet-0.1.0/quilt_spreadsheet/cell.py +231 -0
- quilt_spreadsheet-0.1.0/quilt_spreadsheet/clock.py +130 -0
- quilt_spreadsheet-0.1.0/quilt_spreadsheet/color.py +90 -0
- quilt_spreadsheet-0.1.0/quilt_spreadsheet/grid.py +135 -0
- quilt_spreadsheet-0.1.0/quilt_spreadsheet/ledger.py +163 -0
- quilt_spreadsheet-0.1.0/quilt_spreadsheet/perception.py +72 -0
- quilt_spreadsheet-0.1.0/quilt_spreadsheet.egg-info/PKG-INFO +181 -0
- quilt_spreadsheet-0.1.0/quilt_spreadsheet.egg-info/SOURCES.txt +18 -0
- quilt_spreadsheet-0.1.0/quilt_spreadsheet.egg-info/dependency_links.txt +1 -0
- quilt_spreadsheet-0.1.0/quilt_spreadsheet.egg-info/top_level.txt +1 -0
- quilt_spreadsheet-0.1.0/setup.cfg +4 -0
- quilt_spreadsheet-0.1.0/setup.py +20 -0
- quilt_spreadsheet-0.1.0/tests/test_spreadsheet.py +262 -0
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: quilt-spreadsheet
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: The Quilt IDE as spreadsheet substrate — cells as programs with hooks, double-entry bookkeeping, per-cell color namespaces, backend porting, distributed clocks
|
|
5
|
+
Author: Casey / SuperInstance
|
|
6
|
+
License: MIT
|
|
7
|
+
Classifier: Development Status :: 3 - Alpha
|
|
8
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Requires-Python: >=3.8
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Dynamic: license-file
|
|
14
|
+
Dynamic: requires-python
|
|
15
|
+
|
|
16
|
+
# quilt-spreadsheet
|
|
17
|
+
|
|
18
|
+
> **The Quilt IDE as spreadsheet substrate.**
|
|
19
|
+
>
|
|
20
|
+
> Front-end: rows × cols. Each cell is a runnable program with hooks.
|
|
21
|
+
> Double-entry bookkeeping tracks every pull/push. Per-cell color
|
|
22
|
+
> namespaces. Backend porting across scales. Distributed clocks with
|
|
23
|
+
> skew tolerance.
|
|
24
|
+
|
|
25
|
+
## What this is
|
|
26
|
+
|
|
27
|
+
A runnable Python substrate where:
|
|
28
|
+
|
|
29
|
+
1. **Front-end is a spreadsheet** — `QuiltSpreadsheet(rows, cols)`. Each cell is a `SheetCell`.
|
|
30
|
+
2. **Each cell is a runnable program** with hooks (pulls wake it). No `main()`. Event-driven.
|
|
31
|
+
3. **Double-entry bookkeeping** — every pull/push pair is recorded as a ledger pair. Same UUID, same timestamp.
|
|
32
|
+
4. **Per-cell color namespaces** — cells can label anything anything. The substrate checks canonical agreement, not labels.
|
|
33
|
+
5. **Backend porting** — different cells can use different scales/units. The backend converts at the boundary.
|
|
34
|
+
6. **Distributed clocks with skew** — clocks can disagree. Time-between-events is the sync signal. Confidence rises with shared events.
|
|
35
|
+
|
|
36
|
+
## Quick start
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
# Run the canonical demo
|
|
40
|
+
python3 -m quilt_spreadsheet
|
|
41
|
+
|
|
42
|
+
# Or with custom dimensions
|
|
43
|
+
python3 -m quilt_spreadsheet --rows 4 --cols 4
|
|
44
|
+
|
|
45
|
+
# Run individual demos
|
|
46
|
+
PYTHONPATH=. python3 demos/demo_double_entry.py
|
|
47
|
+
PYTHONPATH=. python3 demos/demo_color_independent.py
|
|
48
|
+
PYTHONPATH=. python3 demos/demo_backend_conversion.py
|
|
49
|
+
PYTHONPATH=. python3 demos/demo_clock_skew.py
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Quick example
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
from quilt_spreadsheet.grid import QuiltSpreadsheet
|
|
56
|
+
|
|
57
|
+
sheet = QuiltSpreadsheet(rows=4, cols=4)
|
|
58
|
+
|
|
59
|
+
def adder(*, source, hook, value):
|
|
60
|
+
return (value or 0) + 1
|
|
61
|
+
|
|
62
|
+
def doubler(*, source, hook, value):
|
|
63
|
+
return (value or 0) * 2
|
|
64
|
+
|
|
65
|
+
# Place programs into cells
|
|
66
|
+
sheet.place_cell(0, 0, adder)
|
|
67
|
+
sheet.place_cell(0, 1, doubler)
|
|
68
|
+
|
|
69
|
+
# Cell (0, 0) pulls from cell (0, 1) with value=5
|
|
70
|
+
result = sheet.pull(0, 0, 0, 1, hook="double", value=5)
|
|
71
|
+
|
|
72
|
+
print(result)
|
|
73
|
+
# {'response': 10, 'pair_id': '...', 'timestamp_ns': ..., 'cell': (0, 1)}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## The 4 demos
|
|
77
|
+
|
|
78
|
+
### 1. `demos/demo_double_entry.py`
|
|
79
|
+
|
|
80
|
+
Demonstrates the load-bearing ledger. Pulls and pushes are paired by UUID
|
|
81
|
+
+ timestamp. Unbalanced pulls are flagged but not prevented.
|
|
82
|
+
|
|
83
|
+
### 2. `demos/demo_color_independent.py`
|
|
84
|
+
|
|
85
|
+
Two cells with different color labels (red/blue) for the same canonical
|
|
86
|
+
(CRITICAL). The substrate verifies agreement at canonical level, not
|
|
87
|
+
labels.
|
|
88
|
+
|
|
89
|
+
### 3. `demos/demo_backend_conversion.py`
|
|
90
|
+
|
|
91
|
+
Backend ports values between scales (celsius → fahrenheit). Gates values
|
|
92
|
+
below threshold. Snaps approximations to discrete grid. Time is preserved
|
|
93
|
+
in same units on both sides.
|
|
94
|
+
|
|
95
|
+
### 4. `demos/demo_clock_skew.py`
|
|
96
|
+
|
|
97
|
+
Two cells share 10 events. Confidence rises asymptotically from 0.5 to 0.91.
|
|
98
|
+
Skew is observable but manageable.
|
|
99
|
+
|
|
100
|
+
## Architecture
|
|
101
|
+
|
|
102
|
+
```
|
|
103
|
+
QuiltSpreadsheet (rows × cols)
|
|
104
|
+
├── CellClock [per-cell, private]
|
|
105
|
+
├── Ledger [double-entry pair ledger]
|
|
106
|
+
├── Backend [conversions, gates, snaps]
|
|
107
|
+
└── SheetCell (rows × cols)
|
|
108
|
+
├── program: Callable (no main, runs on pull)
|
|
109
|
+
├── axioms: frozenset (DNA)
|
|
110
|
+
├── dials: List[float] (16 mutable state slots)
|
|
111
|
+
├── color: ColorNamespace (private labels)
|
|
112
|
+
├── perception: Perception (own sort/group)
|
|
113
|
+
├── witness_log: List[dict]
|
|
114
|
+
├── pulls: Dict[cell, List[hook]]
|
|
115
|
+
└── pushes: Dict[cell, List[hook]]
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## The 3 commands
|
|
119
|
+
|
|
120
|
+
```python
|
|
121
|
+
# 1. Place a program into a cell
|
|
122
|
+
sheet.place_cell(row, col, program, axioms=None)
|
|
123
|
+
|
|
124
|
+
# 2. Pull from another cell (cell A at (r0, c0) wakes cell at (r1, c1))
|
|
125
|
+
sheet.pull(r0, c0, r1, c1, hook='name', value=...)
|
|
126
|
+
|
|
127
|
+
# 3. Inspect state
|
|
128
|
+
sheet.render() # spreadsheet-shaped ascii view
|
|
129
|
+
sheet.summary() # substrate state dict
|
|
130
|
+
sheet.ledger.summary() # double-entry pair ledger
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Substrate transition
|
|
134
|
+
|
|
135
|
+
This repo is the substrate transition FROM `quilt-egg` (single cell) TO
|
|
136
|
+
`quilt-spreadsheet` (grid of cells with hooks). Same constants, same DNA.
|
|
137
|
+
More dimensions.
|
|
138
|
+
|
|
139
|
+
| quilt-egg | quilt-spreadsheet |
|
|
140
|
+
|---|---|
|
|
141
|
+
| 1 cell | rows × cols cells |
|
|
142
|
+
| 1 tick loop | pull-triggered wake |
|
|
143
|
+
| DNA + dials | DNA + dials + hooks + color + perception |
|
|
144
|
+
| 18 tests | 21 tests |
|
|
145
|
+
| 4 demos | 4 demos |
|
|
146
|
+
|
|
147
|
+
## Layered navigation
|
|
148
|
+
|
|
149
|
+
| Layer | Where |
|
|
150
|
+
|---|---|
|
|
151
|
+
| **CANON.md** | [CANON.md](CANON.md) — what this repo is, in 24 lines |
|
|
152
|
+
| **README** | [README.md](README.md) — quick start, navigation |
|
|
153
|
+
| **Spec** | [docs/SPEC.md](docs/SPEC.md) — half-canon, half-spec |
|
|
154
|
+
| **Source** | [quilt_spreadsheet/](quilt_spreadsheet/) — `clock.py`, `ledger.py`, `color.py`, `backend.py`, `perception.py`, `cell.py`, `grid.py` |
|
|
155
|
+
| **Demos** | [demos/](demos/) — 4 runnable demos |
|
|
156
|
+
| **Tests** | [tests/](tests/) — 21 unit tests |
|
|
157
|
+
| **Origin** | [quilt-egg](../quilt-egg/) — the prior substrate |
|
|
158
|
+
|
|
159
|
+
## Polyformalism
|
|
160
|
+
|
|
161
|
+
This substrate is canonically a polyformalism port. The canary
|
|
162
|
+
`fnv1a-64("café Δ 日本語") = 0x024a555471370b18d` is verified on every
|
|
163
|
+
Quilt port.
|
|
164
|
+
|
|
165
|
+
## Tests
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
PYTHONPATH=. python3 -m unittest discover -s tests -v
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
21 tests covering:
|
|
172
|
+
- Clock skew tolerance and pairwise confidence
|
|
173
|
+
- Double-entry ledger pairing (and unbalanced-pull handling)
|
|
174
|
+
- Per-cell color namespaces
|
|
175
|
+
- Backend conversions, gates, snaps
|
|
176
|
+
- Spreadsheet pull/push wiring
|
|
177
|
+
- Perception sorting/grouping/filtering
|
|
178
|
+
|
|
179
|
+
## License
|
|
180
|
+
|
|
181
|
+
MIT — Casey / SuperInstance, Sept 23, 2026
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# quilt-spreadsheet
|
|
2
|
+
|
|
3
|
+
> **The Quilt IDE as spreadsheet substrate.**
|
|
4
|
+
>
|
|
5
|
+
> Front-end: rows × cols. Each cell is a runnable program with hooks.
|
|
6
|
+
> Double-entry bookkeeping tracks every pull/push. Per-cell color
|
|
7
|
+
> namespaces. Backend porting across scales. Distributed clocks with
|
|
8
|
+
> skew tolerance.
|
|
9
|
+
|
|
10
|
+
## What this is
|
|
11
|
+
|
|
12
|
+
A runnable Python substrate where:
|
|
13
|
+
|
|
14
|
+
1. **Front-end is a spreadsheet** — `QuiltSpreadsheet(rows, cols)`. Each cell is a `SheetCell`.
|
|
15
|
+
2. **Each cell is a runnable program** with hooks (pulls wake it). No `main()`. Event-driven.
|
|
16
|
+
3. **Double-entry bookkeeping** — every pull/push pair is recorded as a ledger pair. Same UUID, same timestamp.
|
|
17
|
+
4. **Per-cell color namespaces** — cells can label anything anything. The substrate checks canonical agreement, not labels.
|
|
18
|
+
5. **Backend porting** — different cells can use different scales/units. The backend converts at the boundary.
|
|
19
|
+
6. **Distributed clocks with skew** — clocks can disagree. Time-between-events is the sync signal. Confidence rises with shared events.
|
|
20
|
+
|
|
21
|
+
## Quick start
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
# Run the canonical demo
|
|
25
|
+
python3 -m quilt_spreadsheet
|
|
26
|
+
|
|
27
|
+
# Or with custom dimensions
|
|
28
|
+
python3 -m quilt_spreadsheet --rows 4 --cols 4
|
|
29
|
+
|
|
30
|
+
# Run individual demos
|
|
31
|
+
PYTHONPATH=. python3 demos/demo_double_entry.py
|
|
32
|
+
PYTHONPATH=. python3 demos/demo_color_independent.py
|
|
33
|
+
PYTHONPATH=. python3 demos/demo_backend_conversion.py
|
|
34
|
+
PYTHONPATH=. python3 demos/demo_clock_skew.py
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Quick example
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
from quilt_spreadsheet.grid import QuiltSpreadsheet
|
|
41
|
+
|
|
42
|
+
sheet = QuiltSpreadsheet(rows=4, cols=4)
|
|
43
|
+
|
|
44
|
+
def adder(*, source, hook, value):
|
|
45
|
+
return (value or 0) + 1
|
|
46
|
+
|
|
47
|
+
def doubler(*, source, hook, value):
|
|
48
|
+
return (value or 0) * 2
|
|
49
|
+
|
|
50
|
+
# Place programs into cells
|
|
51
|
+
sheet.place_cell(0, 0, adder)
|
|
52
|
+
sheet.place_cell(0, 1, doubler)
|
|
53
|
+
|
|
54
|
+
# Cell (0, 0) pulls from cell (0, 1) with value=5
|
|
55
|
+
result = sheet.pull(0, 0, 0, 1, hook="double", value=5)
|
|
56
|
+
|
|
57
|
+
print(result)
|
|
58
|
+
# {'response': 10, 'pair_id': '...', 'timestamp_ns': ..., 'cell': (0, 1)}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## The 4 demos
|
|
62
|
+
|
|
63
|
+
### 1. `demos/demo_double_entry.py`
|
|
64
|
+
|
|
65
|
+
Demonstrates the load-bearing ledger. Pulls and pushes are paired by UUID
|
|
66
|
+
+ timestamp. Unbalanced pulls are flagged but not prevented.
|
|
67
|
+
|
|
68
|
+
### 2. `demos/demo_color_independent.py`
|
|
69
|
+
|
|
70
|
+
Two cells with different color labels (red/blue) for the same canonical
|
|
71
|
+
(CRITICAL). The substrate verifies agreement at canonical level, not
|
|
72
|
+
labels.
|
|
73
|
+
|
|
74
|
+
### 3. `demos/demo_backend_conversion.py`
|
|
75
|
+
|
|
76
|
+
Backend ports values between scales (celsius → fahrenheit). Gates values
|
|
77
|
+
below threshold. Snaps approximations to discrete grid. Time is preserved
|
|
78
|
+
in same units on both sides.
|
|
79
|
+
|
|
80
|
+
### 4. `demos/demo_clock_skew.py`
|
|
81
|
+
|
|
82
|
+
Two cells share 10 events. Confidence rises asymptotically from 0.5 to 0.91.
|
|
83
|
+
Skew is observable but manageable.
|
|
84
|
+
|
|
85
|
+
## Architecture
|
|
86
|
+
|
|
87
|
+
```
|
|
88
|
+
QuiltSpreadsheet (rows × cols)
|
|
89
|
+
├── CellClock [per-cell, private]
|
|
90
|
+
├── Ledger [double-entry pair ledger]
|
|
91
|
+
├── Backend [conversions, gates, snaps]
|
|
92
|
+
└── SheetCell (rows × cols)
|
|
93
|
+
├── program: Callable (no main, runs on pull)
|
|
94
|
+
├── axioms: frozenset (DNA)
|
|
95
|
+
├── dials: List[float] (16 mutable state slots)
|
|
96
|
+
├── color: ColorNamespace (private labels)
|
|
97
|
+
├── perception: Perception (own sort/group)
|
|
98
|
+
├── witness_log: List[dict]
|
|
99
|
+
├── pulls: Dict[cell, List[hook]]
|
|
100
|
+
└── pushes: Dict[cell, List[hook]]
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## The 3 commands
|
|
104
|
+
|
|
105
|
+
```python
|
|
106
|
+
# 1. Place a program into a cell
|
|
107
|
+
sheet.place_cell(row, col, program, axioms=None)
|
|
108
|
+
|
|
109
|
+
# 2. Pull from another cell (cell A at (r0, c0) wakes cell at (r1, c1))
|
|
110
|
+
sheet.pull(r0, c0, r1, c1, hook='name', value=...)
|
|
111
|
+
|
|
112
|
+
# 3. Inspect state
|
|
113
|
+
sheet.render() # spreadsheet-shaped ascii view
|
|
114
|
+
sheet.summary() # substrate state dict
|
|
115
|
+
sheet.ledger.summary() # double-entry pair ledger
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Substrate transition
|
|
119
|
+
|
|
120
|
+
This repo is the substrate transition FROM `quilt-egg` (single cell) TO
|
|
121
|
+
`quilt-spreadsheet` (grid of cells with hooks). Same constants, same DNA.
|
|
122
|
+
More dimensions.
|
|
123
|
+
|
|
124
|
+
| quilt-egg | quilt-spreadsheet |
|
|
125
|
+
|---|---|
|
|
126
|
+
| 1 cell | rows × cols cells |
|
|
127
|
+
| 1 tick loop | pull-triggered wake |
|
|
128
|
+
| DNA + dials | DNA + dials + hooks + color + perception |
|
|
129
|
+
| 18 tests | 21 tests |
|
|
130
|
+
| 4 demos | 4 demos |
|
|
131
|
+
|
|
132
|
+
## Layered navigation
|
|
133
|
+
|
|
134
|
+
| Layer | Where |
|
|
135
|
+
|---|---|
|
|
136
|
+
| **CANON.md** | [CANON.md](CANON.md) — what this repo is, in 24 lines |
|
|
137
|
+
| **README** | [README.md](README.md) — quick start, navigation |
|
|
138
|
+
| **Spec** | [docs/SPEC.md](docs/SPEC.md) — half-canon, half-spec |
|
|
139
|
+
| **Source** | [quilt_spreadsheet/](quilt_spreadsheet/) — `clock.py`, `ledger.py`, `color.py`, `backend.py`, `perception.py`, `cell.py`, `grid.py` |
|
|
140
|
+
| **Demos** | [demos/](demos/) — 4 runnable demos |
|
|
141
|
+
| **Tests** | [tests/](tests/) — 21 unit tests |
|
|
142
|
+
| **Origin** | [quilt-egg](../quilt-egg/) — the prior substrate |
|
|
143
|
+
|
|
144
|
+
## Polyformalism
|
|
145
|
+
|
|
146
|
+
This substrate is canonically a polyformalism port. The canary
|
|
147
|
+
`fnv1a-64("café Δ 日本語") = 0x024a555471370b18d` is verified on every
|
|
148
|
+
Quilt port.
|
|
149
|
+
|
|
150
|
+
## Tests
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
PYTHONPATH=. python3 -m unittest discover -s tests -v
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
21 tests covering:
|
|
157
|
+
- Clock skew tolerance and pairwise confidence
|
|
158
|
+
- Double-entry ledger pairing (and unbalanced-pull handling)
|
|
159
|
+
- Per-cell color namespaces
|
|
160
|
+
- Backend conversions, gates, snaps
|
|
161
|
+
- Spreadsheet pull/push wiring
|
|
162
|
+
- Perception sorting/grouping/filtering
|
|
163
|
+
|
|
164
|
+
## License
|
|
165
|
+
|
|
166
|
+
MIT — Casey / SuperInstance, Sept 23, 2026
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "quilt-spreadsheet"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "The Quilt IDE as spreadsheet substrate — cells as programs with hooks, double-entry bookkeeping, per-cell color namespaces, backend porting, distributed clocks"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.8"
|
|
11
|
+
license = {text = "MIT"}
|
|
12
|
+
authors = [{name = "Casey / SuperInstance"}]
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Development Status :: 3 - Alpha",
|
|
15
|
+
"License :: OSI Approved :: MIT License",
|
|
16
|
+
"Programming Language :: Python :: 3",
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
[tool.setuptools]
|
|
20
|
+
packages = ["quilt_spreadsheet"]
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"""
|
|
2
|
+
quilt-spreadsheet — the IDE substrate.
|
|
3
|
+
|
|
4
|
+
A spreadsheet-as-IDE where:
|
|
5
|
+
- front-end looks like a spreadsheet (rows x cols)
|
|
6
|
+
- each cell is a runnable program with hooks
|
|
7
|
+
- double-entry bookkeeping tracks every pull/push pair
|
|
8
|
+
- per-cell color namespaces (semantic heterogeneity is OK)
|
|
9
|
+
- backend porting handles scale/unit conversions
|
|
10
|
+
- distributed clocks allow skew; time-between-events converges
|
|
11
|
+
|
|
12
|
+
This is the SUBSTRATE TRANSITION from quilt-egg (a single cell) to
|
|
13
|
+
quilt-spreadsheet (a grid of cells with hooks).
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from .clock import (
|
|
17
|
+
CellClock, DistributedClock, get_default_clock, reset_default_clock
|
|
18
|
+
)
|
|
19
|
+
from .ledger import (
|
|
20
|
+
LedgerEntry, Ledger, get_default_ledger, reset_default_ledger
|
|
21
|
+
)
|
|
22
|
+
from .color import (
|
|
23
|
+
ColorNamespace, same_canonical, labels_matter
|
|
24
|
+
)
|
|
25
|
+
from .backend import (
|
|
26
|
+
Backend, get_default_backend, reset_default_backend,
|
|
27
|
+
convert, REGISTRY,
|
|
28
|
+
)
|
|
29
|
+
from .perception import Perception
|
|
30
|
+
from .cell import (
|
|
31
|
+
SheetCell, DOCTRINE_AXIOMS, ACTION_AXIOMS,
|
|
32
|
+
register_cell, reset_registry, get_cell, all_cells,
|
|
33
|
+
)
|
|
34
|
+
from .grid import QuiltSpreadsheet, place_cell
|
|
35
|
+
|
|
36
|
+
__all__ = [
|
|
37
|
+
"CellClock", "DistributedClock",
|
|
38
|
+
"get_default_clock", "reset_default_clock",
|
|
39
|
+
"LedgerEntry", "Ledger",
|
|
40
|
+
"get_default_ledger", "reset_default_ledger",
|
|
41
|
+
"ColorNamespace", "same_canonical", "labels_matter",
|
|
42
|
+
"Backend", "get_default_backend", "reset_default_backend",
|
|
43
|
+
"convert", "REGISTRY",
|
|
44
|
+
"Perception",
|
|
45
|
+
"SheetCell", "DOCTRINE_AXIOMS", "ACTION_AXIOMS",
|
|
46
|
+
"register_cell", "reset_registry", "get_cell", "all_cells",
|
|
47
|
+
"QuiltSpreadsheet", "place_cell",
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
__version__ = "0.1.0"
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"""
|
|
2
|
+
__main__.py — `python -m quilt_spreadsheet` runs a demo spreadsheet.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import sys
|
|
7
|
+
|
|
8
|
+
from .grid import QuiltSpreadsheet, reset_all
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def demo_spreadsheet():
|
|
12
|
+
"""Build a 4×4 substrate with 4 programs that respond to pulls."""
|
|
13
|
+
reset_all()
|
|
14
|
+
sheet = QuiltSpreadsheet(rows=4, cols=4, name="canonical_demo")
|
|
15
|
+
|
|
16
|
+
def adder(*, source, hook, value):
|
|
17
|
+
# Cell that adds 1 to whatever is pulled
|
|
18
|
+
return (value or 0) + 1
|
|
19
|
+
|
|
20
|
+
def doubler(*, source, hook, value):
|
|
21
|
+
return (value or 0) * 2
|
|
22
|
+
|
|
23
|
+
def labeler(*, source, hook, value):
|
|
24
|
+
# Cell that translates a label
|
|
25
|
+
return "doubled" if hook == "double" else "added"
|
|
26
|
+
|
|
27
|
+
def tally(*, source, hook, value):
|
|
28
|
+
# Counts how many times it has been pulled
|
|
29
|
+
return f"tally:{value}"
|
|
30
|
+
|
|
31
|
+
# Place four programs
|
|
32
|
+
sheet.place_cell(0, 0, adder)
|
|
33
|
+
sheet.place_cell(0, 1, doubler)
|
|
34
|
+
sheet.place_cell(1, 0, labeler)
|
|
35
|
+
sheet.place_cell(1, 1, tally)
|
|
36
|
+
|
|
37
|
+
# Wire a workflow: cell at (2, 2) pulls adder, then doubler
|
|
38
|
+
def workflow(*, source, hook, value):
|
|
39
|
+
return f"workflow[{hook}]={value}"
|
|
40
|
+
|
|
41
|
+
sheet.place_cell(2, 2, workflow)
|
|
42
|
+
# Pull adder
|
|
43
|
+
sheet.pull(2, 2, 0, 0, hook="step1", value=5)
|
|
44
|
+
# Pull doubler — note this uses ledger.record_pair
|
|
45
|
+
sheet.pull(0, 0, 0, 1, hook="double", value=5)
|
|
46
|
+
|
|
47
|
+
print(sheet.render())
|
|
48
|
+
print()
|
|
49
|
+
print("Summary:")
|
|
50
|
+
for k, v in sheet.summary().items():
|
|
51
|
+
print(f" {k}: {v}")
|
|
52
|
+
print()
|
|
53
|
+
print("Pulls and pushes recorded in the ledger (double-entry pairs).")
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def main():
|
|
57
|
+
parser = argparse.ArgumentParser(
|
|
58
|
+
prog="quilt_spreadsheet",
|
|
59
|
+
description="The Quilt spreadsheet IDE substrate — double-entry bookkeeping, distributed clocks, per-cell color namespaces, backend porting.",
|
|
60
|
+
)
|
|
61
|
+
parser.add_argument("--rows", type=int, default=4, help="Spreadsheet rows (default: 4)")
|
|
62
|
+
parser.add_argument("--cols", type=int, default=4, help="Spreadsheet cols (default: 4)")
|
|
63
|
+
parser.add_argument("--demo", choices=["canonical"], default="canonical", help="Demo to run")
|
|
64
|
+
args = parser.parse_args()
|
|
65
|
+
if args.demo == "canonical":
|
|
66
|
+
demo_spreadsheet()
|
|
67
|
+
return 0
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
if __name__ == "__main__":
|
|
71
|
+
sys.exit(main())
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
"""
|
|
2
|
+
backend.py — the porting, conversion, gating, and snapping layer.
|
|
3
|
+
|
|
4
|
+
Casey: "the true backend is the actual porting and conversions. one cell
|
|
5
|
+
might be using a different scale than another but if the backend can convert
|
|
6
|
+
so their equal exchange for their applications, it doesn't matter. the
|
|
7
|
+
backend can gate variables or convert approximations to snaps but the time
|
|
8
|
+
is in the same units on both sides."
|
|
9
|
+
|
|
10
|
+
This is the polyformalism-port layer at the substrate level. Different
|
|
11
|
+
cells may use:
|
|
12
|
+
- different scales (celsius vs fahrenheit)
|
|
13
|
+
- different units (ns vs ms)
|
|
14
|
+
- different precision (continuous vs snapped)
|
|
15
|
+
- different visibility (some variables are gated)
|
|
16
|
+
|
|
17
|
+
The backend DOES NOT enforce agreement. The backend PROVIDES porting
|
|
18
|
+
for when cells need to exchange data.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from typing import Any, Tuple
|
|
22
|
+
import math
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
#: The canonical conversions registry — extensible.
|
|
26
|
+
#: Each entry: name -> callable(value, from_unit, to_unit)
|
|
27
|
+
#: Time is preserved in the SAME units on both sides (per Casey).
|
|
28
|
+
REGISTRY = {}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def convert(name: str):
|
|
32
|
+
"""Decorator to register a backend conversion."""
|
|
33
|
+
def deco(fn):
|
|
34
|
+
REGISTRY[name] = fn
|
|
35
|
+
return fn
|
|
36
|
+
return deco
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@convert("temperature")
|
|
40
|
+
def convert_temperature(value: float, from_unit: str, to_unit: str) -> float:
|
|
41
|
+
"""Convert temperature scales."""
|
|
42
|
+
if from_unit == to_unit:
|
|
43
|
+
return value
|
|
44
|
+
if from_unit == 'celsius' and to_unit == 'fahrenheit':
|
|
45
|
+
return value * 9/5 + 32
|
|
46
|
+
if from_unit == 'fahrenheit' and to_unit == 'celsius':
|
|
47
|
+
return (value - 32) * 5/9
|
|
48
|
+
if from_unit == 'celsius' and to_unit == 'kelvin':
|
|
49
|
+
return value + 273.15
|
|
50
|
+
raise ValueError(f"unsupported temperature conversion: {from_unit} -> {to_unit}")
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@convert("time")
|
|
54
|
+
def convert_time(value: float, from_unit: str, to_unit: str) -> float:
|
|
55
|
+
"""Time is preserved in the SAME units on both sides (per Casey).
|
|
56
|
+
|
|
57
|
+
Per Casey's directive: 'time is in the same units on both sides'.
|
|
58
|
+
This converter refuses to convert across units — it normalizes to
|
|
59
|
+
nanoseconds (canonical) and back.
|
|
60
|
+
|
|
61
|
+
Use ms and ns. The substrate does not negotiate time across calendars.
|
|
62
|
+
"""
|
|
63
|
+
if from_unit == to_unit:
|
|
64
|
+
return value
|
|
65
|
+
if from_unit not in ('ns', 'ms', 's') or to_unit not in ('ns', 'ms', 's'):
|
|
66
|
+
raise ValueError(f"time must be in same units: {from_unit} vs {to_unit}")
|
|
67
|
+
# Casey: convert for compatibility but it's a unit-preserving op
|
|
68
|
+
if from_unit == 'ns' and to_unit == 'ms':
|
|
69
|
+
return value / 1e6
|
|
70
|
+
if from_unit == 'ms' and to_unit == 'ns':
|
|
71
|
+
return value * 1e6
|
|
72
|
+
if from_unit == 'ns' and to_unit == 's':
|
|
73
|
+
return value / 1e9
|
|
74
|
+
if from_unit == 's' and to_unit == 'ns':
|
|
75
|
+
return value * 1e9
|
|
76
|
+
return value
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
@convert("distance")
|
|
80
|
+
def convert_distance(value: float, from_unit: str, to_unit: str) -> float:
|
|
81
|
+
"""Distance scaling."""
|
|
82
|
+
if from_unit == to_unit:
|
|
83
|
+
return value
|
|
84
|
+
if from_unit == 'm' and to_unit == 'cm':
|
|
85
|
+
return value * 100
|
|
86
|
+
if from_unit == 'cm' and to_unit == 'm':
|
|
87
|
+
return value / 100
|
|
88
|
+
raise ValueError(f"unsupported distance conversion: {from_unit} -> {to_unit}")
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
@convert("ratio")
|
|
92
|
+
def convert_ratio(value: float, from_unit: str, to_unit: str) -> float:
|
|
93
|
+
"""Ratios are unitless — convert between percent, decimal, ppm."""
|
|
94
|
+
if from_unit == to_unit:
|
|
95
|
+
return value
|
|
96
|
+
if from_unit == 'decimal' and to_unit == 'percent':
|
|
97
|
+
return value * 100
|
|
98
|
+
if from_unit == 'percent' and to_unit == 'decimal':
|
|
99
|
+
return value / 100
|
|
100
|
+
if from_unit == 'ppm' and to_unit == 'decimal':
|
|
101
|
+
return value / 1e6
|
|
102
|
+
raise ValueError(f"unsupported ratio conversion: {from_unit} -> {to_unit}")
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
class Backend:
|
|
106
|
+
"""The porting backend — applied at cell boundaries.
|
|
107
|
+
|
|
108
|
+
Functions: convert, gate, snap, time-aware porting.
|
|
109
|
+
"""
|
|
110
|
+
|
|
111
|
+
def __init__(self):
|
|
112
|
+
self.conversions_run: int = 0
|
|
113
|
+
|
|
114
|
+
def port(self, value: Any, from_unit: str, to_unit: str,
|
|
115
|
+
domain: str = "ratio") -> Any:
|
|
116
|
+
"""Port a value across scales/units in the same domain.
|
|
117
|
+
|
|
118
|
+
Default domain is 'ratio' (unitless). For physical conversions
|
|
119
|
+
pass domain='temperature' or 'distance'.
|
|
120
|
+
"""
|
|
121
|
+
if from_unit == to_unit:
|
|
122
|
+
return value
|
|
123
|
+
if domain not in REGISTRY:
|
|
124
|
+
raise ValueError(f"unknown backend domain: {domain}")
|
|
125
|
+
self.conversions_run += 1
|
|
126
|
+
return REGISTRY[domain](value, from_unit, to_unit)
|
|
127
|
+
|
|
128
|
+
def gate(self, value: Any, predicate) -> Any:
|
|
129
|
+
"""Backend 'gate' — release a value only if predicate is satisfied.
|
|
130
|
+
|
|
131
|
+
Casey: 'the backend can gate variables or convert approximations
|
|
132
|
+
to snaps'. The gate is controlled release/transformation.
|
|
133
|
+
|
|
134
|
+
Returns None if predicate fails. The cell receives None and can
|
|
135
|
+
decide what to do (default action: skip).
|
|
136
|
+
"""
|
|
137
|
+
if not predicate(value):
|
|
138
|
+
return None
|
|
139
|
+
return value
|
|
140
|
+
|
|
141
|
+
def gate_below(self, value: float, threshold: float) -> Any:
|
|
142
|
+
"""Common gate: block values below threshold."""
|
|
143
|
+
if value < threshold:
|
|
144
|
+
return None
|
|
145
|
+
return value
|
|
146
|
+
|
|
147
|
+
def snap(self, value: float, granularity: float) -> float:
|
|
148
|
+
"""Approximation snap — snap to discrete grid.
|
|
149
|
+
|
|
150
|
+
Casey: 'the backend can gate variables or convert approximations
|
|
151
|
+
to snaps'. A snap rounds the value to the nearest multiple of
|
|
152
|
+
granularity.
|
|
153
|
+
|
|
154
|
+
e.g. snap(3.7, 0.5) = 3.5 (or 4.0, depending on tie-break)
|
|
155
|
+
"""
|
|
156
|
+
if granularity <= 0:
|
|
157
|
+
return value
|
|
158
|
+
return round(value / granularity) * granularity
|
|
159
|
+
|
|
160
|
+
def port_time(self, value: float, from_unit: str, to_unit: str) -> float:
|
|
161
|
+
"""Time porting — preserves same units on both sides (per Casey)."""
|
|
162
|
+
return REGISTRY["time"](value, from_unit, to_unit)
|
|
163
|
+
|
|
164
|
+
def stats(self) -> dict:
|
|
165
|
+
return {
|
|
166
|
+
"conversions_run": self.conversions_run,
|
|
167
|
+
"available_conversions": sorted(REGISTRY.keys()),
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
_default_backend = None
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def get_default_backend() -> Backend:
|
|
175
|
+
global _default_backend
|
|
176
|
+
if _default_backend is None:
|
|
177
|
+
_default_backend = Backend()
|
|
178
|
+
return _default_backend
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def reset_default_backend() -> None:
|
|
182
|
+
global _default_backend
|
|
183
|
+
_default_backend = None
|