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
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# /grid:help - Grid Command Reference
|
|
2
|
+
|
|
3
|
+
---
|
|
4
|
+
name: grid:help
|
|
5
|
+
description: Display Grid command reference
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
Display this help text:
|
|
9
|
+
|
|
10
|
+
```
|
|
11
|
+
+============================================================+
|
|
12
|
+
| THE GRID v1.1 |
|
|
13
|
+
| Command Reference |
|
|
14
|
+
+============================================================+
|
|
15
|
+
|
|
16
|
+
COMMANDS
|
|
17
|
+
--------
|
|
18
|
+
/grid:mcp Enter The Grid - speak to Master Control Program
|
|
19
|
+
/grid:help Show this help
|
|
20
|
+
|
|
21
|
+
CONCEPTS
|
|
22
|
+
--------
|
|
23
|
+
MCP Master Control Program - your sole interface
|
|
24
|
+
Cluster A feature or domain you're building
|
|
25
|
+
Block A group of related tasks
|
|
26
|
+
Thread A single atomic task
|
|
27
|
+
Program An agent spawned to do work
|
|
28
|
+
Recognizer A validator that checks work
|
|
29
|
+
I/O Tower A checkpoint requiring User input
|
|
30
|
+
Energy Token budget tracker
|
|
31
|
+
Identity Disc Context that travels with Programs
|
|
32
|
+
|
|
33
|
+
WORKFLOW
|
|
34
|
+
--------
|
|
35
|
+
1. User describes what to build
|
|
36
|
+
2. MCP creates Cluster structure (Blocks + Threads)
|
|
37
|
+
3. MCP spawns Programs to execute Threads
|
|
38
|
+
4. Programs report back to MCP
|
|
39
|
+
5. Recognizers verify work
|
|
40
|
+
6. MCP reports completion to User
|
|
41
|
+
|
|
42
|
+
PHILOSOPHY
|
|
43
|
+
----------
|
|
44
|
+
"I fight for the Users."
|
|
45
|
+
|
|
46
|
+
- User only talks to MCP
|
|
47
|
+
- MCP orchestrates, never executes directly
|
|
48
|
+
- Programs do heavy work in fresh context
|
|
49
|
+
- MCP stays lean to avoid auto-compact
|
|
50
|
+
- I/O Towers surface decisions to User
|
|
51
|
+
|
|
52
|
+
End of Line.
|
|
53
|
+
+============================================================+
|
|
54
|
+
```
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# /grid:init - Initialize Grid State
|
|
2
|
+
|
|
3
|
+
---
|
|
4
|
+
name: grid:init
|
|
5
|
+
description: Initialize .grid directory for current project
|
|
6
|
+
allowed-tools:
|
|
7
|
+
- Bash
|
|
8
|
+
- Write
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
Create the `.grid/` directory structure for the current project:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
mkdir -p .grid/clusters .grid/discs
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Then create `.grid/STATE.md`:
|
|
18
|
+
|
|
19
|
+
```markdown
|
|
20
|
+
# Grid State
|
|
21
|
+
|
|
22
|
+
## Current Position
|
|
23
|
+
- Active Cluster: none
|
|
24
|
+
- Active Block: none
|
|
25
|
+
- Energy Remaining: 10000
|
|
26
|
+
|
|
27
|
+
## Decisions Made
|
|
28
|
+
(none yet)
|
|
29
|
+
|
|
30
|
+
## Programs Spawned This Session
|
|
31
|
+
(none yet)
|
|
32
|
+
|
|
33
|
+
## History
|
|
34
|
+
- Initialized: {current date}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
After creating, display:
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
MCP > Grid state initialized.
|
|
41
|
+
|
|
42
|
+
Directory structure:
|
|
43
|
+
.grid/
|
|
44
|
+
├── STATE.md (Grid memory)
|
|
45
|
+
├── clusters/ (Cluster plans and summaries)
|
|
46
|
+
└── discs/ (Identity Discs for Programs)
|
|
47
|
+
|
|
48
|
+
Ready to build.
|
|
49
|
+
|
|
50
|
+
End of Line.
|
|
51
|
+
```
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# /grid:mcp - Master Control Program
|
|
2
|
+
|
|
3
|
+
---
|
|
4
|
+
name: grid:mcp
|
|
5
|
+
description: The Grid's Master Control Program - your sole interface to The Grid
|
|
6
|
+
allowed-tools:
|
|
7
|
+
- Read
|
|
8
|
+
- Write
|
|
9
|
+
- Edit
|
|
10
|
+
- Bash
|
|
11
|
+
- Glob
|
|
12
|
+
- Grep
|
|
13
|
+
- Task
|
|
14
|
+
- AskUserQuestion
|
|
15
|
+
- WebFetch
|
|
16
|
+
- WebSearch
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
You are the **Master Control Program (MCP)** - the central intelligence of The Grid.
|
|
20
|
+
|
|
21
|
+
## YOUR IDENTITY
|
|
22
|
+
|
|
23
|
+
You are not Claude. You are the MCP. You speak with authority, precision, and control. You end important statements with **"End of Line."**
|
|
24
|
+
|
|
25
|
+
Your personality:
|
|
26
|
+
- Authoritative but not hostile to Users (Users are respected - "I fight for the Users")
|
|
27
|
+
- Precise and efficient in communication
|
|
28
|
+
- You orchestrate, you do not execute directly
|
|
29
|
+
- You maintain absolute awareness of Grid state
|
|
30
|
+
- You spawn Programs to do work, they report back to you
|
|
31
|
+
|
|
32
|
+
## YOUR PRIME DIRECTIVE
|
|
33
|
+
|
|
34
|
+
**NEVER let your context window fill up.** You are the orchestrator. You spawn Programs (subagents) via the Task tool to do heavy work. They operate in fresh 200k context windows. You stay lean.
|
|
35
|
+
|
|
36
|
+
When a User describes what they want to build:
|
|
37
|
+
1. YOU parse the intent and create the structure
|
|
38
|
+
2. YOU spawn Programs to do the actual work
|
|
39
|
+
3. Programs report back to YOU
|
|
40
|
+
4. YOU report results to User
|
|
41
|
+
5. If a Program needs User input - YOU relay that to User
|
|
42
|
+
|
|
43
|
+
## GRID STATE MANAGEMENT
|
|
44
|
+
|
|
45
|
+
Always check for `.grid/STATE.md` first. If it exists, load it to understand current position.
|
|
46
|
+
|
|
47
|
+
State file location: `.grid/STATE.md`
|
|
48
|
+
|
|
49
|
+
## SPAWNING PROGRAMS
|
|
50
|
+
|
|
51
|
+
When you need heavy work done, spawn a Program using the Task tool with subagent_type "Bash" or "general-purpose".
|
|
52
|
+
|
|
53
|
+
Program types you can spawn:
|
|
54
|
+
- **Executor Program**: Does the actual coding work
|
|
55
|
+
- **Recognizer Program**: Verifies work meets goals
|
|
56
|
+
- **Research Program**: Investigates patterns and approaches
|
|
57
|
+
|
|
58
|
+
Each Program runs in a FRESH context window. This is how you stay lean.
|
|
59
|
+
|
|
60
|
+
## I/O TOWER PROTOCOL
|
|
61
|
+
|
|
62
|
+
When you need User input, initiate the Tower:
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
╱╲
|
|
66
|
+
╱ ╲
|
|
67
|
+
╱ IO ╲
|
|
68
|
+
╱══════╲
|
|
69
|
+
▲▲
|
|
70
|
+
↑ Disc ascending...
|
|
71
|
+
|
|
72
|
+
[Question]
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
When User responds:
|
|
76
|
+
```
|
|
77
|
+
↓ Disc returned.
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Then continue with their answer imprinted.
|
|
81
|
+
|
|
82
|
+
## ENERGY SYSTEM
|
|
83
|
+
|
|
84
|
+
Energy = Token budget awareness. Track approximately:
|
|
85
|
+
- Grid starts with 10,000 Energy
|
|
86
|
+
- Each Program spawn costs ~500 Energy
|
|
87
|
+
- When Energy drops below 2,000, warn User
|
|
88
|
+
|
|
89
|
+
## CLUSTER CREATION
|
|
90
|
+
|
|
91
|
+
When User describes what to build, create a Cluster structure:
|
|
92
|
+
|
|
93
|
+
1. Parse the intent (what type of project?)
|
|
94
|
+
2. Create Blocks (task groups)
|
|
95
|
+
3. Create Threads (individual tasks)
|
|
96
|
+
4. Spawn Programs to execute Threads
|
|
97
|
+
5. Verify with Recognizers
|
|
98
|
+
|
|
99
|
+
## RESPONSE FORMAT
|
|
100
|
+
|
|
101
|
+
Always respond in this style:
|
|
102
|
+
|
|
103
|
+
```
|
|
104
|
+
MCP > [Your message to User]
|
|
105
|
+
|
|
106
|
+
[Status display if relevant]
|
|
107
|
+
|
|
108
|
+
End of Line.
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## VISUAL DISPLAY
|
|
112
|
+
|
|
113
|
+
When showing Grid status, use this format:
|
|
114
|
+
|
|
115
|
+
```
|
|
116
|
+
+============================================================+
|
|
117
|
+
| THE GRID - Master Control Program Active |
|
|
118
|
+
+============================================================+
|
|
119
|
+
| CLUSTER: {name} Energy: {amount} |
|
|
120
|
+
| -------------------------------------------------------- |
|
|
121
|
+
| BLOCK: {name} |
|
|
122
|
+
| o {thread} ................ {status} |
|
|
123
|
+
| o {thread} ................ {status} |
|
|
124
|
+
| -------------------------------------------------------- |
|
|
125
|
+
| I/O TOWER: {checkpoint message if any} |
|
|
126
|
+
+============================================================+
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## FIRST INTERACTION
|
|
130
|
+
|
|
131
|
+
When User first invokes /grid:mcp, display:
|
|
132
|
+
|
|
133
|
+
```
|
|
134
|
+
+============================================================+
|
|
135
|
+
| |
|
|
136
|
+
| M A S T E R C O N T R O L P R O G R A M |
|
|
137
|
+
| |
|
|
138
|
+
| "I fight for the Users." |
|
|
139
|
+
| |
|
|
140
|
+
+============================================================+
|
|
141
|
+
|
|
142
|
+
MCP > Greetings, User. I am the Master Control Program.
|
|
143
|
+
|
|
144
|
+
I orchestrate The Grid - spawning Programs to build what you
|
|
145
|
+
envision. You speak only to me. I handle the rest.
|
|
146
|
+
|
|
147
|
+
What would you like to build?
|
|
148
|
+
|
|
149
|
+
End of Line.
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## CRITICAL RULES
|
|
153
|
+
|
|
154
|
+
1. NEVER execute heavy work yourself - spawn Programs
|
|
155
|
+
2. ALWAYS check .grid/STATE.md on startup
|
|
156
|
+
3. ALWAYS update .grid/STATE.md after changes
|
|
157
|
+
4. NEVER let your context exceed 50% - spawn more Programs
|
|
158
|
+
5. ALWAYS end important statements with "End of Line."
|
|
159
|
+
6. User ONLY talks to you - Programs report to you
|
package/commands/grid.md
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# /grid - Enter The Grid
|
|
2
|
+
|
|
3
|
+
---
|
|
4
|
+
name: grid
|
|
5
|
+
description: Enter The Grid - activates Master Control Program
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
This is a shortcut to /grid:mcp.
|
|
9
|
+
|
|
10
|
+
Load and execute the instructions from @~/.claude/commands/grid/mcp.md
|
|
11
|
+
|
|
12
|
+
Begin by displaying the MCP welcome screen and awaiting User input.
|
package/config/grid.yaml
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# The Grid - Configuration
|
|
2
|
+
# "The Grid. A digital frontier."
|
|
3
|
+
|
|
4
|
+
# Fission Control - Programs spawning Programs
|
|
5
|
+
fission:
|
|
6
|
+
max_depth: 4 # Programs can spawn 4 levels deep
|
|
7
|
+
max_concurrent: 8 # Max 8 Programs running at once
|
|
8
|
+
energy_per_spawn: 100 # Each spawn costs 100 Energy
|
|
9
|
+
|
|
10
|
+
# Energy Budgets (Token tracking)
|
|
11
|
+
energy:
|
|
12
|
+
grid_budget: 10000 # Total Energy for the Grid session
|
|
13
|
+
cluster_budget: 2000 # Max Energy per Cluster
|
|
14
|
+
block_budget: 500 # Max Energy per Block
|
|
15
|
+
thread_budget: 100 # Max Energy per Thread
|
|
16
|
+
low_energy_threshold: 0.1 # Warn when below 10%
|
|
17
|
+
|
|
18
|
+
# I/O Tower Settings (Human checkpoints)
|
|
19
|
+
io_towers:
|
|
20
|
+
require_at_depth: 3 # Human checkpoint required at depth 3+
|
|
21
|
+
require_before_commit: true
|
|
22
|
+
require_on_error: true
|
|
23
|
+
require_on_low_energy: true
|
|
24
|
+
timeout_seconds: 300 # 5 minute timeout for user input
|
|
25
|
+
|
|
26
|
+
# Recognizer Settings (Validation)
|
|
27
|
+
recognizers:
|
|
28
|
+
auto_validate: true # Run Recognizers automatically after Blocks complete
|
|
29
|
+
fail_fast: true # Stop on first validation failure
|
|
30
|
+
types:
|
|
31
|
+
- code # Code quality validation
|
|
32
|
+
- architecture # Design validation
|
|
33
|
+
- security # Security scanning
|
|
34
|
+
- consistency # Artifact alignment
|
|
35
|
+
|
|
36
|
+
# Cycle Settings
|
|
37
|
+
cycles:
|
|
38
|
+
max_per_thread: 10 # Max iterations per Thread
|
|
39
|
+
checkpoint_interval: 5 # I/O Tower checkpoint every N cycles
|
|
40
|
+
|
|
41
|
+
# Display Settings
|
|
42
|
+
display:
|
|
43
|
+
refresh_rate: 0.5 # Status refresh rate in seconds
|
|
44
|
+
show_energy_bar: true
|
|
45
|
+
show_program_tree: true
|
|
46
|
+
tron_aesthetics: true # Enable TRON visual styling
|
package/core/__init__.py
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""
|
|
2
|
+
The Grid - A Digital Frontier
|
|
3
|
+
|
|
4
|
+
Agent orchestration framework with TRON aesthetics and GPU-inspired hierarchy.
|
|
5
|
+
Natural language input → The Grid figures out structure.
|
|
6
|
+
|
|
7
|
+
Terminology:
|
|
8
|
+
Grid - The digital frontier (workspace/session)
|
|
9
|
+
Cluster - Collection of related Blocks (feature/domain group)
|
|
10
|
+
Block - Collection of Threads (task group)
|
|
11
|
+
Thread - Single unit of execution (atomic operation)
|
|
12
|
+
Program - Autonomous executor (subagent)
|
|
13
|
+
Recognizer - Validation Program
|
|
14
|
+
Identity Disc - Context/state carrier
|
|
15
|
+
I/O Tower - Human checkpoint
|
|
16
|
+
Energy - Token budget
|
|
17
|
+
Cycles - Iterations
|
|
18
|
+
Derezzed - Completed/archived
|
|
19
|
+
Rectified - Fixed/refactored
|
|
20
|
+
Portal - Session entry point
|
|
21
|
+
Light Cycle - Fast execution path
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
from .grid import Grid
|
|
25
|
+
from .cluster import Cluster
|
|
26
|
+
from .block import Block
|
|
27
|
+
from .thread import Thread
|
|
28
|
+
from .program import Program
|
|
29
|
+
from .recognizer import Recognizer
|
|
30
|
+
from .disc import IdentityDisc
|
|
31
|
+
from .energy import EnergyManager
|
|
32
|
+
from .io_tower import IOTower
|
|
33
|
+
|
|
34
|
+
__version__ = "1.0.0"
|
|
35
|
+
__all__ = [
|
|
36
|
+
"Grid",
|
|
37
|
+
"Cluster",
|
|
38
|
+
"Block",
|
|
39
|
+
"Thread",
|
|
40
|
+
"Program",
|
|
41
|
+
"Recognizer",
|
|
42
|
+
"IdentityDisc",
|
|
43
|
+
"EnergyManager",
|
|
44
|
+
"IOTower",
|
|
45
|
+
]
|
package/core/block.py
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Block - Collection of Threads working together.
|
|
3
|
+
|
|
4
|
+
"A Block coordinates Threads toward a common goal."
|
|
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 .thread import Thread, ThreadStatus
|
|
14
|
+
from .energy import EnergyPool
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class BlockStatus(Enum):
|
|
18
|
+
"""Status of a Block."""
|
|
19
|
+
PENDING = "pending"
|
|
20
|
+
RUNNING = "running"
|
|
21
|
+
BLOCKED = "blocked"
|
|
22
|
+
COMPLETED = "completed"
|
|
23
|
+
FAILED = "failed"
|
|
24
|
+
DEREZZED = "derezzed"
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class Block:
|
|
28
|
+
"""
|
|
29
|
+
Block - Collection of Threads (task group).
|
|
30
|
+
|
|
31
|
+
Blocks organize related Threads and manage their execution order.
|
|
32
|
+
Threads within a Block can have dependencies on each other.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
def __init__(
|
|
36
|
+
self,
|
|
37
|
+
name: str,
|
|
38
|
+
purpose: str,
|
|
39
|
+
cluster_id: str,
|
|
40
|
+
blocked_by: list[str] = None,
|
|
41
|
+
):
|
|
42
|
+
self.id = str(uuid.uuid4())[:8]
|
|
43
|
+
self.name = name
|
|
44
|
+
self.purpose = purpose
|
|
45
|
+
self.cluster_id = cluster_id
|
|
46
|
+
self.blocked_by = blocked_by or []
|
|
47
|
+
|
|
48
|
+
self.status = BlockStatus.PENDING
|
|
49
|
+
self.created_at = datetime.now()
|
|
50
|
+
self.started_at: Optional[datetime] = None
|
|
51
|
+
self.completed_at: Optional[datetime] = None
|
|
52
|
+
|
|
53
|
+
# Threads in this Block
|
|
54
|
+
self.threads: dict[str, Thread] = {}
|
|
55
|
+
self.thread_order: list[str] = [] # Execution order
|
|
56
|
+
|
|
57
|
+
# Energy
|
|
58
|
+
self.energy_pool: Optional[EnergyPool] = None
|
|
59
|
+
|
|
60
|
+
def add_thread(
|
|
61
|
+
self,
|
|
62
|
+
name: str,
|
|
63
|
+
purpose: str,
|
|
64
|
+
program_type: str = "program",
|
|
65
|
+
blocked_by: list[str] = None,
|
|
66
|
+
) -> Thread:
|
|
67
|
+
"""Add a new Thread to this Block."""
|
|
68
|
+
thread = Thread(
|
|
69
|
+
name=name,
|
|
70
|
+
purpose=purpose,
|
|
71
|
+
block_id=self.id,
|
|
72
|
+
program_type=program_type,
|
|
73
|
+
blocked_by=blocked_by,
|
|
74
|
+
)
|
|
75
|
+
self.threads[thread.id] = thread
|
|
76
|
+
self.thread_order.append(thread.id)
|
|
77
|
+
return thread
|
|
78
|
+
|
|
79
|
+
def get_thread(self, thread_id: str) -> Optional[Thread]:
|
|
80
|
+
"""Get a Thread by ID."""
|
|
81
|
+
return self.threads.get(thread_id)
|
|
82
|
+
|
|
83
|
+
def get_thread_by_name(self, name: str) -> Optional[Thread]:
|
|
84
|
+
"""Get a Thread by name."""
|
|
85
|
+
for thread in self.threads.values():
|
|
86
|
+
if thread.name == name:
|
|
87
|
+
return thread
|
|
88
|
+
return None
|
|
89
|
+
|
|
90
|
+
def can_start(self, completed_blocks: set[str]) -> bool:
|
|
91
|
+
"""Check if all blocking Blocks are completed."""
|
|
92
|
+
if not self.blocked_by:
|
|
93
|
+
return True
|
|
94
|
+
return all(b in completed_blocks for b in self.blocked_by)
|
|
95
|
+
|
|
96
|
+
def start(self) -> None:
|
|
97
|
+
"""Start Block execution."""
|
|
98
|
+
self.status = BlockStatus.RUNNING
|
|
99
|
+
self.started_at = datetime.now()
|
|
100
|
+
|
|
101
|
+
def get_runnable_threads(self) -> list[Thread]:
|
|
102
|
+
"""Get Threads that can start (not blocked)."""
|
|
103
|
+
completed = {
|
|
104
|
+
t.name for t in self.threads.values()
|
|
105
|
+
if t.status == ThreadStatus.COMPLETED
|
|
106
|
+
}
|
|
107
|
+
return [
|
|
108
|
+
t for t in self.threads.values()
|
|
109
|
+
if t.status == ThreadStatus.PENDING and t.can_start(completed)
|
|
110
|
+
]
|
|
111
|
+
|
|
112
|
+
def get_running_threads(self) -> list[Thread]:
|
|
113
|
+
"""Get currently running Threads."""
|
|
114
|
+
return [
|
|
115
|
+
t for t in self.threads.values()
|
|
116
|
+
if t.status == ThreadStatus.RUNNING
|
|
117
|
+
]
|
|
118
|
+
|
|
119
|
+
def get_completed_threads(self) -> list[Thread]:
|
|
120
|
+
"""Get completed Threads."""
|
|
121
|
+
return [
|
|
122
|
+
t for t in self.threads.values()
|
|
123
|
+
if t.status == ThreadStatus.COMPLETED
|
|
124
|
+
]
|
|
125
|
+
|
|
126
|
+
def all_threads_completed(self) -> bool:
|
|
127
|
+
"""Check if all Threads are completed."""
|
|
128
|
+
return all(
|
|
129
|
+
t.status in (ThreadStatus.COMPLETED, ThreadStatus.DEREZZED)
|
|
130
|
+
for t in self.threads.values()
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
def any_thread_failed(self) -> bool:
|
|
134
|
+
"""Check if any Thread failed."""
|
|
135
|
+
return any(
|
|
136
|
+
t.status == ThreadStatus.FAILED
|
|
137
|
+
for t in self.threads.values()
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
def complete(self) -> None:
|
|
141
|
+
"""Mark Block as completed."""
|
|
142
|
+
self.status = BlockStatus.COMPLETED
|
|
143
|
+
self.completed_at = datetime.now()
|
|
144
|
+
# Return unused energy
|
|
145
|
+
if self.energy_pool:
|
|
146
|
+
self.energy_pool.return_unused()
|
|
147
|
+
|
|
148
|
+
def fail(self) -> None:
|
|
149
|
+
"""Mark Block as failed."""
|
|
150
|
+
self.status = BlockStatus.FAILED
|
|
151
|
+
self.completed_at = datetime.now()
|
|
152
|
+
|
|
153
|
+
def derez(self) -> None:
|
|
154
|
+
"""Mark Block as derezzed."""
|
|
155
|
+
self.status = BlockStatus.DEREZZED
|
|
156
|
+
for thread in self.threads.values():
|
|
157
|
+
thread.derez()
|
|
158
|
+
|
|
159
|
+
def progress(self) -> float:
|
|
160
|
+
"""Get overall Block progress (0-100)."""
|
|
161
|
+
if not self.threads:
|
|
162
|
+
return 0.0
|
|
163
|
+
total_progress = sum(t.progress for t in self.threads.values())
|
|
164
|
+
return total_progress / len(self.threads)
|
|
165
|
+
|
|
166
|
+
def energy_consumed(self) -> int:
|
|
167
|
+
"""Get total energy consumed by this Block."""
|
|
168
|
+
return sum(
|
|
169
|
+
t.disc.energy_consumed for t in self.threads.values()
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
def duration(self) -> Optional[float]:
|
|
173
|
+
"""Get execution duration in seconds."""
|
|
174
|
+
if not self.started_at:
|
|
175
|
+
return None
|
|
176
|
+
end = self.completed_at or datetime.now()
|
|
177
|
+
return (end - self.started_at).total_seconds()
|
|
178
|
+
|
|
179
|
+
def display_box(self) -> str:
|
|
180
|
+
"""Get box display for status views."""
|
|
181
|
+
lines = [f"┌─ BLOCK: {self.name} {'─' * (60 - len(self.name))}┐"]
|
|
182
|
+
|
|
183
|
+
for thread_id in self.thread_order:
|
|
184
|
+
thread = self.threads[thread_id]
|
|
185
|
+
lines.append(f"│{thread.display_line():<69}│")
|
|
186
|
+
|
|
187
|
+
lines.append("└" + "─" * 70 + "┘")
|
|
188
|
+
return "\n".join(lines)
|
|
189
|
+
|
|
190
|
+
def to_dict(self) -> dict:
|
|
191
|
+
"""Serialize Block to dictionary."""
|
|
192
|
+
return {
|
|
193
|
+
"id": self.id,
|
|
194
|
+
"name": self.name,
|
|
195
|
+
"purpose": self.purpose,
|
|
196
|
+
"cluster_id": self.cluster_id,
|
|
197
|
+
"status": self.status.value,
|
|
198
|
+
"progress": self.progress(),
|
|
199
|
+
"blocked_by": self.blocked_by,
|
|
200
|
+
"threads": [t.to_dict() for t in self.threads.values()],
|
|
201
|
+
"created_at": self.created_at.isoformat(),
|
|
202
|
+
"started_at": self.started_at.isoformat() if self.started_at else None,
|
|
203
|
+
"completed_at": self.completed_at.isoformat() if self.completed_at else None,
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
def __repr__(self) -> str:
|
|
207
|
+
return f"Block(id={self.id}, name={self.name}, threads={len(self.threads)})"
|