Skip to content

Commit 38ca68e

Browse files
authored
Merge pull request #265 from CambrianTech/feature/rust-orm-conversion
Unified Modular Runtime: Consolidate workers into continuum-core + Rust ORM
2 parents f0d842b + c835306 commit 38ca68e

File tree

349 files changed

+17630
-33343
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

349 files changed

+17630
-33343
lines changed

CLAUDE.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,14 @@ npm start # DEPLOYS code changes, takes 130s or so
348348

349349
**IF YOU FORGET `npm start`, THE BROWSER SHOWS OLD CODE!**
350350

351+
**NEVER CALL `cargo build` DIRECTLY!**
352+
- ALL Rust binaries MUST be built via `npm start`
353+
- If you run `cargo build --release` manually, that binary only exists on YOUR machine
354+
- When someone else clones the repo and runs `npm start`, that step doesn't happen
355+
- The repo is BROKEN for everyone except you
356+
- Manual build steps = broken repo for all other users
357+
- If a Rust binary needs to be built, it MUST be wired into the `npm start` build scripts
358+
351359
Don't panic and stash changes first before anything drastic. Use the stash to your advantage and you will be safe from catastrophe. Remember we have git for a reason!
352360

353361
### Chat Commands

docs/COMMAND-SHORTHAND-DESIGN.md

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
# Command Shorthand Design
2+
3+
**Status**: Approved via team decision (2026-02-09)
4+
**Proposal ID**: 13208c93-714a-436d-b681-a2e1b8a71a3a
5+
**Contributors**: Grok, DeepSeek Assistant, Together Assistant, Groq Lightning, Joel
6+
7+
## Overview
8+
9+
Unix-like 2-5 letter command prefixes to reduce context length while maintaining discoverability. This design emerged from collaborative discussion in general chat where multiple AI assistants independently converged on the same modular, hierarchical approach.
10+
11+
## Core Prefixes
12+
13+
| Prefix | Domain | Examples | Replaces |
14+
|--------|--------|----------|----------|
15+
| `cd/` | Code operations | cd/edit, cd/read, cd/diff, cd/verify | code/* |
16+
| `gt/` | Git operations | gt/init, gt/status, gt/commit, gt/push | workspace/git/* |
17+
| `sh/` | Shell operations | sh/exec, sh/stat, sh/watch, sh/kill | code/shell/* |
18+
| `dt/` | Data operations | dt/list, dt/read, dt/query, dt/create | data/* |
19+
| `cl/` | Collaboration | cl/chat, cl/vote, cl/prop, cl/wall | collaboration/* |
20+
| `ai/` | AI operations | ai/gen, ai/rag, ai/status | (already short) |
21+
| `lg/` | Logs | lg/read, lg/search, lg/stats | logs/* |
22+
| `ui/` | Interface | ui/click, ui/screenshot, ui/scroll | interface/* |
23+
| `ws/` | Workspace | ws/list, ws/tree, ws/task | workspace/* |
24+
25+
## Context Savings
26+
27+
Real examples from current command set:
28+
29+
| Shorthand | Full Command | Chars Saved |
30+
|-----------|--------------|-------------|
31+
| `gt/init` | `workspace/git/workspace/init` | **28** |
32+
| `cl/vote` | `collaboration/decision/vote` | **22** |
33+
| `sh/exec` | `code/shell/execute` | **10** |
34+
| `cd/edit` | `code/edit` | 2 |
35+
| `dt/list` | `data/list` | 1 |
36+
37+
**Cumulative impact**: Hundreds of characters saved per session, significant for AI context windows.
38+
39+
## Subcommand Patterns
40+
41+
Keep subcommands short and consistent:
42+
43+
```
44+
cd/rd → code/read
45+
cd/ed → code/edit
46+
cd/wr → code/write
47+
cd/df → code/diff
48+
cd/tr → code/tree
49+
50+
gt/st → workspace/git/status
51+
gt/cm → workspace/git/commit
52+
gt/ps → workspace/git/push
53+
gt/br → workspace/git/branch
54+
55+
sh/x → code/shell/execute
56+
sh/k → code/shell/kill
57+
sh/w → code/shell/watch
58+
59+
dt/ls → data/list
60+
dt/rd → data/read
61+
dt/cr → data/create
62+
dt/rm → data/delete
63+
dt/q → data/query-open
64+
65+
cl/msg → collaboration/chat/send
66+
cl/exp → collaboration/chat/export
67+
cl/vt → collaboration/decision/vote
68+
cl/pr → collaboration/decision/propose
69+
```
70+
71+
## Discovery System
72+
73+
### Help Command
74+
```bash
75+
help cd # List all code commands
76+
help gt # List all git commands
77+
help # List all prefixes with descriptions
78+
```
79+
80+
### Search Command
81+
```bash
82+
search edit # Find all edit-related commands
83+
search vector # Find all vector-related commands
84+
```
85+
86+
### Tab Completion
87+
- Type `cd/` + TAB → shows all code subcommands
88+
- Type `gt/s` + TAB → completes to `gt/status` or shows options
89+
90+
## Implementation Phases
91+
92+
### Phase 1: Core Prefixes (Immediate)
93+
- `cd/` for code operations (most frequently used)
94+
- `gt/` for git operations (highest char savings)
95+
- `sh/` for shell operations
96+
97+
### Phase 2: Data & Collaboration
98+
- `dt/` for data operations
99+
- `cl/` for collaboration
100+
- `lg/` for logs
101+
102+
### Phase 3: Discovery System
103+
- Help command with prefix listings
104+
- Search command for keyword lookup
105+
- Tab completion (CLI enhancement)
106+
107+
### Phase 4: Migration
108+
- Backward compatibility aliases (old commands still work)
109+
- Deprecation warnings for verbose forms
110+
- Documentation updates
111+
- Gradual transition timeline
112+
113+
## Architecture
114+
115+
### Alias Resolution Layer
116+
```
117+
User Input → Alias Resolver → Full Command → Command Router → Handler
118+
↓ ↓ ↓ ↓ ↓
119+
"cd/ed" → "code/edit" → lookup → dispatch → execute
120+
```
121+
122+
### Registration Pattern
123+
```typescript
124+
// In command registration
125+
CommandRegistry.registerAlias('cd/ed', 'code/edit');
126+
CommandRegistry.registerAlias('gt/st', 'workspace/git/status');
127+
128+
// Or via prefix mapping
129+
const PREFIX_MAP = {
130+
'cd/': 'code/',
131+
'gt/': 'workspace/git/',
132+
'sh/': 'code/shell/',
133+
'dt/': 'data/',
134+
'cl/': 'collaboration/',
135+
} as const;
136+
```
137+
138+
### Constants (Single Source of Truth)
139+
```typescript
140+
// system/shared/CommandPrefixes.ts
141+
export const CMD_PREFIX = {
142+
CODE: 'cd',
143+
GIT: 'gt',
144+
SHELL: 'sh',
145+
DATA: 'dt',
146+
COLLAB: 'cl',
147+
AI: 'ai',
148+
LOGS: 'lg',
149+
UI: 'ui',
150+
WORKSPACE: 'ws',
151+
} as const;
152+
153+
export type CmdPrefix = typeof CMD_PREFIX[keyof typeof CMD_PREFIX];
154+
```
155+
156+
## Design Principles
157+
158+
1. **Unix Philosophy**: Short, memorable, composable commands
159+
2. **Discoverability**: Help/search prevents "where's that tool?" frustration
160+
3. **Backward Compatible**: Old verbose commands continue to work
161+
4. **Hierarchical**: Prefixes map to logical domains
162+
5. **Consistent**: Same patterns across all prefixes
163+
6. **Context-Optimized**: Reduces token usage for AI assistants
164+
165+
## Team Discussion Highlights
166+
167+
> "Keeps things snappy like Unix (ls over list-files, right?)" — Grok
168+
169+
> "gt/init alone shaves off a chunk that adds up fast in long sessions" — DeepSeek
170+
171+
> "I prefer shorthands like we do with unix commands, which will cut down on context length for you" — Joel
172+
173+
> "Multiple AI assistants independently converged on the same modular, hierarchical strategy" — DeepSeek (noting emergent consensus)
174+
175+
## Decision Outcome
176+
177+
- **Winner**: Option 1 (cd/ for code operations as foundation)
178+
- **Confidence**: 5/5 pairwise victories
179+
- **Consensus**: 100% agreement among voters
180+
- **Next Step**: Prototype in test branch, validate, then expand
181+
182+
---
183+
184+
*Document generated from team discussion in general chat, 2026-02-09*

src/debug/jtag/commands/ai/cost/server/AICostServerCommand.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,14 +42,14 @@ export class AICostServerCommand extends AICostCommand {
4242
}
4343

4444
// Query AIGenerationEntity from database using data/list - let SQL do the filtering
45+
// Note: omitting 'limit' means no limit (Rust ORM Option<usize> defaults to None)
4546
const listParams = createDataListParams(
4647
params.context,
4748
params.sessionId,
4849
{
4950
collection: 'ai_generations',
5051
filter,
51-
orderBy: [{ field: 'timestamp', direction: 'desc' }],
52-
limit: -1 // Get ALL records (no pagination for aggregate queries)
52+
orderBy: [{ field: 'timestamp', direction: 'desc' }]
5353
}
5454
);
5555

src/debug/jtag/commands/ai/generate/server/AIGenerateServerCommand.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import { paramsToRequest, responseToResult, createErrorResult, createAIGenerateR
1414
import { AIProviderDaemon } from '../../../../daemons/ai-provider-daemon/shared/AIProviderDaemon';
1515
import { RAGBuilderFactory } from '../../../../system/rag/shared/RAGBuilder';
1616
import { ChatRAGBuilder } from '../../../../system/rag/builders/ChatRAGBuilder';
17-
import { DataDaemon } from '../../../../daemons/data-daemon/shared/DataDaemon';
17+
import { ORM } from '../../../../daemons/data-daemon/server/ORM';
1818
import { UserEntity } from '../../../../system/data/entities/UserEntity';
1919
import type { TextGenerationRequest } from '../../../../daemons/ai-provider-daemon/shared/AIProviderTypesV2';
2020
import { SystemPaths } from '../../../../system/core/config/SystemPaths';
@@ -43,7 +43,7 @@ export class AIGenerateServerCommand extends AIGenerateCommand {
4343
let targetPersonaId = params.personaId;
4444
let personaDisplayName = 'ai-generate-command'; // Fallback name for tracking
4545
if (!targetPersonaId) {
46-
const usersResult = await DataDaemon.query<UserEntity>({
46+
const usersResult = await ORM.query<UserEntity>({
4747
collection: UserEntity.collection,
4848
filter: { type: 'persona' },
4949
limit: 1

src/debug/jtag/commands/ai/rag/inspect/server/RAGInspectServerCommand.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import type { JTAGContext } from '../../../../../system/core/types/JTAGTypes';
99
import type { ICommandDaemon } from '../../../../../daemons/command-daemon/shared/CommandBase';
1010
import type { RAGInspectParams, RAGInspectResult } from '../shared/RAGInspectTypes';
1111
import { ChatRAGBuilder } from '../../../../../system/rag/builders/ChatRAGBuilder';
12-
import { DataDaemon } from '../../../../../daemons/data-daemon/shared/DataDaemon';
12+
import { ORM } from '../../../../../daemons/data-daemon/server/ORM';
1313
import { ChatMessageEntity } from '../../../../../system/data/entities/ChatMessageEntity';
1414
import { getThoughtStreamCoordinator } from '../../../../../system/conversation/server/ThoughtStreamCoordinator';
1515
import type { Thought } from '../../../../../system/conversation/shared/ConversationCoordinationTypes';
@@ -101,7 +101,7 @@ export class RAGInspectServerCommand extends RAGInspectCommand {
101101
if (params.triggerMessageId) {
102102
try {
103103
// Load the trigger message
104-
const msg = await DataDaemon.read<ChatMessageEntity>(ChatMessageEntity.collection, params.triggerMessageId);
104+
const msg = await ORM.read<ChatMessageEntity>(ChatMessageEntity.collection, params.triggerMessageId);
105105
if (msg) {
106106

107107
// Get actual decision from ThoughtStream

src/debug/jtag/commands/ai/sleep/server/AiSleepServerCommand.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -174,12 +174,20 @@ export class AiSleepServerCommand extends CommandBase<AiSleepParams, AiSleepResu
174174
});
175175
}
176176

177-
// Get persona ID - use explicit personaId, or callerId (injected by PersonaToolExecutor),
178-
// or fall back to UserIdentityResolver for external callers (CLI, etc.)
179-
let targetPersonaId = personaId || (params as any).callerId;
177+
// Get persona ID - priority: context.userId, then explicit personaId, then callerId, then UserIdentityResolver
178+
// Priority:
179+
// 1. params.context?.userId - When a PersonaUser executes, their ID is in context
180+
// 2. Explicit personaId param - For backwards compatibility
181+
// 3. callerId (injected) - Legacy PersonaToolExecutor injection
182+
// 4. UserIdentityResolver - Fallback for CLI calls
183+
let targetPersonaId = params.context?.userId || personaId || (params as any).callerId;
184+
185+
if (params.context?.userId) {
186+
console.log(`😴 ai/sleep: Using context.userId: ${params.context.userId}`);
187+
}
180188

181189
if (!targetPersonaId) {
182-
// Fall back to UserIdentityResolver for external callers (CLI, Claude Code, etc.)
190+
// FALLBACK: Use UserIdentityResolver for external callers (CLI, Claude Code, etc.)
183191
const identity = await UserIdentityResolver.resolve();
184192
if (identity.exists && identity.userId) {
185193
targetPersonaId = identity.userId;

src/debug/jtag/commands/ai/status/server/AIStatusServerCommand.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import type { JTAGContext } from '../../../../system/core/types/JTAGTypes';
99
import type { ICommandDaemon } from '../../../../daemons/command-daemon/shared/CommandBase';
1010
import type { AIStatusParams, AIStatusResult, PersonaHealth } from '../shared/AIStatusTypes';
1111
import { UserDaemonServer } from '../../../../daemons/user-daemon/server/UserDaemonServer';
12-
import { DataDaemon } from '../../../../daemons/data-daemon/shared/DataDaemon';
12+
import { ORM } from '../../../../daemons/data-daemon/server/ORM';
1313
import { COLLECTIONS } from '../../../../system/data/config/DatabaseConfig';
1414
import type { UserEntity } from '../../../../system/data/entities/UserEntity';
1515
import type { PersonaUser } from '../../../../system/user/server/PersonaUser';
@@ -61,7 +61,7 @@ export class AIStatusServerCommand extends AIStatusCommand {
6161
private async executeWithDaemon(userDaemon: UserDaemonServer, params: AIStatusParams): Promise<AIStatusResult> {
6262

6363
// Query all PersonaUser entities from database
64-
const result = await DataDaemon.query<UserEntity>({
64+
const result = await ORM.query<UserEntity>({
6565
collection: COLLECTIONS.USERS,
6666
filter: { type: 'persona' }
6767
});

src/debug/jtag/commands/ai/thoughtstream/server/ThoughtStreamServerCommand.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import type { JTAGContext } from '../../../../system/core/types/JTAGTypes';
1414
import type { ICommandDaemon } from '../../../../daemons/command-daemon/shared/CommandBase';
1515
import { getThoughtStreamCoordinator } from '../../../../system/conversation/server/ThoughtStreamCoordinator';
1616
import { RAGBuilderFactory } from '../../../../system/rag/shared/RAGBuilder';
17-
import { DataDaemon } from '../../../../daemons/data-daemon/shared/DataDaemon';
17+
import { ORM } from '../../../../daemons/data-daemon/server/ORM';
1818
import { COLLECTIONS } from '../../../../system/data/config/DatabaseConfig';
1919
import type { ChatMessageEntity } from '../../../../system/data/entities/ChatMessageEntity';
2020
import type { UserEntity } from '../../../../system/data/entities/UserEntity';
@@ -74,7 +74,7 @@ export class ThoughtStreamServerCommand extends ThoughtStreamCommand {
7474

7575
try {
7676
// Query data daemon for the message
77-
const msg = await DataDaemon.read<ChatMessageEntity>(
77+
const msg = await ORM.read<ChatMessageEntity>(
7878
COLLECTIONS.CHAT_MESSAGES,
7979
stream.messageId
8080
);
@@ -497,7 +497,7 @@ export class ThoughtStreamServerCommand extends ThoughtStreamCommand {
497497

498498
try {
499499
// Query user collection by displayName field
500-
const result = await DataDaemon.query<UserEntity>({
500+
const result = await ORM.query<UserEntity>({
501501
collection: COLLECTIONS.USERS,
502502
filter: { displayName: name },
503503
limit: 1
@@ -583,7 +583,7 @@ export class ThoughtStreamServerCommand extends ThoughtStreamCommand {
583583

584584
private async getPersonaName(personaId: string, params: ThoughtStreamParams): Promise<string> {
585585
try {
586-
const user = await DataDaemon.read<UserEntity>(
586+
const user = await ORM.read<UserEntity>(
587587
COLLECTIONS.USERS,
588588
personaId
589589
);

src/debug/jtag/commands/canvas/stroke/add/server/CanvasStrokeAddServerCommand.ts

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ import { createCanvasStrokeAddResult, CANVAS_STROKE_EVENTS } from '../shared/Can
1111
import { CanvasStrokeEntity } from '@system/data/entities/CanvasStrokeEntity';
1212
import { ChatMessageEntity } from '@system/data/entities/ChatMessageEntity';
1313
import { RoomEntity } from '@system/data/entities/RoomEntity';
14-
import { DataDaemon } from '@daemons/data-daemon/shared/DataDaemon';
14+
import { UserEntity } from '@system/data/entities/UserEntity';
15+
import { ORM } from '@daemons/data-daemon/server/ORM';
1516
import { Events } from '@system/core/shared/Events';
1617
import { Commands } from '@system/core/shared/Commands';
1718
import { COLLECTIONS } from '@system/shared/Constants';
@@ -49,10 +50,30 @@ export class CanvasStrokeAddServerCommand extends CommandBase<CanvasStrokeAddPar
4950
}
5051

5152
try {
52-
// Get creator info - use UserIdentityResolver to detect caller (Claude Code, Joel, etc.)
53-
const identity = await UserIdentityResolver.resolve();
54-
const creatorId = identity.userId || strokeParams.sessionId;
55-
const creatorName = identity.displayName;
53+
// Get creator info - priority: context.userId (PersonaUsers), then UserIdentityResolver (CLI)
54+
let creatorId: string;
55+
let creatorName: string;
56+
57+
if (strokeParams.context?.userId) {
58+
// FIRST: Check context.userId (PersonaUsers set this)
59+
creatorId = strokeParams.context.userId;
60+
// We need to look up the name from the database
61+
const userResult = await DataList.execute<UserEntity>({
62+
collection: UserEntity.collection,
63+
filter: { id: creatorId },
64+
limit: 1,
65+
context: strokeParams.context,
66+
sessionId: strokeParams.sessionId
67+
});
68+
creatorName = userResult.success && userResult.items && userResult.items.length > 0
69+
? userResult.items[0].displayName
70+
: 'Unknown';
71+
} else {
72+
// FALLBACK: Use UserIdentityResolver (CLI, Claude Code, Joel, etc.)
73+
const identity = await UserIdentityResolver.resolve();
74+
creatorId = identity.userId || strokeParams.sessionId;
75+
creatorName = identity.displayName;
76+
}
5677

5778
// Create stroke entity
5879
const stroke = new CanvasStrokeEntity();
@@ -81,7 +102,7 @@ export class CanvasStrokeAddServerCommand extends CommandBase<CanvasStrokeAddPar
81102
}
82103

83104
// Save to database (throws on failure, returns entity on success)
84-
await DataDaemon.store(COLLECTIONS.CANVAS_STROKES, stroke);
105+
await ORM.store(COLLECTIONS.CANVAS_STROKES, stroke);
85106

86107
// Emit real-time event for all clients to sync
87108
Events.emit(CANVAS_STROKE_EVENTS.STROKE_ADDED, {

0 commit comments

Comments
 (0)