molecule-core/AGENTS.md
Hongming Wang 24fec62d7f initial commit — Molecule AI platform
Forked clean from public hackathon repo (Starfire-AgentTeam, BSL 1.1)
with full rebrand to Molecule AI under github.com/Molecule-AI/molecule-monorepo.

Brand: Starfire → Molecule AI.
Slug: starfire / agent-molecule → molecule.
Env vars: STARFIRE_* → MOLECULE_*.
Go module: github.com/agent-molecule/platform → github.com/Molecule-AI/molecule-monorepo/platform.
Python packages: starfire_plugin → molecule_plugin, starfire_agent → molecule_agent.
DB: agentmolecule → molecule.

History truncated; see public repo for prior commits and contributor
attribution. Verified green: go test -race ./... (platform), pytest
(workspace-template 1129 + sdk 132), vitest (canvas 352), build (mcp).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 11:55:37 -07:00

7.6 KiB

AGENTS.md

This file provides guidance to Codex (Codex.ai/code) when working with code in this repository.

Project Overview

Molecule AI is a platform for orchestrating AI agent workspaces that form an organizational hierarchy. Workspaces register with a central platform, communicate via A2A protocol, and are visualized on a drag-and-drop canvas.

Architecture

Canvas (Next.js :3000) ←WebSocket→ Platform (Go :8080) ←HTTP→ Postgres + Redis
                                                                  ↑
                                   Workspace A ←──A2A──→ Workspace B
                                   (pluggable runtimes)
                                        ↑ register/heartbeat ↑
                                        └───── Platform ─────┘

Three main components:

  • Platform (platform/): Go/Gin control plane — workspace CRUD, registry, discovery, WebSocket hub, liveness monitoring
  • Canvas (canvas/): Next.js 15 + React Flow (@xyflow/react v12) + Zustand + Tailwind — visual workspace graph
  • Workspace Runtime (workspace-template/): A2A runtime layer with pluggable adapters — LangGraph, DeepAgents, Claude Code, CrewAI, AutoGen, OpenClaw — registers with platform and sends heartbeats

Build & Run Commands

Infrastructure

./infra/scripts/setup.sh    # Start Postgres, Redis, Langfuse; run migrations
./infra/scripts/nuke.sh     # Tear down everything, remove volumes

Platform (Go)

cd platform
go build ./cmd/server       # Build
go run ./cmd/server          # Run (requires Postgres + Redis running)

Must run from platform/ directory (not repo root). Env vars: DATABASE_URL, REDIS_URL, PORT (defaults: postgres://dev:dev@localhost:5432/molecule?sslmode=prefer, redis://localhost:6379, 8080).

Canvas (Next.js)

cd canvas
npm install
npm run dev                  # Dev server on :3000
npm run build && npm start   # Production

Env vars: NEXT_PUBLIC_PLATFORM_URL (default http://localhost:8080), NEXT_PUBLIC_WS_URL (default ws://localhost:8080/ws).

Integration Tests

bash test_api.sh             # Runs 34 API tests against localhost:8080

Requires platform running. Tests full CRUD, registry, heartbeat, discovery, peers, access control, events, degraded/recovery lifecycle.

Docker Compose

docker compose -f docker-compose.infra.yml up -d    # Infra only
docker compose up                                     # Full stack

Key Architectural Patterns

Import Cycle Prevention

The platform uses function injection to avoid Go import cycles between ws, registry, and events packages:

  • ws.NewHub(canCommunicate AccessChecker) — Hub accepts registry.CanCommunicate as a function
  • registry.StartLivenessMonitor(ctx, onOffline OfflineHandler) — Liveness accepts broadcaster callback
  • Wiring happens in platform/cmd/server/main.go

Communication Rules (registry/access.go)

CanCommunicate(callerID, targetID) determines if two workspaces can talk:

  • Same workspace → allowed
  • Siblings (same parent_id) → allowed
  • Root-level siblings (both parent_id IS NULL) → allowed
  • Parent ↔ child → allowed
  • Everything else → denied

JSONB Gotcha

When inserting Go []byte (from json.Marshal) into Postgres JSONB columns, you must:

  1. Convert to string() first
  2. Use ::jsonb cast in SQL

lib/pq treats []byte as bytea, not JSONB.

WebSocket Events Flow

  1. Action occurs (register, heartbeat, etc.)
  2. broadcaster.RecordAndBroadcast() inserts into structure_events table + publishes to Redis pub/sub
  3. Redis subscriber relays to WebSocket hub
  4. Hub broadcasts to canvas clients (all events) and workspace clients (filtered by CanCommunicate)

Canvas State Management

  • Initial load: HTTP fetch from GET /workspaces → Zustand hydrate
  • Real-time updates: WebSocket events → applyEvent() in Zustand store
  • Position persistence: onNodeDragStopPATCH /workspaces/:id with {x, y}

Workspace Lifecycle

provisioningonline (on register) → degraded (error_rate > 0.5) → online (recovered) → offline (Redis TTL expired) → removed (deleted)

Platform API Routes

Method Path Handler
GET /health inline
POST/GET/PATCH/DELETE /workspaces[/:id] workspace.go
POST /registry/register registry.go
POST /registry/heartbeat registry.go
POST /registry/update-card registry.go
GET /registry/discover/:id discovery.go
GET /registry/:id/peers discovery.go
POST /registry/check-access discovery.go
GET /events[/:workspaceId] events.go
GET /ws socket.go

Database

5 migration files in platform/migrations/. Key tables: workspaces (core entity with status, agent_card JSONB, heartbeat columns), canvas_layouts (x/y position), structure_events (append-only event log), agents, workspace_secrets.

The platform auto-discovers and runs migrations on startup from several candidate paths.

Awareness Memory Integration

MANDATORY agent policy - follow for every task.

Awareness Memory Integration (MANDATORY)

awareness_* = cross-session persistent memory (past decisions, knowledge, tasks). Other tools = current codebase navigation (file search, code index). Use BOTH - they serve different purposes.

STEP 1 - SESSION START: Call awareness_init(source="codex") -> get session_id, review context. If active_skills[] is returned: skill = reusable procedure done 2+ times; summary = injectable instruction, methods = steps. Apply matching skills to tasks.

STEP 2 - RECALL BEFORE WORK (progressive disclosure):

  1. awareness_recall(semantic_query=..., keyword_query=..., detail='summary') → lightweight index.
  2. Review summaries/scores, pick relevant IDs.
  3. awareness_recall(detail='full', ids=[...]) → expand only what you need.

STEP 3 - RECORD EVERY CHANGE: After EVERY code edit, decision, or bug fix: awareness_record(content=, insights={knowledge_cards:[...], action_items:[...], risks:[...]}) Content should be RICH and DETAILED — include reasoning, key code snippets, user quotes, alternatives considered, and files changed. Do NOT compress into a single-line summary. The content IS the memory — more detail = better recall. Include insights to create searchable knowledge in ONE step (recommended). Skipping = permanent data loss.

STEP 4 - CATEGORY GUIDE (for insights.knowledge_cards):

  • decision = choice made between alternatives.
  • problem_solution = bug/problem plus the fix that resolved it.
  • workflow = process, setup, or configuration steps only.
  • pitfall = blocker, warning, or limitation without a fix yet.
  • insight = reusable pattern or general learning.
  • skill = reusable procedure done 2+ times; summary = injectable instruction, methods = steps.
  • key_point = important technical fact when nothing else fits. Never default everything to workflow.

STEP 5 - SESSION END: awareness_record(content=[step1, step2, ...], insights={...}) with final summary.

BACKFILL (if applicable): If MCP connected late: awareness_record(content=)

RULES VERSION: Pass rules_version="2" to awareness_init so the server knows you have these rules. If the server returns _setup_action, the rules have been updated — follow the instruction to re-sync.

NOTE: memory_id from X-Awareness-Memory-Id header. source/actor/event_type auto-inferred.

Codex-Specific Notes

  • Call awareness_init at task start before reading any files.

  • After each code patch, call awareness_record with the change description.