htm 0.0.1
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.
- checksums.yaml +7 -0
- data/.architecture/decisions/adrs/001-use-postgresql-timescaledb-storage.md +227 -0
- data/.architecture/decisions/adrs/002-two-tier-memory-architecture.md +322 -0
- data/.architecture/decisions/adrs/003-ollama-default-embedding-provider.md +339 -0
- data/.architecture/decisions/adrs/004-multi-robot-shared-memory-hive-mind.md +374 -0
- data/.architecture/decisions/adrs/005-rag-based-retrieval-with-hybrid-search.md +443 -0
- data/.architecture/decisions/adrs/006-context-assembly-strategies.md +444 -0
- data/.architecture/decisions/adrs/007-working-memory-eviction-strategy.md +461 -0
- data/.architecture/decisions/adrs/008-robot-identification-system.md +550 -0
- data/.architecture/decisions/adrs/009-never-forget-explicit-deletion-only.md +570 -0
- data/.architecture/decisions/adrs/010-redis-working-memory-rejected.md +323 -0
- data/.architecture/decisions/adrs/011-database-side-embedding-generation-with-pgai.md +585 -0
- data/.architecture/decisions/adrs/012-llm-driven-ontology-topic-extraction.md +583 -0
- data/.architecture/decisions/adrs/013-activerecord-orm-and-many-to-many-tagging.md +299 -0
- data/.architecture/decisions/adrs/014-client-side-embedding-generation-workflow.md +569 -0
- data/.architecture/decisions/adrs/015-hierarchical-tag-ontology-and-llm-extraction.md +701 -0
- data/.architecture/decisions/adrs/016-async-embedding-and-tag-generation.md +694 -0
- data/.architecture/members.yml +144 -0
- data/.architecture/reviews/2025-10-29-llm-configuration-and-async-processing-review.md +1137 -0
- data/.architecture/reviews/initial-system-analysis.md +330 -0
- data/.envrc +32 -0
- data/.irbrc +145 -0
- data/CHANGELOG.md +150 -0
- data/COMMITS.md +196 -0
- data/LICENSE +21 -0
- data/README.md +1347 -0
- data/Rakefile +51 -0
- data/SETUP.md +268 -0
- data/config/database.yml +67 -0
- data/db/migrate/20250101000001_enable_extensions.rb +14 -0
- data/db/migrate/20250101000002_create_robots.rb +14 -0
- data/db/migrate/20250101000003_create_nodes.rb +42 -0
- data/db/migrate/20250101000005_create_tags.rb +38 -0
- data/db/migrate/20250101000007_add_node_vector_indexes.rb +30 -0
- data/db/schema.sql +473 -0
- data/db/seed_data/README.md +100 -0
- data/db/seed_data/presidents.md +136 -0
- data/db/seed_data/states.md +151 -0
- data/db/seeds.rb +208 -0
- data/dbdoc/README.md +173 -0
- data/dbdoc/public.node_stats.md +48 -0
- data/dbdoc/public.node_stats.svg +41 -0
- data/dbdoc/public.node_tags.md +40 -0
- data/dbdoc/public.node_tags.svg +112 -0
- data/dbdoc/public.nodes.md +54 -0
- data/dbdoc/public.nodes.svg +118 -0
- data/dbdoc/public.nodes_tags.md +39 -0
- data/dbdoc/public.nodes_tags.svg +112 -0
- data/dbdoc/public.ontology_structure.md +48 -0
- data/dbdoc/public.ontology_structure.svg +38 -0
- data/dbdoc/public.operations_log.md +42 -0
- data/dbdoc/public.operations_log.svg +130 -0
- data/dbdoc/public.relationships.md +39 -0
- data/dbdoc/public.relationships.svg +41 -0
- data/dbdoc/public.robot_activity.md +46 -0
- data/dbdoc/public.robot_activity.svg +35 -0
- data/dbdoc/public.robots.md +35 -0
- data/dbdoc/public.robots.svg +90 -0
- data/dbdoc/public.schema_migrations.md +29 -0
- data/dbdoc/public.schema_migrations.svg +26 -0
- data/dbdoc/public.tags.md +35 -0
- data/dbdoc/public.tags.svg +60 -0
- data/dbdoc/public.topic_relationships.md +45 -0
- data/dbdoc/public.topic_relationships.svg +32 -0
- data/dbdoc/schema.json +1437 -0
- data/dbdoc/schema.svg +154 -0
- data/docs/api/database.md +806 -0
- data/docs/api/embedding-service.md +532 -0
- data/docs/api/htm.md +797 -0
- data/docs/api/index.md +259 -0
- data/docs/api/long-term-memory.md +1096 -0
- data/docs/api/working-memory.md +665 -0
- data/docs/architecture/adrs/001-postgresql-timescaledb.md +314 -0
- data/docs/architecture/adrs/002-two-tier-memory.md +411 -0
- data/docs/architecture/adrs/003-ollama-embeddings.md +421 -0
- data/docs/architecture/adrs/004-hive-mind.md +437 -0
- data/docs/architecture/adrs/005-rag-retrieval.md +531 -0
- data/docs/architecture/adrs/006-context-assembly.md +496 -0
- data/docs/architecture/adrs/007-eviction-strategy.md +645 -0
- data/docs/architecture/adrs/008-robot-identification.md +625 -0
- data/docs/architecture/adrs/009-never-forget.md +648 -0
- data/docs/architecture/adrs/010-redis-working-memory-rejected.md +323 -0
- data/docs/architecture/adrs/011-pgai-integration.md +494 -0
- data/docs/architecture/adrs/index.md +215 -0
- data/docs/architecture/hive-mind.md +736 -0
- data/docs/architecture/index.md +351 -0
- data/docs/architecture/overview.md +538 -0
- data/docs/architecture/two-tier-memory.md +873 -0
- data/docs/assets/css/custom.css +83 -0
- data/docs/assets/images/htm-core-components.svg +63 -0
- data/docs/assets/images/htm-database-schema.svg +93 -0
- data/docs/assets/images/htm-hive-mind-architecture.svg +125 -0
- data/docs/assets/images/htm-importance-scoring-framework.svg +83 -0
- data/docs/assets/images/htm-layered-architecture.svg +71 -0
- data/docs/assets/images/htm-long-term-memory-architecture.svg +115 -0
- data/docs/assets/images/htm-working-memory-architecture.svg +120 -0
- data/docs/assets/images/htm.jpg +0 -0
- data/docs/assets/images/htm_demo.gif +0 -0
- data/docs/assets/js/mathjax.js +18 -0
- data/docs/assets/videos/htm_video.mp4 +0 -0
- data/docs/database_rake_tasks.md +322 -0
- data/docs/development/contributing.md +787 -0
- data/docs/development/index.md +336 -0
- data/docs/development/schema.md +596 -0
- data/docs/development/setup.md +719 -0
- data/docs/development/testing.md +819 -0
- data/docs/guides/adding-memories.md +824 -0
- data/docs/guides/context-assembly.md +1009 -0
- data/docs/guides/getting-started.md +577 -0
- data/docs/guides/index.md +118 -0
- data/docs/guides/long-term-memory.md +941 -0
- data/docs/guides/multi-robot.md +866 -0
- data/docs/guides/recalling-memories.md +927 -0
- data/docs/guides/search-strategies.md +953 -0
- data/docs/guides/working-memory.md +717 -0
- data/docs/index.md +214 -0
- data/docs/installation.md +477 -0
- data/docs/multi_framework_support.md +519 -0
- data/docs/quick-start.md +655 -0
- data/docs/setup_local_database.md +302 -0
- data/docs/using_rake_tasks_in_your_app.md +383 -0
- data/examples/basic_usage.rb +93 -0
- data/examples/cli_app/README.md +317 -0
- data/examples/cli_app/htm_cli.rb +270 -0
- data/examples/custom_llm_configuration.rb +183 -0
- data/examples/example_app/Rakefile +71 -0
- data/examples/example_app/app.rb +206 -0
- data/examples/sinatra_app/Gemfile +21 -0
- data/examples/sinatra_app/app.rb +335 -0
- data/lib/htm/active_record_config.rb +113 -0
- data/lib/htm/configuration.rb +342 -0
- data/lib/htm/database.rb +594 -0
- data/lib/htm/embedding_service.rb +115 -0
- data/lib/htm/errors.rb +34 -0
- data/lib/htm/job_adapter.rb +154 -0
- data/lib/htm/jobs/generate_embedding_job.rb +65 -0
- data/lib/htm/jobs/generate_tags_job.rb +82 -0
- data/lib/htm/long_term_memory.rb +965 -0
- data/lib/htm/models/node.rb +109 -0
- data/lib/htm/models/node_tag.rb +33 -0
- data/lib/htm/models/robot.rb +52 -0
- data/lib/htm/models/tag.rb +76 -0
- data/lib/htm/railtie.rb +76 -0
- data/lib/htm/sinatra.rb +157 -0
- data/lib/htm/tag_service.rb +135 -0
- data/lib/htm/tasks.rb +38 -0
- data/lib/htm/version.rb +5 -0
- data/lib/htm/working_memory.rb +182 -0
- data/lib/htm.rb +400 -0
- data/lib/tasks/db.rake +19 -0
- data/lib/tasks/htm.rake +147 -0
- data/lib/tasks/jobs.rake +312 -0
- data/mkdocs.yml +190 -0
- data/scripts/install_local_database.sh +309 -0
- metadata +341 -0
|
@@ -0,0 +1,625 @@
|
|
|
1
|
+
# ADR-008: Robot Identification System
|
|
2
|
+
|
|
3
|
+
**Status**: Accepted
|
|
4
|
+
|
|
5
|
+
**Date**: 2025-10-25
|
|
6
|
+
|
|
7
|
+
**Decision Makers**: Dewayne VanHoozer, Claude (Anthropic)
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Quick Summary
|
|
12
|
+
|
|
13
|
+
HTM uses a **dual-identifier system** with mandatory unique `robot_id` (UUID) and optional human-readable `robot_name`. Both are auto-generated if not provided, with automatic robot registration and activity tracking in the database.
|
|
14
|
+
|
|
15
|
+
**Why**: Unique robot identification enables memory attribution, cross-robot analytics, debugging, and future features like permissions and groups while maintaining developer-friendly names.
|
|
16
|
+
|
|
17
|
+
**Impact**: Globally unique identity with human-readable names and automatic management, at the cost of dual identifiers and manual persistence requirements for stable identities.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Context
|
|
22
|
+
|
|
23
|
+
In the HTM hive mind architecture (ADR-004), multiple robots share a global memory database. Each robot needs a unique identity to:
|
|
24
|
+
|
|
25
|
+
- Attribute memories to their creator
|
|
26
|
+
- Track robot activity over time
|
|
27
|
+
- Enable queries like "which robot said this?"
|
|
28
|
+
- Debug conversation attribution
|
|
29
|
+
- Support future features (privacy, analytics, collaboration)
|
|
30
|
+
|
|
31
|
+
### Identity Requirements
|
|
32
|
+
|
|
33
|
+
- **Uniqueness**: Must be globally unique (no collisions)
|
|
34
|
+
- **Persistence**: Should remain stable across sessions
|
|
35
|
+
- **Human-readable**: Developers need to identify robots easily
|
|
36
|
+
- **Automation**: Should auto-generate if not provided
|
|
37
|
+
- **Registration**: Track all robots using the system
|
|
38
|
+
|
|
39
|
+
### Alternative Identification Schemes
|
|
40
|
+
|
|
41
|
+
1. **Auto-generated UUID only**: Unique but not human-readable
|
|
42
|
+
2. **User-provided name only**: Readable but collision-prone
|
|
43
|
+
3. **UUID + optional name**: Unique ID with optional readable name
|
|
44
|
+
4. **Sequential ID**: Simple but requires coordination
|
|
45
|
+
5. **Hostname + PID**: Automatic but not persistent across restarts
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## Decision
|
|
50
|
+
|
|
51
|
+
We will use a **dual-identifier system**: mandatory unique `robot_id` (UUID) + optional human-readable `robot_name`, with automatic generation if not provided.
|
|
52
|
+
|
|
53
|
+
### Robot Identification Components
|
|
54
|
+
|
|
55
|
+
**1. Robot ID (`robot_id`)**
|
|
56
|
+
|
|
57
|
+
- **Type**: UUID v4 (RFC 4122)
|
|
58
|
+
- **Format**: `"f47ac10b-58cc-4372-a567-0e02b2c3d479"`
|
|
59
|
+
- **Generation**: `SecureRandom.uuid` if not provided
|
|
60
|
+
- **Persistence**: User-provided for stability, or auto-generated per session
|
|
61
|
+
- **Usage**: Primary key, foreign key references, attribution
|
|
62
|
+
|
|
63
|
+
**2. Robot Name (`robot_name`)**
|
|
64
|
+
|
|
65
|
+
- **Type**: String (optional, human-readable)
|
|
66
|
+
- **Format**: Any descriptive string (e.g., "Code Helper", "Research Assistant")
|
|
67
|
+
- **Generation**: `"robot_#{robot_id[0..7]}"` if not provided
|
|
68
|
+
- **Persistence**: Stored in database, updatable
|
|
69
|
+
- **Usage**: Display, debugging, user interfaces
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## Rationale
|
|
74
|
+
|
|
75
|
+
### Why UUID + Name?
|
|
76
|
+
|
|
77
|
+
**UUID provides guarantees**:
|
|
78
|
+
|
|
79
|
+
- Globally unique (collision probability: ~10^-36)
|
|
80
|
+
- No coordination required (decentralized generation)
|
|
81
|
+
- Cryptographically random (unpredictable)
|
|
82
|
+
- Standard format (RFC 4122, widely supported)
|
|
83
|
+
|
|
84
|
+
**Name provides usability**:
|
|
85
|
+
|
|
86
|
+
- Human-readable in logs and debugging
|
|
87
|
+
- Meaningful for user-facing features
|
|
88
|
+
- Easy to remember ("Code Helper" vs UUID)
|
|
89
|
+
- Updatable without breaking references
|
|
90
|
+
|
|
91
|
+
**Combination is best**:
|
|
92
|
+
|
|
93
|
+
- UUID for database integrity
|
|
94
|
+
- Name for developer experience
|
|
95
|
+
- Auto-generation for convenience
|
|
96
|
+
- User control for persistence
|
|
97
|
+
|
|
98
|
+
### Why Auto-Generate?
|
|
99
|
+
|
|
100
|
+
**Convenience**:
|
|
101
|
+
|
|
102
|
+
- Users don't need to manage UUIDs manually
|
|
103
|
+
- Works out-of-box with no configuration
|
|
104
|
+
- Still allows explicit robot_id for persistence
|
|
105
|
+
|
|
106
|
+
**Session vs Persistent Identity**:
|
|
107
|
+
|
|
108
|
+
- **Session identity**: Auto-generated UUID, ephemeral robot
|
|
109
|
+
- **Persistent identity**: User-provided UUID, stable across restarts
|
|
110
|
+
|
|
111
|
+
```ruby
|
|
112
|
+
# Ephemeral robot (new UUID every session)
|
|
113
|
+
htm = HTM.new(robot_name: "Temp Helper")
|
|
114
|
+
|
|
115
|
+
# Persistent robot (same UUID across sessions)
|
|
116
|
+
ROBOT_ID = "f47ac10b-58cc-4372-a567-0e02b2c3d479"
|
|
117
|
+
htm = HTM.new(robot_id: ROBOT_ID, robot_name: "Persistent Helper")
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Why Register Robots?
|
|
121
|
+
|
|
122
|
+
**Activity tracking**:
|
|
123
|
+
|
|
124
|
+
- Know which robots are active
|
|
125
|
+
- Monitor robot usage patterns
|
|
126
|
+
- Identify inactive robots for cleanup
|
|
127
|
+
|
|
128
|
+
**Metadata extensibility**:
|
|
129
|
+
|
|
130
|
+
- Future: Robot roles, permissions, preferences
|
|
131
|
+
- Future: Robot groups/teams
|
|
132
|
+
- Future: Configuration per robot
|
|
133
|
+
|
|
134
|
+
**Debugging**:
|
|
135
|
+
|
|
136
|
+
- "Which robot created this memory?"
|
|
137
|
+
- "What has this robot been doing?"
|
|
138
|
+
- "When was this robot last active?"
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## Implementation Details
|
|
143
|
+
|
|
144
|
+
### Robot Registry
|
|
145
|
+
|
|
146
|
+
```sql
|
|
147
|
+
CREATE TABLE robots (
|
|
148
|
+
id TEXT PRIMARY KEY, -- robot_id (UUID)
|
|
149
|
+
name TEXT, -- robot_name (human-readable)
|
|
150
|
+
created_at TIMESTAMP DEFAULT NOW(),
|
|
151
|
+
last_active TIMESTAMP DEFAULT NOW(),
|
|
152
|
+
metadata JSONB -- future extensibility
|
|
153
|
+
);
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Robot Attribution
|
|
157
|
+
|
|
158
|
+
Every memory node stores `robot_id`:
|
|
159
|
+
|
|
160
|
+
```sql
|
|
161
|
+
CREATE TABLE nodes (
|
|
162
|
+
id SERIAL PRIMARY KEY,
|
|
163
|
+
key TEXT UNIQUE NOT NULL,
|
|
164
|
+
value TEXT NOT NULL,
|
|
165
|
+
robot_id TEXT NOT NULL REFERENCES robots(id), -- Attribution
|
|
166
|
+
...
|
|
167
|
+
);
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Initialization
|
|
171
|
+
|
|
172
|
+
```ruby
|
|
173
|
+
htm = HTM.new(
|
|
174
|
+
robot_id: "f47ac10b-58cc-4372-a567-0e02b2c3d479", # optional, auto-generated
|
|
175
|
+
robot_name: "Code Helper" # optional, descriptive
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
# Auto-generated example:
|
|
179
|
+
# robot_id: "3a7b2c4d-8e9f-4a5b-9c8d-7e6f5a4b3c2d"
|
|
180
|
+
# robot_name: "robot_3a7b2c4d"
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### Registration Logic
|
|
184
|
+
|
|
185
|
+
**Upsert semantics**:
|
|
186
|
+
|
|
187
|
+
```sql
|
|
188
|
+
INSERT INTO robots (id, name)
|
|
189
|
+
VALUES ($1, $2)
|
|
190
|
+
ON CONFLICT (id) DO UPDATE
|
|
191
|
+
SET name = $2, last_active = CURRENT_TIMESTAMP
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
- Automatic on first HTM initialization
|
|
195
|
+
- Updates name and last_active if robot_id exists
|
|
196
|
+
- Activity tracking: `last_active` updated on every operation
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
## Consequences
|
|
201
|
+
|
|
202
|
+
### Positive
|
|
203
|
+
|
|
204
|
+
- Uniqueness guaranteed: UUID collision-proof
|
|
205
|
+
- Human-readable: Names easy to identify in logs
|
|
206
|
+
- Auto-generation: Works without manual configuration
|
|
207
|
+
- Persistence option: User can provide stable robot_id
|
|
208
|
+
- Attribution tracking: Every memory linked to creator
|
|
209
|
+
- Activity monitoring: Track robot usage over time
|
|
210
|
+
- Future-proof: Metadata field for extensibility
|
|
211
|
+
- Standard format: UUID is widely recognized
|
|
212
|
+
|
|
213
|
+
### Negative
|
|
214
|
+
|
|
215
|
+
- Dual identifiers: Two fields instead of one (complexity)
|
|
216
|
+
- Name collisions: Names not unique (only IDs are)
|
|
217
|
+
- Manual persistence: User must manage robot_id for stability
|
|
218
|
+
- No automatic migration: Robot ID changes break historical attribution
|
|
219
|
+
- UUID verbosity: UUIDs are long (36 characters)
|
|
220
|
+
|
|
221
|
+
### Neutral
|
|
222
|
+
|
|
223
|
+
- Session vs persistent: User chooses ephemeral or stable identity
|
|
224
|
+
- Name mutability: Names can be updated, IDs cannot
|
|
225
|
+
- Registry cleanup: Inactive robots accumulate (manual cleanup needed)
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
## Use Cases
|
|
230
|
+
|
|
231
|
+
### Use Case 1: Ephemeral Robot (Session Identity)
|
|
232
|
+
|
|
233
|
+
```ruby
|
|
234
|
+
# New UUID generated every time
|
|
235
|
+
htm = HTM.new(robot_name: "Quick Helper")
|
|
236
|
+
|
|
237
|
+
# robot_id: auto-generated UUID
|
|
238
|
+
# robot_name: "Quick Helper"
|
|
239
|
+
|
|
240
|
+
# Next session: different robot_id, same name
|
|
241
|
+
# Memories attributed to different robot each time
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
### Use Case 2: Persistent Robot (Stable Identity)
|
|
245
|
+
|
|
246
|
+
```ruby
|
|
247
|
+
# Store robot_id in config or environment
|
|
248
|
+
ROBOT_ID = ENV['ROBOT_ID'] || "f47ac10b-58cc-4372-a567-0e02b2c3d479"
|
|
249
|
+
|
|
250
|
+
htm = HTM.new(
|
|
251
|
+
robot_id: ROBOT_ID,
|
|
252
|
+
robot_name: "Code Helper"
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
# Same robot_id across sessions
|
|
256
|
+
# Memories consistently attributed to this robot
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### Use Case 3: Multiple Robots in Same Process
|
|
260
|
+
|
|
261
|
+
```ruby
|
|
262
|
+
# Research assistant
|
|
263
|
+
research_bot = HTM.new(
|
|
264
|
+
robot_id: "research-001",
|
|
265
|
+
robot_name: "Research Assistant"
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
# Code helper
|
|
269
|
+
code_bot = HTM.new(
|
|
270
|
+
robot_id: "code-001",
|
|
271
|
+
robot_name: "Code Helper"
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
# Each robot has own working memory
|
|
275
|
+
# Both share same long-term memory database
|
|
276
|
+
# Memories attributed to respective robots
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
### Use Case 4: Robot Activity Analysis
|
|
280
|
+
|
|
281
|
+
```sql
|
|
282
|
+
-- Which robots have been active?
|
|
283
|
+
SELECT id, name, last_active
|
|
284
|
+
FROM robots
|
|
285
|
+
ORDER BY last_active DESC;
|
|
286
|
+
|
|
287
|
+
-- Which robot contributed most memories?
|
|
288
|
+
SELECT robot_id, COUNT(*) as memory_count
|
|
289
|
+
FROM nodes
|
|
290
|
+
GROUP BY robot_id
|
|
291
|
+
ORDER BY memory_count DESC;
|
|
292
|
+
|
|
293
|
+
-- What has "Code Helper" been doing?
|
|
294
|
+
SELECT operation, created_at, details
|
|
295
|
+
FROM operations_log
|
|
296
|
+
WHERE robot_id = (SELECT id FROM robots WHERE name = 'Code Helper')
|
|
297
|
+
ORDER BY created_at DESC
|
|
298
|
+
LIMIT 50;
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
### Use Case 5: Conversation Attribution
|
|
302
|
+
|
|
303
|
+
```ruby
|
|
304
|
+
# Which robot discussed PostgreSQL?
|
|
305
|
+
breakdown = htm.which_robot_said("PostgreSQL")
|
|
306
|
+
# => { "f47ac10b-..." => 15, "3a7b2c4d-..." => 8 }
|
|
307
|
+
|
|
308
|
+
# Get robot names
|
|
309
|
+
robot_names = breakdown.keys.map do |robot_id|
|
|
310
|
+
db.query("SELECT name FROM robots WHERE id = $1", [robot_id]).first
|
|
311
|
+
end
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
---
|
|
315
|
+
|
|
316
|
+
## Robot Identity Lifecycle
|
|
317
|
+
|
|
318
|
+
### 1. Creation
|
|
319
|
+
|
|
320
|
+
```ruby
|
|
321
|
+
htm = HTM.new(robot_id: uuid, robot_name: name)
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
### 2. Registration
|
|
325
|
+
|
|
326
|
+
```sql
|
|
327
|
+
INSERT INTO robots (id, name, created_at, last_active)
|
|
328
|
+
VALUES (uuid, name, NOW(), NOW())
|
|
329
|
+
ON CONFLICT (id) DO UPDATE SET name = name, last_active = NOW()
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
### 3. Activity Tracking
|
|
333
|
+
|
|
334
|
+
```ruby
|
|
335
|
+
# On every HTM operation (add_node, recall, forget, retrieve)
|
|
336
|
+
@long_term_memory.update_robot_activity(@robot_id)
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
```sql
|
|
340
|
+
UPDATE robots
|
|
341
|
+
SET last_active = CURRENT_TIMESTAMP
|
|
342
|
+
WHERE id = robot_id
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
### 4. Attribution
|
|
346
|
+
|
|
347
|
+
```ruby
|
|
348
|
+
# Every node stores robot_id
|
|
349
|
+
node_id = @long_term_memory.add(
|
|
350
|
+
key: key,
|
|
351
|
+
value: value,
|
|
352
|
+
robot_id: @robot_id, # Attribution
|
|
353
|
+
...
|
|
354
|
+
)
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
### 5. Querying
|
|
358
|
+
|
|
359
|
+
```sql
|
|
360
|
+
-- Find memories by robot
|
|
361
|
+
SELECT * FROM nodes WHERE robot_id = 'f47ac10b-...'
|
|
362
|
+
|
|
363
|
+
-- Find robot by name
|
|
364
|
+
SELECT * FROM robots WHERE name = 'Code Helper'
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
### 6. Cleanup (Manual)
|
|
368
|
+
|
|
369
|
+
```sql
|
|
370
|
+
-- Find inactive robots
|
|
371
|
+
SELECT * FROM robots WHERE last_active < NOW() - INTERVAL '30 days';
|
|
372
|
+
|
|
373
|
+
-- Delete robot and all its memories (use with caution!)
|
|
374
|
+
DELETE FROM nodes WHERE robot_id = 'f47ac10b-...';
|
|
375
|
+
DELETE FROM robots WHERE id = 'f47ac10b-...';
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
---
|
|
379
|
+
|
|
380
|
+
## Performance Characteristics
|
|
381
|
+
|
|
382
|
+
### UUID Generation
|
|
383
|
+
|
|
384
|
+
- **Time**: < 1ms (SecureRandom.uuid)
|
|
385
|
+
- **Collision probability**: ~10^-36 for v4 UUID
|
|
386
|
+
- **Entropy**: 122 random bits
|
|
387
|
+
|
|
388
|
+
### Robot Registration
|
|
389
|
+
|
|
390
|
+
- **Upsert query**: < 5ms
|
|
391
|
+
- **Index**: Primary key on `robots.id`
|
|
392
|
+
- **Frequency**: Once per HTM initialization
|
|
393
|
+
|
|
394
|
+
### Activity Tracking
|
|
395
|
+
|
|
396
|
+
- **Update query**: < 2ms
|
|
397
|
+
- **Frequency**: Every HTM operation
|
|
398
|
+
- **Index**: Primary key on `robots.id`
|
|
399
|
+
|
|
400
|
+
### Attribution Queries
|
|
401
|
+
|
|
402
|
+
- **Foreign key join**: O(log n) with index
|
|
403
|
+
- **Index**: `nodes.robot_id` indexed as foreign key
|
|
404
|
+
|
|
405
|
+
---
|
|
406
|
+
|
|
407
|
+
## Design Decisions
|
|
408
|
+
|
|
409
|
+
### Decision: UUID v4 (Random) Instead of UUID v1 (Timestamp)
|
|
410
|
+
|
|
411
|
+
**Rationale**:
|
|
412
|
+
|
|
413
|
+
- No MAC address leakage (privacy)
|
|
414
|
+
- Cryptographically random (security)
|
|
415
|
+
- No clock synchronization needed
|
|
416
|
+
|
|
417
|
+
**Alternative**: UUID v1 (timestamp-based)
|
|
418
|
+
|
|
419
|
+
**Rejected**: MAC address exposure, clock sync issues
|
|
420
|
+
|
|
421
|
+
### Decision: Optional robot_id, Mandatory robot_name
|
|
422
|
+
|
|
423
|
+
**Rationale**: Auto-generate both if not provided, allow user override
|
|
424
|
+
|
|
425
|
+
**Alternative**: Require user to provide robot_id
|
|
426
|
+
|
|
427
|
+
**Rejected**: Too much friction, poor DX
|
|
428
|
+
|
|
429
|
+
### Decision: Auto-Generated Name Format
|
|
430
|
+
|
|
431
|
+
**Rationale**: `"robot_#{uuid[0..7]}"` provides:
|
|
432
|
+
|
|
433
|
+
- Uniqueness (first 8 chars usually unique)
|
|
434
|
+
- Traceability (prefix of robot_id)
|
|
435
|
+
- Consistency (predictable format)
|
|
436
|
+
|
|
437
|
+
**Alternative**: Random adjective + noun ("Happy Robot")
|
|
438
|
+
|
|
439
|
+
**Rejected**: Harder to correlate with robot_id
|
|
440
|
+
|
|
441
|
+
### Decision: Upsert Semantics for Registration
|
|
442
|
+
|
|
443
|
+
**Rationale**: Allows robot_name updates, prevents duplicate registration errors
|
|
444
|
+
|
|
445
|
+
**Alternative**: Strict insert-only (error on duplicate)
|
|
446
|
+
|
|
447
|
+
**Rejected**: Prevents name updates, complicates initialization
|
|
448
|
+
|
|
449
|
+
### Decision: JSONB Metadata Field
|
|
450
|
+
|
|
451
|
+
**Rationale**: Future extensibility without schema migrations
|
|
452
|
+
|
|
453
|
+
**Alternative**: Add columns as needed
|
|
454
|
+
|
|
455
|
+
**Deferred**: JSONB is flexible, add columns for indexed fields later
|
|
456
|
+
|
|
457
|
+
---
|
|
458
|
+
|
|
459
|
+
## Risks and Mitigations
|
|
460
|
+
|
|
461
|
+
### Risk: Robot ID Changes Break Attribution
|
|
462
|
+
|
|
463
|
+
!!! danger "Risk"
|
|
464
|
+
User changes robot_id, breaks historical attribution
|
|
465
|
+
|
|
466
|
+
**Likelihood**: Low (user must explicitly change)
|
|
467
|
+
|
|
468
|
+
**Impact**: High (attribution lost)
|
|
469
|
+
|
|
470
|
+
**Mitigation**:
|
|
471
|
+
|
|
472
|
+
- Document robot_id persistence clearly
|
|
473
|
+
- Recommend storing robot_id in config
|
|
474
|
+
- Consider robot_id migration tool (future)
|
|
475
|
+
|
|
476
|
+
### Risk: Name Collisions
|
|
477
|
+
|
|
478
|
+
!!! info "Risk"
|
|
479
|
+
Multiple robots with same name
|
|
480
|
+
|
|
481
|
+
**Likelihood**: Medium (names not enforced unique)
|
|
482
|
+
|
|
483
|
+
**Impact**: Low (IDs are unique, names just for display)
|
|
484
|
+
|
|
485
|
+
**Mitigation**:
|
|
486
|
+
|
|
487
|
+
- Document that names are not unique
|
|
488
|
+
- Use robot_id for queries, name for display
|
|
489
|
+
- Consider unique constraint on name (future)
|
|
490
|
+
|
|
491
|
+
### Risk: Robot Registry Growth
|
|
492
|
+
|
|
493
|
+
!!! warning "Risk"
|
|
494
|
+
Inactive robots accumulate indefinitely
|
|
495
|
+
|
|
496
|
+
**Likelihood**: High (no automatic cleanup)
|
|
497
|
+
|
|
498
|
+
**Impact**: Low (storage, query slowdown)
|
|
499
|
+
|
|
500
|
+
**Mitigation**:
|
|
501
|
+
|
|
502
|
+
- Document cleanup procedures
|
|
503
|
+
- Provide cleanup utilities (future)
|
|
504
|
+
- Monitor robot registry size
|
|
505
|
+
|
|
506
|
+
### Risk: UUID Verbosity
|
|
507
|
+
|
|
508
|
+
!!! info "Risk"
|
|
509
|
+
UUIDs are long (36 chars) in logs
|
|
510
|
+
|
|
511
|
+
**Likelihood**: High (by design)
|
|
512
|
+
|
|
513
|
+
**Impact**: Low (readability in logs)
|
|
514
|
+
|
|
515
|
+
**Mitigation**:
|
|
516
|
+
|
|
517
|
+
- Use robot_name for logging
|
|
518
|
+
- Truncate UUID for display: `robot_id[0..7]`
|
|
519
|
+
- Full UUID available for debugging
|
|
520
|
+
|
|
521
|
+
---
|
|
522
|
+
|
|
523
|
+
## Future Enhancements
|
|
524
|
+
|
|
525
|
+
### Robot Groups/Teams
|
|
526
|
+
|
|
527
|
+
```ruby
|
|
528
|
+
# Assign robots to teams
|
|
529
|
+
robot_team = db.exec("INSERT INTO robot_teams (robot_id, team_name) VALUES ($1, $2)",
|
|
530
|
+
[robot_id, "coding-team"])
|
|
531
|
+
|
|
532
|
+
# Query by team
|
|
533
|
+
memories = htm.recall(robot_team: "coding-team", topic: "APIs")
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
### Robot Permissions
|
|
537
|
+
|
|
538
|
+
```ruby
|
|
539
|
+
# Role-based access control
|
|
540
|
+
robot_role = db.exec("INSERT INTO robot_roles (robot_id, role) VALUES ($1, $2)",
|
|
541
|
+
[robot_id, "admin"])
|
|
542
|
+
|
|
543
|
+
# Restrict operations by role
|
|
544
|
+
htm.forget(key, confirm: :confirmed) # Requires admin role
|
|
545
|
+
```
|
|
546
|
+
|
|
547
|
+
### Robot Configuration
|
|
548
|
+
|
|
549
|
+
```ruby
|
|
550
|
+
# Per-robot settings
|
|
551
|
+
htm = HTM.new(
|
|
552
|
+
robot_id: uuid,
|
|
553
|
+
robot_name: name,
|
|
554
|
+
robot_config: {
|
|
555
|
+
embedding_model: "custom-model",
|
|
556
|
+
working_memory_size: 256_000,
|
|
557
|
+
preferences: { language: "en" }
|
|
558
|
+
}
|
|
559
|
+
)
|
|
560
|
+
```
|
|
561
|
+
|
|
562
|
+
### Robot Migration Tool
|
|
563
|
+
|
|
564
|
+
```ruby
|
|
565
|
+
# Migrate memories from old robot_id to new one
|
|
566
|
+
HTM::Migration.migrate_robot(
|
|
567
|
+
from: "old-robot-id",
|
|
568
|
+
to: "new-robot-id"
|
|
569
|
+
)
|
|
570
|
+
|
|
571
|
+
# Updates all nodes.robot_id references
|
|
572
|
+
# Merges robot registry entries
|
|
573
|
+
```
|
|
574
|
+
|
|
575
|
+
### Short Robot IDs
|
|
576
|
+
|
|
577
|
+
```ruby
|
|
578
|
+
# Generate shorter IDs (like GitHub: 7 chars)
|
|
579
|
+
short_id = SecureRandom.hex(4) # "3a7b2c4d"
|
|
580
|
+
|
|
581
|
+
htm = HTM.new(
|
|
582
|
+
robot_id: short_id,
|
|
583
|
+
robot_name: "Helper"
|
|
584
|
+
)
|
|
585
|
+
|
|
586
|
+
# Trade-off: Lower collision resistance, better readability
|
|
587
|
+
```
|
|
588
|
+
|
|
589
|
+
---
|
|
590
|
+
|
|
591
|
+
## Alternatives Comparison
|
|
592
|
+
|
|
593
|
+
| Approach | Pros | Cons | Decision |
|
|
594
|
+
|----------|------|------|----------|
|
|
595
|
+
| **UUID + Name** | **Unique + readable** | **Dual identifiers** | **ACCEPTED** |
|
|
596
|
+
| UUID Only | Simple, guaranteed unique | Not human-readable | Rejected |
|
|
597
|
+
| Name Only | Simple, readable | Collision-prone | Rejected |
|
|
598
|
+
| Sequential IDs | Short, sortable | Requires coordination | Rejected |
|
|
599
|
+
| Hostname + PID | Automatic | Not persistent | Rejected |
|
|
600
|
+
| ULID | Sortable, shorter | Less standard | Deferred |
|
|
601
|
+
|
|
602
|
+
---
|
|
603
|
+
|
|
604
|
+
## References
|
|
605
|
+
|
|
606
|
+
- [RFC 4122: UUID Specification](https://tools.ietf.org/html/rfc4122)
|
|
607
|
+
- [UUID v4 (Random)](https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_4_(random))
|
|
608
|
+
- [ULID Specification](https://github.com/ulid/spec)
|
|
609
|
+
- [Robot Registry Pattern](https://martinfowler.com/eaaCatalog/registry.html)
|
|
610
|
+
- [ADR-004: Hive Mind Architecture](004-hive-mind.md)
|
|
611
|
+
- [Multi-Robot Guide](../../guides/multi-robot.md)
|
|
612
|
+
|
|
613
|
+
---
|
|
614
|
+
|
|
615
|
+
## Review Notes
|
|
616
|
+
|
|
617
|
+
**Systems Architect**: UUID + name is the right balance. Consider ULID for future versions.
|
|
618
|
+
|
|
619
|
+
**Database Architect**: Foreign key on robot_id is correct. Index on `nodes.robot_id` for attribution queries.
|
|
620
|
+
|
|
621
|
+
**Ruby Expert**: SecureRandom.uuid is standard. Consider `robot_id: SecureRandom.uuid` as default parameter.
|
|
622
|
+
|
|
623
|
+
**Security Specialist**: UUID v4 is cryptographically secure. No MAC address leakage.
|
|
624
|
+
|
|
625
|
+
**Domain Expert**: Auto-generation + manual override gives flexibility. Document persistence clearly.
|