Skip to content

Commit 01e22ec

Browse files
docs: adjust agents.md after 1st test
1 parent 752d06b commit 01e22ec

File tree

1 file changed

+371
-1
lines changed

1 file changed

+371
-1
lines changed

AGENTS.md

Lines changed: 371 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,190 @@ This file provides context for AI coding tools (GitHub Copilot, Cursor, Aider, G
2929

3030
---
3131

32+
## 🏗️ Critical: Understanding Arkiv Architecture
33+
34+
### Client/Node Architecture
35+
36+
**⚠️ MOST IMPORTANT CONCEPT**: Arkiv uses a **Client → Node → Blockchain Network** architecture.
37+
38+
```
39+
LOCAL DEVELOPMENT (Single Node):
40+
┌─────────────┐ ┌─────────────────────────────┐
41+
│ Client │ ───→ │ Arkiv Node │
42+
│ (Arkiv) │ │ (Full Blockchain Instance) │
43+
└─────────────┘ └─────────────────────────────┘
44+
45+
PRODUCTION (Multi-Node Network):
46+
┌────────────────────────────────────────────────────────────────┐
47+
│ BLOCKCHAIN (Network of nodes). │
48+
┌─────────────┐ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
49+
│ Client │ ───→ │ │ Arkiv Node 1 │ ←──→ │ Arkiv Node 2 │ ←──→ │ Arkiv Node 3 │ │
50+
│ (Arkiv) │ │ │ (Consensus) │ │ (Consensus) │ │ (Consensus) │ │
51+
└─────────────┘ │ └──────────────┘ └──────────────┘ └──────────────┘ │
52+
│ Shared Ledger (Replicated State) │
53+
└────────────────────────────────────────────────────────────────┘
54+
```
55+
56+
**Key Understanding:**
57+
- **Client** = Python object that sends transactions/queries (lightweight)
58+
- **Arkiv Node** = Full blockchain node running consensus, storage, and networking (heavyweight)
59+
- **Local Node** = Complete, real blockchain node - NOT a simulation or mock
60+
- **Production Network** = Multiple nodes running the same software, forming consensus
61+
62+
**Critical Insight:**
63+
Your local `ArkivNode()` is **identical** to production nodes - same code, same blockchain logic, same storage. The only difference is that it runs **alone** (single node) instead of in a **network** (multiple nodes forming consensus). This means:
64+
- ✅ Your local tests are running against **real blockchain code**
65+
- ✅ Behavior is identical to production (just without network consensus)
66+
- ✅ When you deploy, you're connecting to multiple copies of what you tested locally
67+
68+
**For Clients to Interact:**
69+
- ⚠️ **Multiple clients MUST connect to the SAME node/blockchain**
70+
- ⚠️ Each `Arkiv()` creates a separate blockchain - clients on different nodes CANNOT see each other's data
71+
- ✅ Use shared node pattern (shown below) for multi-client communication
72+
73+
### Each `Arkiv()` Creates a NEW Node!
74+
75+
**CRITICAL MISTAKE - Multiple Isolated Blockchains:**
76+
77+
```python
78+
# This creates TWO separate blockchains that CANNOT communicate!
79+
client1 = Arkiv() # Starts Node 1 with Blockchain A
80+
client2 = Arkiv() # Starts Node 2 with Blockchain B (completely separate!)
81+
82+
# Alice and Bob are in different universes - no communication possible!
83+
client1.arkiv.create_entity(...) # Stored in Blockchain A
84+
client2.arkiv.create_entity(...) # Stored in Blockchain B
85+
```
86+
87+
**Why this happens:**
88+
- `Arkiv()` without parameters calls `ArkivNode().start()` internally
89+
- Each node runs its own blockchain (like starting separate PostgreSQL instances)
90+
- Coming from traditional APIs (where multiple clients → one server), this is counterintuitive
91+
92+
**CORRECT Pattern 1 - Single Client, Multiple Accounts (Testing/Simulation Only):**
93+
94+
```python
95+
# Use when ONE entity needs to sign transactions as DIFFERENT accounts
96+
# Example: Testing ownership transfers, simulating multi-user scenarios
97+
client = Arkiv() # One node, one blockchain
98+
99+
# Create multiple accounts
100+
alice = NamedAccount.create("alice")
101+
bob = NamedAccount.create("bob")
102+
client.node.fund_account(alice)
103+
client.node.fund_account(bob)
104+
105+
# Add to client registry
106+
client.accounts["alice"] = alice
107+
client.accounts["bob"] = bob
108+
109+
# Switch signing account (same client controls both)
110+
client.switch_to("alice")
111+
client.arkiv.create_entity(...) # Alice's message
112+
113+
client.switch_to("bob")
114+
client.arkiv.create_entity(...) # Bob's message (same blockchain!)
115+
```
116+
117+
⚠️ **Important**: Pattern 1 is for single-client scenarios (tests, demos, automation). When simulating real multi-party interaction, use Pattern 2.
118+
119+
**CORRECT Pattern 2 - Multiple Independent Clients, Shared Node (Multi-Party Interaction):**
120+
121+
```python
122+
# Use when DIFFERENT parties need to interact with each other
123+
# Each party maintains their own client and private key
124+
# Critical: Parties MUST NOT share accounts - each has independent client
125+
126+
# Create ONE shared node
127+
main_client = Arkiv() # Starts the node
128+
shared_node = main_client.node
129+
130+
# Create provider from shared node
131+
from arkiv.provider import ProviderBuilder
132+
from web3.providers.base import BaseProvider
133+
from typing import cast
134+
135+
provider = cast(BaseProvider, ProviderBuilder().node(shared_node).build())
136+
137+
# Each party gets their own client with their own account
138+
alice_account = NamedAccount.create("alice")
139+
bob_account = NamedAccount.create("bob")
140+
shared_node.fund_account(alice_account)
141+
shared_node.fund_account(bob_account)
142+
143+
alice_client = Arkiv(provider=provider, account=alice_account)
144+
bob_client = Arkiv(provider=provider, account=bob_account)
145+
146+
# Now Alice and Bob interact independently on the SAME blockchain
147+
alice_client.arkiv.create_entity(...) # Alice's action (Alice's private key)
148+
bob_client.arkiv.create_entity(...) # Bob's action (Bob's private key)
149+
# They can see each other's data because they share the blockchain!
150+
```
151+
152+
**Key Difference:**
153+
- **Pattern 1**: ONE client switches between accounts (for testing/automation)
154+
- **Pattern 2**: MULTIPLE independent clients, each with own account (for real multi-party interaction)
155+
156+
### Node Lifecycle Management
157+
158+
**⚠️ CRITICAL**: Nodes are long-running processes that MUST be cleaned up.
159+
160+
**WRONG - Resource Leak:**
161+
162+
```python
163+
def my_function():
164+
client = Arkiv() # Starts node
165+
client.arkiv.create_entity(...)
166+
# Function ends, node keeps running! Memory leak!
167+
```
168+
169+
You'll see this warning:
170+
```
171+
Arkiv client with managed node is being destroyed but node is still running.
172+
Call arkiv.node.stop() or use context manager: 'with Arkiv() as arkiv:'
173+
```
174+
175+
**CORRECT Pattern 1 - Context Manager (Recommended):**
176+
177+
```python
178+
# Node automatically stopped when exiting context
179+
with Arkiv() as client:
180+
client.arkiv.create_entity(...)
181+
# ... do work ...
182+
# Node stopped here automatically
183+
```
184+
185+
**CORRECT Pattern 2 - Explicit Cleanup:**
186+
187+
```python
188+
client = Arkiv()
189+
try:
190+
client.arkiv.create_entity(...)
191+
# ... do work ...
192+
finally:
193+
if client.node:
194+
client.node.stop() # Explicitly stop node
195+
```
196+
197+
**CORRECT Pattern 3 - Shared Node in Threads:**
198+
199+
```python
200+
# Main thread owns the node
201+
main_client = Arkiv()
202+
203+
def worker_thread(shared_node):
204+
provider = ProviderBuilder().node(shared_node).build()
205+
client = Arkiv(provider=provider, account=my_account)
206+
# Do work...
207+
client.arkiv.cleanup_filters() # Clean up event watchers
208+
# DON'T call client.node.stop() - main thread owns the node!
209+
210+
# Later, in main thread:
211+
main_client.node.stop() # Only the owner stops the node
212+
```
213+
214+
---
215+
32216
## 🌟 What is Arkiv? (For New AI Assistants)
33217

34218
**Arkiv is a Web3 database that solves the Web3 data trilemma: Decentralization + Queryability + Simplicity.**
@@ -889,5 +1073,191 @@ if len(payload) > 90_000: # ~90KB safe limit
8891073

8901074
---
8911075

892-
*Last updated: 2024-11-25*
1076+
## 🤖 AI-Generated Project Guidelines
1077+
1078+
When creating new applications using AI assistants (Copilot, Cursor, etc.), follow these critical patterns:
1079+
1080+
### Project Structure
1081+
1082+
**⚠️ IMPORTANT**: User code goes in `src/`, NOT in `src/arkiv_starter/`
1083+
1084+
```
1085+
src/
1086+
├── arkiv_starter/ # Template examples (READ-ONLY reference)
1087+
│ ├── 01_clients.py
1088+
│ ├── 02_entity_crud.py
1089+
│ └── ...
1090+
1091+
├── my_app/ # ✅ Your application code here
1092+
│ ├── __init__.py
1093+
│ ├── core.py # Core functionality
1094+
│ ├── models.py # Data models
1095+
│ └── utils.py # Helper functions
1096+
1097+
├── my_app_demo.py # ✅ Demo script (imports from my_app/)
1098+
└── tests/
1099+
└── test_my_app.py # ✅ Tests for your app
1100+
```
1101+
1102+
**WRONG - Polluting template examples:**
1103+
```python
1104+
# DON'T add your code to arkiv_starter/
1105+
src/arkiv_starter/my_chat_app.py # Wrong location!
1106+
```
1107+
1108+
**CORRECT - Separate user code:**
1109+
```python
1110+
# Put your code in its own module
1111+
src/chat/
1112+
├── __init__.py
1113+
├── client.py # Chat client class
1114+
├── messages.py # Message handling
1115+
└── demo.py # Demo script
1116+
```
1117+
1118+
### Demo vs. Core Code Separation
1119+
1120+
**Key principle:** Demo code should USE your core functionality, not REPLICATE it.
1121+
1122+
**WRONG - Code duplication:**
1123+
```python
1124+
# chat_demo.py - DON'T do this
1125+
def send_message(client, text): # Reimplementing functionality
1126+
# ... duplicate code ...
1127+
1128+
def watch_messages(client): # Reimplementing functionality
1129+
# ... duplicate code ...
1130+
1131+
# Demo uses duplicated code
1132+
send_message(client, "Hello")
1133+
```
1134+
1135+
**CORRECT - Demo imports and uses core code:**
1136+
```python
1137+
# src/chat/client.py - Core functionality
1138+
class ChatClient:
1139+
def send_message(self, text: str):
1140+
# Implementation here
1141+
pass
1142+
1143+
def watch_messages(self):
1144+
# Implementation here
1145+
pass
1146+
1147+
# src/chat/demo.py - Demo uses core code
1148+
from chat.client import ChatClient
1149+
1150+
def main():
1151+
chat = ChatClient()
1152+
chat.send_message("Hello")
1153+
chat.watch_messages()
1154+
```
1155+
1156+
### Initial Testing
1157+
1158+
**Always create a basic test** to verify your core functionality works:
1159+
1160+
```python
1161+
# tests/test_chat.py
1162+
import pytest
1163+
from chat.client import ChatClient
1164+
1165+
def test_send_message(client):
1166+
"""Test that messages can be sent successfully."""
1167+
chat = ChatClient(client=client)
1168+
1169+
message_key = chat.send_message("Test message")
1170+
1171+
assert message_key is not None
1172+
assert client.arkiv.entity_exists(message_key)
1173+
1174+
def test_retrieve_messages(client):
1175+
"""Test that messages can be retrieved."""
1176+
chat = ChatClient(client=client)
1177+
1178+
# Send a message
1179+
chat.send_message("Test message")
1180+
1181+
# Retrieve messages
1182+
messages = chat.get_messages()
1183+
1184+
assert len(messages) > 0
1185+
assert messages[0]["text"] == "Test message"
1186+
```
1187+
1188+
### Complete AI Prompt Pattern
1189+
1190+
Use this template when asking AI to create a new application:
1191+
1192+
```
1193+
Create a [application name] using Arkiv SDK with the following:
1194+
1195+
1. Project Structure:
1196+
- Core code in src/[app_name]/ with __init__.py
1197+
- Main class in src/[app_name]/client.py
1198+
- Demo script in src/[app_name]/demo.py that IMPORTS and USES the main class
1199+
- Tests in tests/test_[app_name].py
1200+
1201+
2. Implementation Requirements:
1202+
- Use context manager pattern: with Arkiv() as client
1203+
- For multi-user: Create ONE shared node, multiple clients with shared provider
1204+
- Always call client.node.stop() or use context manager for cleanup
1205+
- Add basic test that verifies core functionality
1206+
1207+
3. Code Organization:
1208+
- Demo script should NOT duplicate core functionality
1209+
- Demo should import from the main module
1210+
- Include docstrings for all public methods
1211+
1212+
4. Testing:
1213+
- Create at least 2 tests: one for creation, one for retrieval
1214+
- Use fixtures from conftest.py
1215+
- Run with: uv run pytest tests/test_[app_name].py
1216+
```
1217+
1218+
### Example: Good Project Creation
1219+
1220+
**User prompt:**
1221+
```
1222+
Create a voting application using Arkiv. Follow the AI-Generated Project Guidelines
1223+
in AGENTS.md. Include core functionality in src/voting/, demo in src/voting/demo.py,
1224+
and tests in tests/test_voting.py.
1225+
```
1226+
1227+
**Expected AI output:**
1228+
```
1229+
src/voting/
1230+
├── __init__.py
1231+
├── poll.py # Poll class with create_poll, vote, get_results
1232+
├── demo.py # Demo that imports Poll and shows usage
1233+
tests/
1234+
└── test_voting.py # Tests for Poll functionality
1235+
```
1236+
1237+
**Demo pattern:**
1238+
```python
1239+
# src/voting/demo.py
1240+
from voting.poll import Poll
1241+
1242+
def main():
1243+
with Arkiv() as client:
1244+
poll = Poll(client)
1245+
1246+
# Create poll
1247+
poll_id = poll.create("Favorite color?", ["Red", "Blue", "Green"])
1248+
1249+
# Vote
1250+
poll.vote(poll_id, "Blue")
1251+
1252+
# Show results
1253+
results = poll.get_results(poll_id)
1254+
print(results)
1255+
1256+
if __name__ == "__main__":
1257+
main()
1258+
```
1259+
1260+
---
1261+
1262+
*Last updated: 2025-11-25*
8931263
*This file works with all AI coding tools that support the AGENTS.md standard.*

0 commit comments

Comments
 (0)