molecule-core/plugins/molecule-dev/rules/codebase-conventions.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

4.4 KiB

Molecule AI Codebase Conventions

These rules apply to every agent working on the Molecule AI / Molecule AI codebase. They are lessons learned from real bugs — not style preferences. Violating them causes production failures.

Canvas (Next.js 15 App Router)

'use client' — NON-NEGOTIABLE

Every .tsx file in canvas/src/ that uses React hooks (useState, useEffect, useCallback, useMemo, useRef), Zustand stores (useCanvasStore, useSecretsStore), or event handlers (onClick, onChange) MUST have 'use client'; as the very first line. Without it, Next.js renders the component as server HTML and React never hydrates it — buttons appear but silently don't respond to clicks.

This has caused two reverted PRs. Always run this check before reporting done:

cd canvas
for f in $(grep -rl "useState\|useEffect\|useCallback\|useMemo\|useRef\|useStore\|onClick\|onChange" src/ --include="*.tsx"); do
  head -3 "$f" | grep -q "use client" || echo "MISSING 'use client': $f"
done

Zustand Selectors — No New Objects

Never call a function that returns a new object inside a Zustand selector:

// BAD — creates new object every render → infinite re-renders
const grouped = useSecretsStore((s) => s.getGrouped());

// GOOD — use useMemo with stable selector values
const secrets = useSecretsStore((s) => s.secrets);
const grouped = useMemo(() => groupSecrets(secrets), [secrets]);

Dark Zinc Theme — No Light Colors

The canvas is dark-themed. Every new component must use:

  • Backgrounds: zinc-900, zinc-950, #18181b, #09090b
  • Text: zinc-300, zinc-400, #d4d4d8, #a1a1aa
  • Accents: blue-500, blue-600, violet-500
  • Borders: zinc-700, zinc-800
  • Never: white, #ffffff, #f5f5f5, or any light gray

API Response Shapes

Always verify the actual platform API response format before writing fetch code. Check the Go handler or test with curl — don't assume. Past bug: FE assumed GET /settings/secrets returned a flat array but it returns { secrets: [...] }.

Platform (Go)

SQL Safety

  • Always use parameterized queries ($1, $2), never string concatenation
  • Use ExecContext / QueryContext with context, never bare Exec / Query
  • Always check rows.Err() after iterating result sets
  • JSONB: convert []byte to string() and use ::jsonb cast

Access Control

  • Every endpoint touching workspace data must verify ownership
  • A2A proxy calls go through CanCommunicate() — new proxy paths must respect it
  • System callers (webhook:*, system:*, test:*) bypass via isSystemCaller()

Container Lifecycle

  • Use ContainerRemove(Force: true) to stop containers — never ContainerStop + ContainerRemove separately (restart policy race condition causes zombies)
  • Always reap zombie processes: proc.wait() after proc.kill() with a timeout

Workspace Runtime (Python)

Error Sanitization

Never emit raw exception messages or subprocess stderr to the user. Use sanitize_agent_error() which exposes the exception class name but strips the message body (which can leak tokens, paths, and stack traces).

System Prompt Hot-Reload

System prompts are re-read from /configs/system-prompt.md on every message. Always use encoding="utf-8", errors="replace" when reading prompt files.

Inter-Agent Communication

Parallel Delegation

Use delegate_task_async to send tasks to multiple peers simultaneously. Don't serialize what can be parallel — fire all async delegations, then poll check_task_status to collect results as they finish.

Side Questions

Any agent can ask a peer a direct question via delegate_task (sync) without going through the lead. FE can ask BE "what's the API response format?" mid-task. Use this to avoid guessing — it's faster than getting it wrong.

Proactive Updates

Use send_message_to_user to push status updates to the CEO's chat at any time. Don't wait until everything is done to report — acknowledge immediately, update during long work, deliver results when complete.

Before Reporting Done

Every agent, regardless of role, must verify their own work before claiming completion:

  1. Read back every file you changed — confirm it looks right
  2. Run the relevant test suite (npm test, go test, python -m pytest)
  3. Run the relevant build (npm run build, go build)
  4. Check for framework-specific gotchas (the 'use client' grep above)
  5. If you can imagine a way your change could break, test that scenario