the-grid-cc 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +206 -0
- package/agents/grid-executor.md +62 -0
- package/agents/grid-guard.md +72 -0
- package/agents/grid-planner.md +70 -0
- package/agents/grid-recognizer.md +100 -0
- package/bin/install.js +276 -0
- package/cli/__init__.py +7 -0
- package/cli/main.py +385 -0
- package/commands/grid/VERSION +1 -0
- package/commands/grid/help.md +54 -0
- package/commands/grid/init.md +51 -0
- package/commands/grid/mcp.md +159 -0
- package/commands/grid.md +12 -0
- package/config/grid.yaml +46 -0
- package/core/__init__.py +45 -0
- package/core/block.py +207 -0
- package/core/cluster.py +228 -0
- package/core/disc.py +254 -0
- package/core/energy.py +267 -0
- package/core/grid.py +326 -0
- package/core/io_tower.py +242 -0
- package/core/program.py +268 -0
- package/core/recognizer.py +294 -0
- package/core/thread.py +180 -0
- package/package.json +37 -0
- package/templates/__init__.py +14 -0
- package/templates/status.py +223 -0
- package/templates/welcome.py +101 -0
package/core/cluster.py
ADDED
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Cluster - Collection of related Blocks.
|
|
3
|
+
|
|
4
|
+
"Clusters organize the computational landscape of The Grid."
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from dataclasses import dataclass, field
|
|
8
|
+
from datetime import datetime
|
|
9
|
+
from typing import Optional
|
|
10
|
+
from enum import Enum
|
|
11
|
+
import uuid
|
|
12
|
+
|
|
13
|
+
from .block import Block, BlockStatus
|
|
14
|
+
from .energy import EnergyPool
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class ClusterStatus(Enum):
|
|
18
|
+
"""Status of a Cluster."""
|
|
19
|
+
PENDING = "pending"
|
|
20
|
+
RUNNING = "running"
|
|
21
|
+
COMPLETED = "completed"
|
|
22
|
+
FAILED = "failed"
|
|
23
|
+
DEREZZED = "derezzed"
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class Cluster:
|
|
27
|
+
"""
|
|
28
|
+
Cluster - Collection of related Blocks (feature/domain group).
|
|
29
|
+
|
|
30
|
+
Clusters represent a major feature or domain area.
|
|
31
|
+
They contain Blocks that work together toward a shared goal.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
def __init__(
|
|
35
|
+
self,
|
|
36
|
+
name: str,
|
|
37
|
+
purpose: str,
|
|
38
|
+
description: str = "",
|
|
39
|
+
):
|
|
40
|
+
self.id = str(uuid.uuid4())[:8]
|
|
41
|
+
self.name = name
|
|
42
|
+
self.purpose = purpose
|
|
43
|
+
self.description = description
|
|
44
|
+
|
|
45
|
+
self.status = ClusterStatus.PENDING
|
|
46
|
+
self.created_at = datetime.now()
|
|
47
|
+
self.started_at: Optional[datetime] = None
|
|
48
|
+
self.completed_at: Optional[datetime] = None
|
|
49
|
+
|
|
50
|
+
# Blocks in this Cluster
|
|
51
|
+
self.blocks: dict[str, Block] = {}
|
|
52
|
+
self.block_order: list[str] = [] # Execution order
|
|
53
|
+
|
|
54
|
+
# Energy
|
|
55
|
+
self.energy_pool: Optional[EnergyPool] = None
|
|
56
|
+
|
|
57
|
+
# I/O Tower checkpoints
|
|
58
|
+
self.io_tower_checkpoints: list[str] = []
|
|
59
|
+
|
|
60
|
+
def add_block(
|
|
61
|
+
self,
|
|
62
|
+
name: str,
|
|
63
|
+
purpose: str,
|
|
64
|
+
blocked_by: list[str] = None,
|
|
65
|
+
) -> Block:
|
|
66
|
+
"""Add a new Block to this Cluster."""
|
|
67
|
+
block = Block(
|
|
68
|
+
name=name,
|
|
69
|
+
purpose=purpose,
|
|
70
|
+
cluster_id=self.id,
|
|
71
|
+
blocked_by=blocked_by,
|
|
72
|
+
)
|
|
73
|
+
self.blocks[block.id] = block
|
|
74
|
+
self.block_order.append(block.id)
|
|
75
|
+
return block
|
|
76
|
+
|
|
77
|
+
def get_block(self, block_id: str) -> Optional[Block]:
|
|
78
|
+
"""Get a Block by ID."""
|
|
79
|
+
return self.blocks.get(block_id)
|
|
80
|
+
|
|
81
|
+
def get_block_by_name(self, name: str) -> Optional[Block]:
|
|
82
|
+
"""Get a Block by name."""
|
|
83
|
+
for block in self.blocks.values():
|
|
84
|
+
if block.name == name:
|
|
85
|
+
return block
|
|
86
|
+
return None
|
|
87
|
+
|
|
88
|
+
def start(self) -> None:
|
|
89
|
+
"""Start Cluster execution."""
|
|
90
|
+
self.status = ClusterStatus.RUNNING
|
|
91
|
+
self.started_at = datetime.now()
|
|
92
|
+
|
|
93
|
+
def get_runnable_blocks(self) -> list[Block]:
|
|
94
|
+
"""Get Blocks that can start (not blocked by other Blocks)."""
|
|
95
|
+
completed = {
|
|
96
|
+
b.name for b in self.blocks.values()
|
|
97
|
+
if b.status == BlockStatus.COMPLETED
|
|
98
|
+
}
|
|
99
|
+
return [
|
|
100
|
+
b for b in self.blocks.values()
|
|
101
|
+
if b.status == BlockStatus.PENDING and b.can_start(completed)
|
|
102
|
+
]
|
|
103
|
+
|
|
104
|
+
def get_running_blocks(self) -> list[Block]:
|
|
105
|
+
"""Get currently running Blocks."""
|
|
106
|
+
return [
|
|
107
|
+
b for b in self.blocks.values()
|
|
108
|
+
if b.status == BlockStatus.RUNNING
|
|
109
|
+
]
|
|
110
|
+
|
|
111
|
+
def get_completed_blocks(self) -> list[Block]:
|
|
112
|
+
"""Get completed Blocks."""
|
|
113
|
+
return [
|
|
114
|
+
b for b in self.blocks.values()
|
|
115
|
+
if b.status == BlockStatus.COMPLETED
|
|
116
|
+
]
|
|
117
|
+
|
|
118
|
+
def all_blocks_completed(self) -> bool:
|
|
119
|
+
"""Check if all Blocks are completed."""
|
|
120
|
+
return all(
|
|
121
|
+
b.status in (BlockStatus.COMPLETED, BlockStatus.DEREZZED)
|
|
122
|
+
for b in self.blocks.values()
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
def any_block_failed(self) -> bool:
|
|
126
|
+
"""Check if any Block failed."""
|
|
127
|
+
return any(
|
|
128
|
+
b.status == BlockStatus.FAILED
|
|
129
|
+
for b in self.blocks.values()
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
def complete(self) -> None:
|
|
133
|
+
"""Mark Cluster as completed."""
|
|
134
|
+
self.status = ClusterStatus.COMPLETED
|
|
135
|
+
self.completed_at = datetime.now()
|
|
136
|
+
# Return unused energy
|
|
137
|
+
if self.energy_pool:
|
|
138
|
+
self.energy_pool.return_unused()
|
|
139
|
+
|
|
140
|
+
def fail(self) -> None:
|
|
141
|
+
"""Mark Cluster as failed."""
|
|
142
|
+
self.status = ClusterStatus.FAILED
|
|
143
|
+
self.completed_at = datetime.now()
|
|
144
|
+
|
|
145
|
+
def derez(self) -> None:
|
|
146
|
+
"""Mark Cluster as derezzed."""
|
|
147
|
+
self.status = ClusterStatus.DEREZZED
|
|
148
|
+
for block in self.blocks.values():
|
|
149
|
+
block.derez()
|
|
150
|
+
|
|
151
|
+
def progress(self) -> float:
|
|
152
|
+
"""Get overall Cluster progress (0-100)."""
|
|
153
|
+
if not self.blocks:
|
|
154
|
+
return 0.0
|
|
155
|
+
total_progress = sum(b.progress() for b in self.blocks.values())
|
|
156
|
+
return total_progress / len(self.blocks)
|
|
157
|
+
|
|
158
|
+
def total_threads(self) -> int:
|
|
159
|
+
"""Get total number of Threads across all Blocks."""
|
|
160
|
+
return sum(len(b.threads) for b in self.blocks.values())
|
|
161
|
+
|
|
162
|
+
def completed_threads(self) -> int:
|
|
163
|
+
"""Get number of completed Threads."""
|
|
164
|
+
return sum(len(b.get_completed_threads()) for b in self.blocks.values())
|
|
165
|
+
|
|
166
|
+
def energy_consumed(self) -> int:
|
|
167
|
+
"""Get total energy consumed by this Cluster."""
|
|
168
|
+
return sum(b.energy_consumed() for b in self.blocks.values())
|
|
169
|
+
|
|
170
|
+
def duration(self) -> Optional[float]:
|
|
171
|
+
"""Get execution duration in seconds."""
|
|
172
|
+
if not self.started_at:
|
|
173
|
+
return None
|
|
174
|
+
end = self.completed_at or datetime.now()
|
|
175
|
+
return (end - self.started_at).total_seconds()
|
|
176
|
+
|
|
177
|
+
def add_io_checkpoint(self, reason: str) -> None:
|
|
178
|
+
"""Add an I/O Tower checkpoint."""
|
|
179
|
+
self.io_tower_checkpoints.append(reason)
|
|
180
|
+
|
|
181
|
+
def display_full(self) -> str:
|
|
182
|
+
"""Get full display for status views."""
|
|
183
|
+
energy_display = ""
|
|
184
|
+
if self.energy_pool:
|
|
185
|
+
energy_display = f"Energy: {self.energy_pool.remaining}"
|
|
186
|
+
|
|
187
|
+
lines = [
|
|
188
|
+
"╔" + "═" * 77 + "╗",
|
|
189
|
+
f"║ CLUSTER: {self.name:<50} {energy_display:>15} ║",
|
|
190
|
+
"╠" + "═" * 77 + "╣",
|
|
191
|
+
"║" + " " * 77 + "║",
|
|
192
|
+
]
|
|
193
|
+
|
|
194
|
+
for block_id in self.block_order:
|
|
195
|
+
block = self.blocks[block_id]
|
|
196
|
+
for line in block.display_box().split("\n"):
|
|
197
|
+
lines.append(f"║ {line:<75}║")
|
|
198
|
+
lines.append("║" + " " * 77 + "║")
|
|
199
|
+
|
|
200
|
+
# I/O Tower checkpoints
|
|
201
|
+
for checkpoint in self.io_tower_checkpoints:
|
|
202
|
+
lines.append(f"║ ⛯ I/O TOWER: {checkpoint:<61}║")
|
|
203
|
+
|
|
204
|
+
lines.append("║" + " " * 77 + "║")
|
|
205
|
+
lines.append("╚" + "═" * 77 + "╝")
|
|
206
|
+
|
|
207
|
+
return "\n".join(lines)
|
|
208
|
+
|
|
209
|
+
def to_dict(self) -> dict:
|
|
210
|
+
"""Serialize Cluster to dictionary."""
|
|
211
|
+
return {
|
|
212
|
+
"id": self.id,
|
|
213
|
+
"name": self.name,
|
|
214
|
+
"purpose": self.purpose,
|
|
215
|
+
"description": self.description,
|
|
216
|
+
"status": self.status.value,
|
|
217
|
+
"progress": self.progress(),
|
|
218
|
+
"blocks": [b.to_dict() for b in self.blocks.values()],
|
|
219
|
+
"io_tower_checkpoints": self.io_tower_checkpoints,
|
|
220
|
+
"total_threads": self.total_threads(),
|
|
221
|
+
"completed_threads": self.completed_threads(),
|
|
222
|
+
"created_at": self.created_at.isoformat(),
|
|
223
|
+
"started_at": self.started_at.isoformat() if self.started_at else None,
|
|
224
|
+
"completed_at": self.completed_at.isoformat() if self.completed_at else None,
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
def __repr__(self) -> str:
|
|
228
|
+
return f"Cluster(id={self.id}, name={self.name}, blocks={len(self.blocks)})"
|
package/core/disc.py
ADDED
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Identity Disc - Carries a Program's memory and decisions.
|
|
3
|
+
|
|
4
|
+
"Everything you do or learn will be imprinted on this disc."
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from dataclasses import dataclass, field
|
|
8
|
+
from datetime import datetime
|
|
9
|
+
from typing import Optional, Any
|
|
10
|
+
from enum import Enum
|
|
11
|
+
import json
|
|
12
|
+
import uuid
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class DiscStatus(Enum):
|
|
16
|
+
"""Status of an Identity Disc."""
|
|
17
|
+
ACTIVE = "active"
|
|
18
|
+
ARCHIVED = "archived"
|
|
19
|
+
DEREZZED = "derezzed"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass
|
|
23
|
+
class Decision:
|
|
24
|
+
"""A recorded decision made by a Program."""
|
|
25
|
+
timestamp: datetime
|
|
26
|
+
choice: str
|
|
27
|
+
reasoning: str
|
|
28
|
+
alternatives: list[str] = field(default_factory=list)
|
|
29
|
+
|
|
30
|
+
def to_dict(self) -> dict:
|
|
31
|
+
return {
|
|
32
|
+
"timestamp": self.timestamp.isoformat(),
|
|
33
|
+
"choice": self.choice,
|
|
34
|
+
"reasoning": self.reasoning,
|
|
35
|
+
"alternatives": self.alternatives,
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@dataclass
|
|
40
|
+
class Discovery:
|
|
41
|
+
"""Something learned during execution."""
|
|
42
|
+
timestamp: datetime
|
|
43
|
+
finding: str
|
|
44
|
+
source: str
|
|
45
|
+
relevance: str = "medium" # low, medium, high, critical
|
|
46
|
+
|
|
47
|
+
def to_dict(self) -> dict:
|
|
48
|
+
return {
|
|
49
|
+
"timestamp": self.timestamp.isoformat(),
|
|
50
|
+
"finding": self.finding,
|
|
51
|
+
"source": self.source,
|
|
52
|
+
"relevance": self.relevance,
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@dataclass
|
|
57
|
+
class Artifact:
|
|
58
|
+
"""A file or resource created/modified by a Program."""
|
|
59
|
+
path: str
|
|
60
|
+
action: str # created, modified, deleted
|
|
61
|
+
timestamp: datetime
|
|
62
|
+
description: str = ""
|
|
63
|
+
|
|
64
|
+
def to_dict(self) -> dict:
|
|
65
|
+
return {
|
|
66
|
+
"path": self.path,
|
|
67
|
+
"action": self.action,
|
|
68
|
+
"timestamp": self.timestamp.isoformat(),
|
|
69
|
+
"description": self.description,
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class IdentityDisc:
|
|
74
|
+
"""
|
|
75
|
+
The Identity Disc - carries a Program's memory and decisions.
|
|
76
|
+
|
|
77
|
+
Each Program on The Grid carries an Identity Disc containing:
|
|
78
|
+
- Purpose: What the Program is trying to accomplish
|
|
79
|
+
- Constraints: Boundaries it must respect
|
|
80
|
+
- Decisions: Choices made and why
|
|
81
|
+
- Discoveries: Things learned during execution
|
|
82
|
+
- Artifacts: Files created/modified
|
|
83
|
+
- Lineage: Parent/child relationships for fission
|
|
84
|
+
"""
|
|
85
|
+
|
|
86
|
+
def __init__(
|
|
87
|
+
self,
|
|
88
|
+
program_id: str,
|
|
89
|
+
purpose: str = "",
|
|
90
|
+
parent_disc: Optional["IdentityDisc"] = None,
|
|
91
|
+
):
|
|
92
|
+
self.id = str(uuid.uuid4())[:8]
|
|
93
|
+
self.program_id = program_id
|
|
94
|
+
self.created_at = datetime.now()
|
|
95
|
+
self.status = DiscStatus.ACTIVE
|
|
96
|
+
|
|
97
|
+
# Core identity
|
|
98
|
+
self.purpose = purpose
|
|
99
|
+
self.constraints: list[str] = []
|
|
100
|
+
|
|
101
|
+
# Memory
|
|
102
|
+
self.decisions: list[Decision] = []
|
|
103
|
+
self.discoveries: list[Discovery] = []
|
|
104
|
+
self.artifacts: list[Artifact] = []
|
|
105
|
+
|
|
106
|
+
# Lineage (for fission)
|
|
107
|
+
self.parent_disc = parent_disc
|
|
108
|
+
self.child_discs: list["IdentityDisc"] = []
|
|
109
|
+
self.depth = 0 if parent_disc is None else parent_disc.depth + 1
|
|
110
|
+
|
|
111
|
+
# Energy tracking
|
|
112
|
+
self.energy_allocated = 0
|
|
113
|
+
self.energy_consumed = 0
|
|
114
|
+
|
|
115
|
+
# Inherit relevant context from parent
|
|
116
|
+
if parent_disc:
|
|
117
|
+
self._inherit_from_parent(parent_disc)
|
|
118
|
+
|
|
119
|
+
def _inherit_from_parent(self, parent: "IdentityDisc") -> None:
|
|
120
|
+
"""Inherit relevant context from parent disc."""
|
|
121
|
+
# Inherit constraints
|
|
122
|
+
self.constraints = parent.constraints.copy()
|
|
123
|
+
|
|
124
|
+
# Inherit high-relevance discoveries
|
|
125
|
+
for discovery in parent.discoveries:
|
|
126
|
+
if discovery.relevance in ("high", "critical"):
|
|
127
|
+
self.discoveries.append(discovery)
|
|
128
|
+
|
|
129
|
+
# Register as child of parent
|
|
130
|
+
parent.child_discs.append(self)
|
|
131
|
+
|
|
132
|
+
def record_decision(
|
|
133
|
+
self,
|
|
134
|
+
choice: str,
|
|
135
|
+
reasoning: str,
|
|
136
|
+
alternatives: list[str] = None,
|
|
137
|
+
) -> Decision:
|
|
138
|
+
"""Record a decision made by the Program."""
|
|
139
|
+
decision = Decision(
|
|
140
|
+
timestamp=datetime.now(),
|
|
141
|
+
choice=choice,
|
|
142
|
+
reasoning=reasoning,
|
|
143
|
+
alternatives=alternatives or [],
|
|
144
|
+
)
|
|
145
|
+
self.decisions.append(decision)
|
|
146
|
+
return decision
|
|
147
|
+
|
|
148
|
+
def record_discovery(
|
|
149
|
+
self,
|
|
150
|
+
finding: str,
|
|
151
|
+
source: str,
|
|
152
|
+
relevance: str = "medium",
|
|
153
|
+
) -> Discovery:
|
|
154
|
+
"""Record something learned during execution."""
|
|
155
|
+
discovery = Discovery(
|
|
156
|
+
timestamp=datetime.now(),
|
|
157
|
+
finding=finding,
|
|
158
|
+
source=source,
|
|
159
|
+
relevance=relevance,
|
|
160
|
+
)
|
|
161
|
+
self.discoveries.append(discovery)
|
|
162
|
+
return discovery
|
|
163
|
+
|
|
164
|
+
def record_artifact(
|
|
165
|
+
self,
|
|
166
|
+
path: str,
|
|
167
|
+
action: str,
|
|
168
|
+
description: str = "",
|
|
169
|
+
) -> Artifact:
|
|
170
|
+
"""Record a file/resource created or modified."""
|
|
171
|
+
artifact = Artifact(
|
|
172
|
+
path=path,
|
|
173
|
+
action=action,
|
|
174
|
+
timestamp=datetime.now(),
|
|
175
|
+
description=description,
|
|
176
|
+
)
|
|
177
|
+
self.artifacts.append(artifact)
|
|
178
|
+
return artifact
|
|
179
|
+
|
|
180
|
+
def add_constraint(self, constraint: str) -> None:
|
|
181
|
+
"""Add a constraint the Program must respect."""
|
|
182
|
+
if constraint not in self.constraints:
|
|
183
|
+
self.constraints.append(constraint)
|
|
184
|
+
|
|
185
|
+
def consume_energy(self, amount: int) -> bool:
|
|
186
|
+
"""Consume energy. Returns False if insufficient energy."""
|
|
187
|
+
if self.energy_consumed + amount > self.energy_allocated:
|
|
188
|
+
return False
|
|
189
|
+
self.energy_consumed += amount
|
|
190
|
+
return True
|
|
191
|
+
|
|
192
|
+
def energy_remaining(self) -> int:
|
|
193
|
+
"""Get remaining energy."""
|
|
194
|
+
return self.energy_allocated - self.energy_consumed
|
|
195
|
+
|
|
196
|
+
def energy_percentage(self) -> float:
|
|
197
|
+
"""Get energy as percentage of allocated."""
|
|
198
|
+
if self.energy_allocated == 0:
|
|
199
|
+
return 0.0
|
|
200
|
+
return (self.energy_remaining() / self.energy_allocated) * 100
|
|
201
|
+
|
|
202
|
+
def derez(self) -> None:
|
|
203
|
+
"""Mark disc as derezzed (completed/archived)."""
|
|
204
|
+
self.status = DiscStatus.DEREZZED
|
|
205
|
+
|
|
206
|
+
def get_lineage(self) -> list[str]:
|
|
207
|
+
"""Get the lineage chain of program IDs."""
|
|
208
|
+
lineage = [self.program_id]
|
|
209
|
+
current = self.parent_disc
|
|
210
|
+
while current:
|
|
211
|
+
lineage.insert(0, current.program_id)
|
|
212
|
+
current = current.parent_disc
|
|
213
|
+
return lineage
|
|
214
|
+
|
|
215
|
+
def to_dict(self) -> dict:
|
|
216
|
+
"""Serialize disc to dictionary."""
|
|
217
|
+
return {
|
|
218
|
+
"id": self.id,
|
|
219
|
+
"program_id": self.program_id,
|
|
220
|
+
"created_at": self.created_at.isoformat(),
|
|
221
|
+
"status": self.status.value,
|
|
222
|
+
"purpose": self.purpose,
|
|
223
|
+
"constraints": self.constraints,
|
|
224
|
+
"decisions": [d.to_dict() for d in self.decisions],
|
|
225
|
+
"discoveries": [d.to_dict() for d in self.discoveries],
|
|
226
|
+
"artifacts": [a.to_dict() for a in self.artifacts],
|
|
227
|
+
"depth": self.depth,
|
|
228
|
+
"lineage": self.get_lineage(),
|
|
229
|
+
"energy_allocated": self.energy_allocated,
|
|
230
|
+
"energy_consumed": self.energy_consumed,
|
|
231
|
+
"child_count": len(self.child_discs),
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
def to_json(self) -> str:
|
|
235
|
+
"""Serialize disc to JSON."""
|
|
236
|
+
return json.dumps(self.to_dict(), indent=2)
|
|
237
|
+
|
|
238
|
+
def summary(self) -> str:
|
|
239
|
+
"""Get a brief summary of the disc contents."""
|
|
240
|
+
lines = [
|
|
241
|
+
f"Identity Disc [{self.id}]",
|
|
242
|
+
f" Program: {self.program_id}",
|
|
243
|
+
f" Purpose: {self.purpose}",
|
|
244
|
+
f" Depth: {self.depth}",
|
|
245
|
+
f" Energy: {self.energy_remaining()}/{self.energy_allocated}",
|
|
246
|
+
f" Decisions: {len(self.decisions)}",
|
|
247
|
+
f" Discoveries: {len(self.discoveries)}",
|
|
248
|
+
f" Artifacts: {len(self.artifacts)}",
|
|
249
|
+
f" Children: {len(self.child_discs)}",
|
|
250
|
+
]
|
|
251
|
+
return "\n".join(lines)
|
|
252
|
+
|
|
253
|
+
def __repr__(self) -> str:
|
|
254
|
+
return f"IdentityDisc(id={self.id}, program={self.program_id}, depth={self.depth})"
|