Skip to content

Commit 6557d89

Browse files
docs: adjust agents.md (3rd iteration)
1 parent 2960e28 commit 6557d89

File tree

1 file changed

+281
-25
lines changed

1 file changed

+281
-25
lines changed

AGENTS.md

Lines changed: 281 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@ This file provides context for AI coding tools (GitHub Copilot, Cursor, Aider, G
2525
-`client.current_signer` → track current account name for switching back
2626
-`NamedAccount.create(name)` → for local dev (not Account.create())
2727

28+
**Multi-user interactive apps (chat/social/gaming):**
29+
-**Server + Client pattern** → server.py runs ONE node, demo.py connects via HTTP
30+
- ✅ Clients use `HTTPProvider("http://127.0.0.1:8545")` to connect to shared server
31+
- ⚠️ **`client = Arkiv()` is fine for prototyping/testing** but NOT for multi-user/multi-process apps
32+
-**NOT** `with Arkiv()` in demo.py (creates separate blockchains per terminal!)
33+
2834
**Time conversion (methods, not imports):**
2935
-`client.arkiv.to_seconds(days=7)` → method on arkiv module
3036
-`client.arkiv.to_blocks(days=1)` → method on arkiv module
@@ -1347,33 +1353,272 @@ def main():
13471353
**Always create a basic test** to verify your core functionality works:
13481354

13491355
```python
1350-
# tests/test_chat.py
1356+
# tests/test_app.py
13511357
import pytest
1352-
from chat.client import ChatClient
1358+
from app.client import AppClient
13531359

1354-
def test_send_message(client):
1355-
"""Test that messages can be sent successfully."""
1356-
chat = ChatClient(client=client)
1360+
def test_create_entity(arkiv_client):
1361+
"""Test that entities can be created successfully."""
1362+
app = AppClient(arkiv_client)
13571363

1358-
message_key = chat.send_message("Test message")
1364+
entity_key = app.create_item("Test data")
13591365

1360-
assert message_key is not None
1361-
assert client.arkiv.entity_exists(message_key)
1366+
assert entity_key is not None
1367+
assert arkiv_client.arkiv.entity_exists(entity_key)
13621368

1363-
def test_retrieve_messages(client):
1364-
"""Test that messages can be retrieved."""
1365-
chat = ChatClient(client=client)
1369+
def test_retrieve_entities(arkiv_client):
1370+
"""Test that entities can be retrieved."""
1371+
app = AppClient(arkiv_client)
13661372

1367-
# Send a message
1368-
chat.send_message("Test message")
1373+
# Create an entity
1374+
app.create_item("Test data")
13691375

1370-
# Retrieve messages
1371-
messages = chat.get_messages()
1376+
# Retrieve entities
1377+
items = app.get_items()
13721378

1373-
assert len(messages) > 0
1374-
assert messages[0]["text"] == "Test message"
1379+
assert len(items) > 0
1380+
```
1381+
1382+
### 🚨 CRITICAL: Multi-User Interactive Applications (Social, Gaming, etc.)
1383+
1384+
**⚠️ STOP AND READ THIS FIRST** if you're building:
1385+
- Any app where multiple independent users interact in real-time
1386+
- Social media/messaging apps
1387+
- Multiplayer games
1388+
1389+
**The Problem:**
1390+
When you run a demo script like `python -m app.demo` in multiple terminals, each creates its **own separate blockchain**. Users can't see each other!
1391+
1392+
**WRONG - Separate Blockchains (Most Common Mistake):**
1393+
1394+
```python
1395+
# src/app/demo.py
1396+
def main():
1397+
with Arkiv() as client: # ← Creates NEW blockchain every time!
1398+
app = AppClient(client, username=username)
1399+
# ... user interacts but is alone in their own universe
1400+
1401+
if __name__ == "__main__":
1402+
main()
1403+
```
1404+
1405+
**What happens:**
1406+
```bash
1407+
# Terminal 1
1408+
$ uv run python -m app.demo
1409+
# Enter username: Alice
1410+
# ← Alice creates Blockchain A
1411+
1412+
# Terminal 2
1413+
$ uv run python -m app.demo
1414+
# Enter username: Bob
1415+
# ← Bob creates Blockchain B (completely separate!)
1416+
1417+
# Alice and Bob CANNOT see each other's data!
13751418
```
13761419

1420+
**Why this is tricky:**
1421+
- Each `Arkiv()` call starts a new blockchain node
1422+
- Different processes = different blockchains
1423+
- Tests work fine (shared fixtures), but real demo breaks
1424+
- This is THE #1 mistake for multi-user interactive apps
1425+
1426+
**CORRECT Solution: Server + Client Architecture**
1427+
1428+
For interactive multi-user apps, you need **TWO types of scripts**:
1429+
1430+
#### 1. **Server Script** - Runs the shared node (one instance only)
1431+
1432+
```python
1433+
# src/app/server.py
1434+
"""Arkiv app server - Run this ONCE to start the shared blockchain node."""
1435+
1436+
import time
1437+
from arkiv import Arkiv
1438+
1439+
def main():
1440+
print("🚀 Starting Arkiv App Server...")
1441+
print("📡 Server running on http://127.0.0.1:8545")
1442+
print("💡 Keep this running while users interact")
1443+
print("⏹️ Press Ctrl+C to stop\n")
1444+
1445+
with Arkiv() as client:
1446+
node = client.node
1447+
assert node is not None
1448+
1449+
print(f"✅ Server ready! RPC endpoint: {node.rpc_url}")
1450+
print(f"📊 Network ID: {client.eth.chain_id}\n")
1451+
1452+
try:
1453+
# Keep server running
1454+
while True:
1455+
time.sleep(1)
1456+
except KeyboardInterrupt:
1457+
print("\n\n🛑 Shutting down server...")
1458+
1459+
print("✅ Server stopped.\n")
1460+
1461+
if __name__ == "__main__":
1462+
main()
1463+
```
1464+
1465+
#### 2. **Client Demo Script** - Connects to the shared node
1466+
1467+
```python
1468+
# src/app/demo.py
1469+
"""Arkiv app client - Run multiple instances to interact together."""
1470+
1471+
import time
1472+
from typing import cast
1473+
from web3 import Web3
1474+
from web3.providers import HTTPProvider
1475+
from web3.providers.base import BaseProvider
1476+
from arkiv import Arkiv, NamedAccount
1477+
1478+
def main():
1479+
print("\n🎉 Welcome to Arkiv App! 🎉\n")
1480+
1481+
# Get username
1482+
username = input("Enter your username: ").strip()
1483+
if not username:
1484+
username = "Anonymous"
1485+
1486+
print(f"\n📡 Connecting to Arkiv server at http://127.0.0.1:8545...")
1487+
1488+
# Connect to shared server
1489+
provider = cast(BaseProvider, HTTPProvider("http://127.0.0.1:8545"))
1490+
1491+
# Create account for this user
1492+
account = NamedAccount.create(username.lower().replace(" ", "-"))
1493+
1494+
# Initialize client connected to shared blockchain
1495+
client = Arkiv(provider=provider, account=account)
1496+
1497+
# Fund account (server must be running!)
1498+
try:
1499+
# Check if we can connect
1500+
client.eth.chain_id
1501+
print("✅ Connected to server!")
1502+
except Exception as e:
1503+
print(f"❌ Cannot connect to server. Is it running?")
1504+
print(f" Start server with: uv run python -m app.server")
1505+
return
1506+
1507+
print(f"\n✅ Connected as {username}...")
1508+
print("⌨️ Interact with the app, '/quit' to exit\n")
1509+
1510+
# Create your app client
1511+
from app.client import AppClient
1512+
app = AppClient(client, username=username)
1513+
1514+
# Your app-specific initialization
1515+
# Example: Load recent data, set up event watchers, etc.
1516+
app.initialize()
1517+
time.sleep(0.2)
1518+
1519+
# Interactive loop
1520+
try:
1521+
while True:
1522+
user_input = input("> ").strip()
1523+
1524+
if not user_input:
1525+
continue
1526+
1527+
if user_input.lower() == "/quit":
1528+
print("\n👋 Goodbye!")
1529+
break
1530+
1531+
# Your app-specific command handling
1532+
try:
1533+
app.handle_command(user_input)
1534+
except Exception as e:
1535+
print(f"❌ Error: {e}")
1536+
1537+
except KeyboardInterrupt:
1538+
print("\n\n👋 Interrupted. Goodbye!")
1539+
1540+
finally:
1541+
app.cleanup()
1542+
1543+
print("✅ Disconnected.\n")
1544+
1545+
if __name__ == "__main__":
1546+
main()
1547+
```
1548+
1549+
#### Usage Instructions (Add to README)
1550+
1551+
```markdown
1552+
## Multi-User Demo
1553+
1554+
### Step 1: Start the server (ONCE, in one terminal)
1555+
1556+
```bash
1557+
uv run python -m app.server
1558+
```
1559+
1560+
Keep this running! It hosts the shared blockchain.
1561+
1562+
### Step 2: Connect clients (multiple terminals)
1563+
1564+
**Terminal 2:**
1565+
```bash
1566+
uv run python -m app.demo
1567+
# Enter username: Alice
1568+
```
1569+
1570+
**Terminal 3:**
1571+
```bash
1572+
uv run python -m app.demo
1573+
# Enter username: Bob
1574+
```
1575+
1576+
**Terminal 4:**
1577+
```bash
1578+
uv run python -m app.demo
1579+
# Enter username: Charlie
1580+
```
1581+
1582+
Now Alice, Bob, and Charlie can all interact together in real-time! 🎉
1583+
```
1584+
1585+
#### Key Implementation Details
1586+
1587+
**Server Requirements:**
1588+
- ✅ Creates and keeps the Arkiv node running
1589+
- ✅ Exposes RPC endpoint (default: http://127.0.0.1:8545)
1590+
- ✅ Stays alive until Ctrl+C
1591+
- ✅ Only ONE instance should run
1592+
1593+
**Client Requirements:**
1594+
- ✅ Connects to existing server via HTTP provider
1595+
- ✅ Creates unique account per user
1596+
- ✅ Funds account from server (or pre-fund accounts)
1597+
- ✅ Multiple instances can run simultaneously
1598+
- ✅ Each user has their own client but shares the blockchain
1599+
1600+
**Account Funding:**
1601+
- Server node can fund accounts
1602+
- Or pre-create funded accounts in server startup
1603+
- Or use a faucet pattern (server endpoint to fund accounts)
1604+
1605+
### When to Use This Pattern
1606+
1607+
**Multi-User Interactive Apps (ALWAYS use server + client):**
1608+
- ✅ Chat/messaging applications (multiple users communicating)
1609+
- ✅ Social media apps (users posting/commenting/liking)
1610+
- ✅ Multiplayer games (players interacting in shared world)
1611+
- ✅ Collaborative tools (real-time editing/voting/planning)
1612+
- ✅ Any app where independent processes need to see each other's data
1613+
- ✅ Any CLI demo where you say "open multiple terminals"
1614+
1615+
**Single-User or Testing (can use simple `with Arkiv()`):**
1616+
- ✅ Personal data storage demos
1617+
- ✅ Automated scripts (cron jobs, bots)
1618+
- ✅ pytest tests (use fixtures)
1619+
- ✅ Single-player games
1620+
- ✅ Data import/export tools
1621+
13771622
### Complete AI Prompt Pattern
13781623

13791624
Use this template when asking AI to create a new application:
@@ -1384,23 +1629,34 @@ Create a [application name] using Arkiv SDK with the following:
13841629
1. Project Structure:
13851630
- Core code in src/[app_name]/ with __init__.py
13861631
- Main class in src/[app_name]/client.py
1387-
- Demo script in src/[app_name]/demo.py that IMPORTS and USES the main class
13881632
- Tests in tests/test_[app_name].py
1389-
1390-
2. Implementation Requirements:
1391-
- Use context manager pattern: with Arkiv() as client
1392-
- For multi-user: Create ONE shared node, multiple clients with shared provider
1633+
- Demo scripts (see #2 below)
1634+
1635+
2. Demo Scripts (CRITICAL - Choose based on app type):
1636+
1637+
**If Multi-User Interactive App (chat, social, multiplayer game):**
1638+
- src/[app_name]/server.py - Runs shared node (one instance)
1639+
- src/[app_name]/demo.py - Client that connects to server (multiple instances)
1640+
- README must explain: "Start server first, then run demo in multiple terminals"
1641+
1642+
**If Single-User App (personal storage, automation, single-player):**
1643+
- src/[app_name]/demo.py - Simple `with Arkiv() as client` pattern
1644+
- No server needed
1645+
1646+
3. Implementation Requirements:
1647+
- For multi-user: Create ONE shared node (server), clients connect via HTTP
1648+
- For single-user: Use context manager pattern: with Arkiv() as client
13931649
- Always call client.node.stop() or use context manager for cleanup
13941650
- Add basic test that verifies core functionality
13951651
1396-
3. Code Organization:
1652+
4. Code Organization:
13971653
- Demo script should NOT duplicate core functionality
13981654
- Demo should import from the main module
13991655
- Include docstrings for all public methods
14001656
1401-
4. Testing:
1657+
5. Testing:
14021658
- Create at least 2 tests: one for creation, one for retrieval
1403-
- Use fixtures from conftest.py
1659+
- Use fixtures from conftest.py (tests ALWAYS use shared fixtures)
14041660
- Run with: uv run pytest tests/test_[app_name].py
14051661
```
14061662

0 commit comments

Comments
 (0)