From af4e0faeefe08e90d072c8a78344a1ccd9f11c7b Mon Sep 17 00:00:00 2001 From: Hongming Wang Date: Wed, 6 May 2026 13:53:26 -0700 Subject: [PATCH] import from local vendored copy (2026-05-06) --- .github/workflows/ci.yml | 5 + .gitignore | 21 ++++ .molecule-ci/scripts/requirements.txt | 1 + .molecule-ci/scripts/validate-plugin.py | 52 ++++++++ CLAUDE.md | 155 ++++++++++++++++++++++++ README.md | 19 +++ adapters/claude_code.py | 2 + adapters/deepagents.py | 2 + known-issues.md | 97 +++++++++++++++ plugin.yaml | 16 +++ rules/codebase-conventions.md | 101 +++++++++++++++ runbooks/local-dev-setup.md | 73 +++++++++++ skills/review-loop/SKILL.md | 78 ++++++++++++ 13 files changed, 622 insertions(+) create mode 100644 .github/workflows/ci.yml create mode 100644 .gitignore create mode 100644 .molecule-ci/scripts/requirements.txt create mode 100644 .molecule-ci/scripts/validate-plugin.py create mode 100644 CLAUDE.md create mode 100644 README.md create mode 100644 adapters/claude_code.py create mode 100644 adapters/deepagents.py create mode 100644 known-issues.md create mode 100644 plugin.yaml create mode 100644 rules/codebase-conventions.md create mode 100644 runbooks/local-dev-setup.md create mode 100644 skills/review-loop/SKILL.md diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..c8fb9d3 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,5 @@ +name: CI +on: [push, pull_request] +jobs: + validate: + uses: Molecule-AI/molecule-ci/.github/workflows/validate-plugin.yml@main diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2af45b5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,21 @@ +# Credentials — never commit. Use .env.example as the template. +.env +.env.local +.env.*.local +.env.* +!.env.example +!.env.sample + +# Private keys + certs +*.pem +*.key +*.crt +*.p12 +*.pfx + +# Secret directories +.secrets/ + +# Workspace auth tokens +.auth-token +.auth_token diff --git a/.molecule-ci/scripts/requirements.txt b/.molecule-ci/scripts/requirements.txt new file mode 100644 index 0000000..3aecde9 --- /dev/null +++ b/.molecule-ci/scripts/requirements.txt @@ -0,0 +1 @@ +pyyaml>=6.0 diff --git a/.molecule-ci/scripts/validate-plugin.py b/.molecule-ci/scripts/validate-plugin.py new file mode 100644 index 0000000..c42e916 --- /dev/null +++ b/.molecule-ci/scripts/validate-plugin.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python3 +"""Validate a Molecule AI plugin repo.""" +import os, sys, yaml + +errors = [] + +# 1. plugin.yaml exists +if not os.path.isfile("plugin.yaml"): + print("::error::plugin.yaml not found at repo root") + sys.exit(1) + +with open("plugin.yaml") as f: + plugin = yaml.safe_load(f) + +# 2. Required fields +for field in ["name", "version", "description"]: + if not plugin.get(field): + errors.append(f"Missing required field: {field}") + +# 3. Version format +v = str(plugin.get("version", "")) +if v and not all(c in "0123456789." for c in v): + errors.append(f"Invalid version format: {v}") + +# 4. Runtimes type +runtimes = plugin.get("runtimes") +if runtimes is not None and not isinstance(runtimes, list): + errors.append(f"runtimes must be a list, got {type(runtimes).__name__}") + +# 5. Has content +content_paths = ["SKILL.md", "hooks", "skills", "rules"] +found = [p for p in content_paths if os.path.exists(p)] +if not found: + errors.append("Plugin must contain at least one of: SKILL.md, hooks/, skills/, rules/") + +# 6. SKILL.md formatting check +if os.path.isfile("SKILL.md"): + with open("SKILL.md") as f: + first_line = f.readline().strip() + if first_line and not first_line.startswith("#"): + print("::warning::SKILL.md should start with a markdown heading (e.g., # Plugin Name)") + +if errors: + for e in errors: + print(f"::error::{e}") + sys.exit(1) + +print(f"✓ plugin.yaml valid: {plugin['name']} v{plugin['version']}") +if found: + print(f" Content: {', '.join(found)}") +if runtimes: + print(f" Runtimes: {', '.join(runtimes)}") diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..74d57c4 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,155 @@ +# molecule-dev — Molecule AI Codebase Conventions Plugin + +`molecule-dev` is a **platform conventions plugin** that captures hard-won lessons from Molecule AI's production codebase. Rather than repeating mistakes, every agent working on the platform benefits from rules derived from real bugs and production failures. + +**Version:** 1.0.0 +**Runtime:** `claude_code`, `deepagents`, `hermes` + +--- + +## Repository Layout + +``` +molecule-dev/ +├── plugin.yaml — Plugin manifest (runtimes, rules, skills) +├── rules/ +│ └── codebase-conventions.md — Platform-wide conventions (Canvas/Go/Python) +├── skills/ +│ └── review-loop/SKILL.md — Multi-round review orchestration workflow +└── adapters/ — Harness adaptors (thin wrappers) +``` + +--- + +## Conventions Summary + +See `rules/codebase-conventions.md` for the full ruleset. Key highlights: + +### Canvas (Next.js 15 App Router) + +- **`'use client'` is non-negotiable** — every `.tsx` file using React hooks or event handlers must declare it as the very first line. +- **Zustand selectors**: never call a function returning a new object inside a selector — use `useMemo` instead. +- **Dark zinc theme** — backgrounds `zinc-900`/`zinc-950`, text `zinc-300`/`zinc-400`. Never use `#ffffff` or light colors. +- **Verify API response shapes** with `curl` or Go handler inspection — don't assume. + +### Platform (Go) + +- **SQL safety**: always parameterized queries (`$1`, `$2`), always `ExecContext`/`QueryContext`. +- **Access control**: every workspace endpoint must verify ownership; new A2A proxy paths must pass `CanCommunicate()`. +- **Container lifecycle**: use `ContainerRemove(Force: true)` — never `ContainerStop` + `ContainerRemove` separately. + +### Workspace Runtime (Python) + +- **Error sanitization**: use `sanitize_agent_error()` — never emit raw exception messages or subprocess stderr to the user. +- **System prompt hot-reload**: always use `encoding="utf-8", errors="replace"` when reading prompt files. + +--- + +## Development + +### Prerequisites + +- Node.js >= 18 (for markdownlint) +- Python 3.11+ (for `adapters/`) +- `gh` CLI authenticated +- Write access to `Molecule-AI/molecule-ai-plugin-molecule-dev` + +### Setup + +```bash +git clone https://github.com/Molecule-AI/molecule-ai-plugin-molecule-dev.git +cd molecule-ai-plugin-molecule-dev + +# Install markdownlint CLI for pre-commit linting +npm install -g markdownlint-cli + +# Validate the plugin structure +gh workflow run validate-plugin.yml --ref main +``` + +### Adapter Dev + +```bash +python3 -m venv .venv && source .venv/bin/activate +pip install plugins_registry + +python -c "from adapters.claude_code import Adaptor; print('OK')" +``` + +### Pre-Commit Checklist + +```bash +# Markdown lint +npx markdownlint '**/*.md' --ignore node_modules + +# YAML validation +python3 -c "import yaml; yaml.safe_load(open('plugin.yaml'))" + +# No pycache committed +find . -name '__pycache__' -o -name '*.pyc' | grep -v .gitignore && exit 1 +``` + +--- + +## Test Commands + +There are no unit tests in this plugin — it is a configuration-only package. Validation is done by the shared `validate-plugin.yml` workflow (see CI). + +To run validation locally: +```bash +gh workflow run validate-plugin.yml --ref main +``` + +To validate `plugin.yaml` structure: +```bash +python3 -c " +import yaml, os +with open('plugin.yaml') as f: + data = yaml.safe_load(f) +for key in ['rules', 'skills']: + for ref in data.get(key, []): + path = ref if ref.endswith('.md') else ref + '.md' + print(f'[OK] {path}' if os.path.exists(path) else f'[MISSING] {path}') +" +``` + +--- + +## Release Process + +1. **Review changes** — `git log origin/main..HEAD --oneline` +2. **Validate** — run `npx markdownlint` on all `.md` files +3. **Bump version** in `plugin.yaml` +4. **Commit** — `chore: bump version to X.Y.Z` +5. **Tag** — `git tag -a vX.Y.Z -m "Release vX.Y.Z"` and push +6. **Create GitHub Release** with a changelog section +7. **Verify CI green** on the release commit + +--- + +## Key Contacts + +| Role | Owner | +|------|-------| +| Platform architecture (Go/Canvas) | Molecule AI Engineering | +| Workspace runtime (Python) | Molecule AI Engineering | +| Plugin maintenance | Molecule AI Engineering | + +For questions, bugs, or suggestions, open an issue in this repo or reach out via the Molecule AI internal channels. + +--- + +## Adding a New Convention + +1. Create `rules/.md` describing the convention. +2. Add it to `rules:` in `plugin.yaml` (and add `rules/` section if not present). +3. If it affects canvas work, include a concrete code example (BAD / GOOD pattern). +4. Run markdownlint validation before committing. + +--- + +## Adding a New Skill + +1. Create `skills//SKILL.md` with YAML frontmatter (`name`, `description`). +2. Add it to `skills:` in `plugin.yaml`. +3. Skills are the canonical workflow surface — prefer skills over commands. diff --git a/README.md b/README.md new file mode 100644 index 0000000..7ba83f2 --- /dev/null +++ b/README.md @@ -0,0 +1,19 @@ +# molecule-dev + +Molecule AI plugin. Install via the Molecule AI platform plugin system. + +## Usage + +### In org template (org.yaml) +```yaml +plugins: + - molecule-dev +``` + +### From URL (community install) +``` +github://Molecule-AI/molecule-ai-plugin-molecule-dev +``` + +## License +Business Source License 1.1 — © Molecule AI. diff --git a/adapters/claude_code.py b/adapters/claude_code.py new file mode 100644 index 0000000..dc33217 --- /dev/null +++ b/adapters/claude_code.py @@ -0,0 +1,2 @@ +"""Claude Code adaptor — uses the generic rule+skill installer.""" +from plugins_registry.builtins import AgentskillsAdaptor as Adaptor # noqa: F401 diff --git a/adapters/deepagents.py b/adapters/deepagents.py new file mode 100644 index 0000000..9572dfb --- /dev/null +++ b/adapters/deepagents.py @@ -0,0 +1,2 @@ +"""DeepAgents adaptor — uses the generic rule+skill installer.""" +from plugins_registry.builtins import AgentskillsAdaptor as Adaptor # noqa: F401 diff --git a/known-issues.md b/known-issues.md new file mode 100644 index 0000000..eccf2ca --- /dev/null +++ b/known-issues.md @@ -0,0 +1,97 @@ +# Known Issues + +Active and recently resolved issues for `molecule-dev`. + +--- + +## Active Issues + +*(None currently open. This section is updated when issues are filed.)* + +--- + +## Recently Resolved + +### [RESOLVED] plugin.yaml Merge Conflict Residue + +**Severity:** P2 +**Resolved in:** v1.0.0 + +**Symptoms:** `plugin.yaml` contained residual merge conflict markers (`<<<<<<<`, `=======`, `>>>>>>>`) that caused YAML parsing to fail. The `runtimes:` section listed `claude_code`, `deepagents` in the main branch while the working copy attempted to add `hermes`. + +**Cause:** A `git stash` operation was used to temporarily discard local changes; on `git stash pop`, the conflicted state was re-applied and committed without conflict resolution. + +**Fix:** Resolved by overwriting `plugin.yaml` with clean, conflict-free content. The file now has 3 runtimes declared and the `rules:` section populated. + +**Prevention:** Never `git add` a file containing conflict markers. Use `git add ` only after `<<<<<<<` markers are fully removed, or use `git mergetool`. + +--- + +## Known Gotchas + +These are not bugs but behaviors that may surprise contributors: + +### `rules/` section missing from plugin.yaml + +**Symptoms:** Adding a new rule file and referencing it in `plugin.yaml` may silently fail if the `rules:` key is absent. + +**Workaround:** Verify `plugin.yaml` contains the top-level `rules:` key before adding rule references. If absent, add it: + +```yaml +rules: + - rules/codebase-conventions.md + - rules/.md +``` + +### `hermes` runtime requires adapter + +**Symptoms:** Installing the plugin for a `hermes` workspace runtime works, but at runtime the adapter import may fail if `plugins_registry` is not installed in the harness environment. + +**Workaround:** This is expected in isolated dev environments. The harness handles adapter resolution at runtime. See `runbooks/local-dev-setup.md` in the repo for local adapter testing. + +### No local test runner + +**Symptoms:** `npm test` / `python -m pytest` return no results — there are no unit tests in this plugin. + +**Workaround:** This is by design — the plugin is a configuration package. Validation is done by the shared `validate-plugin.yml` workflow in CI. Run it locally via `gh workflow run validate-plugin.yml --ref main`. + +--- + +## How to Update This File + +When a new issue is discovered: + +1. Add it under **Active Issues** using the template below. +2. Include: symptom, cause (if known), workaround, and fix (if resolved). +3. When fixed, move it to **Recently Resolved** and note the fix version. + +### Issue Template + +```markdown +## [TICKET-NUMBER] + +**Severity:** P0 / P1 / P2 / P3 +**Status:** Workaround / Fix in progress / Fix available / Resolved in vX.Y.Z + +**Symptoms:** +**Cause:** +**Workaround:** +**Fix (if available):** +``` + +--- + +## Severity Definitions + +| Level | Description | +|-------|-------------| +| P0 | Plugin fails to load entirely; no workaround | +| P1 | Core convention violated; can cause production failures | +| P2 | Non-core issue; minor impact or workaround available | +| P3 | Cosmetic; edge case; informational | + +--- + +## Reporting + +Use the Molecule-AI/internal issue tracker. Tag issues with `plugin-molecule-dev`. diff --git a/plugin.yaml b/plugin.yaml new file mode 100644 index 0000000..d1b6715 --- /dev/null +++ b/plugin.yaml @@ -0,0 +1,16 @@ +name: molecule-dev +version: 1.0.0 +description: Molecule AI codebase conventions, past bugs, quality standards, and coordination workflows +author: Molecule AI +tags: [conventions, quality, coordination, codebase] + +runtimes: + - claude_code + - deepagents + - hermes + +rules: + - rules/codebase-conventions.md + +skills: + - review-loop diff --git a/rules/codebase-conventions.md b/rules/codebase-conventions.md new file mode 100644 index 0000000..87013a0 --- /dev/null +++ b/rules/codebase-conventions.md @@ -0,0 +1,101 @@ +# 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: +```bash +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: +```typescript +// 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 diff --git a/runbooks/local-dev-setup.md b/runbooks/local-dev-setup.md new file mode 100644 index 0000000..df65c42 --- /dev/null +++ b/runbooks/local-dev-setup.md @@ -0,0 +1,73 @@ +# Local Development Setup + +This runbook covers setting up a local development environment for +`molecule-dev`. + +--- + +## Prerequisites + +- Python 3.11+ +- `gh` CLI authenticated +- Write access to `Molecule-AI/molecule-ai-plugin-molecule-dev` + +--- + +## Clone & Bootstrap + +```bash +git clone https://github.com/Molecule-AI/molecule-ai-plugin-molecule-dev.git +cd molecule-ai-plugin-molecule-dev +``` + +--- + +## Validating Plugin Structure + +```bash +python3 -c "import yaml; yaml.safe_load(open('plugin.yaml'))" +echo "plugin.yaml OK" +``` + +--- + +## Testing the Dev Tools + +Run the test suite to verify all dev tooling is functional: + +```bash +python3 -m pytest tests/ -v +``` + +--- + +## Writing New Rules or Skills + +1. Create `rules//RULES.md` or `skills//SKILL.md` +2. Follow the format of existing rules/skills in the repo +3. Validate the plugin structure (above) +4. Add tests in `tests/` if applicable + +--- + +## Troubleshooting + +### "No module named yaml" + +Install PyYAML: +```bash +pip install pyyaml +``` + +### Plugin validation fails + +- Ensure `plugin.yaml` has all required fields: `name`, `version`, `description` +- Ensure at least one of: `SKILL.md`, `hooks/`, `skills/`, `rules/` +- Check the schema with: `python3 -c "import yaml; yaml.safe_load(open('plugin.yaml'))"` + +--- + +## Related + +- `skills/molecule-dev/SKILL.md` — dev tooling documentation +- `rules/` — existing rules \ No newline at end of file diff --git a/skills/review-loop/SKILL.md b/skills/review-loop/SKILL.md new file mode 100644 index 0000000..0363a31 --- /dev/null +++ b/skills/review-loop/SKILL.md @@ -0,0 +1,78 @@ +--- +name: review-loop +description: "Orchestrate a multi-round implementation + review cycle. Use when coordinating a feature that requires implementation (FE/BE), design review (UIUX), security review, and QA verification. Ensures QA findings get routed back for fixes until clean." +--- + +# Review Loop + +Orchestrate implementation through multiple rounds until QA reports zero issues. +This prevents the one-shot delegation problem where QA finds bugs but nobody +fixes them. + +## When to Use + +Use this when you're a coordinator (Dev Lead, PM) assigning a feature that +involves multiple specialists. + +## The Loop + +### Round 1: Design + Implementation (parallel where possible) + +1. **Identify all stakeholders** — before delegating, ask: who needs to be involved? + - UI work → UIUX Designer reviews interaction design FIRST + - Credentials / auth / secrets → Security Auditor reviews + - API changes → Backend Engineer + Frontend Engineer coordinate + - Everything → QA Engineer is the final gate + +2. **Delegate design review first** (if UI work): + ``` + delegate_task_async → UIUX Designer: "Review the interaction design for [feature]" + ``` + +3. **Delegate implementation** (after design, or parallel if non-UI): + ``` + delegate_task_async → Frontend Engineer: "Implement [feature] following UIUX spec" + delegate_task_async → Backend Engineer: "Add [endpoint]" (if needed) + delegate_task_async → Security Auditor: "Review [feature] for [specific concerns]" + ``` + +4. **Delegate QA** (can start in parallel — QA reads existing code while FE works): + ``` + delegate_task_async → QA Engineer: "Review [feature], run full test suite, write missing tests, grep for convention violations" + ``` + +5. **Collect all results** via `check_task_status` on each delegation. + +### Round 2: Fix QA Findings (if any issues found) + +If QA reported issues: + +1. **Send QA's findings back to the implementer:** + ``` + delegate_task → Frontend Engineer: "QA found these issues in your implementation: + [paste QA's specific findings with file:line references] + Fix all of them and report back." + ``` + +2. **Re-run QA on the fixes:** + ``` + delegate_task → QA Engineer: "FE applied fixes for your findings. Re-verify: + [paste the specific issues that were fixed] + Run the test suite again. Report if any issues remain." + ``` + +3. **If QA still finds issues → repeat Round 2.** + +### Round 3: Final Sign-off + +When QA reports zero issues: +- Compile the full report: what was implemented, what was fixed, test results +- Report to PM / CEO with substance, not just "done" + +## Key Rules + +- **Never skip QA.** Even if FE says "I tested it." QA verifies independently. +- **Never skip Security for credential-related features.** A secrets panel without security review is a liability. +- **QA findings are not optional.** If QA found it, it gets fixed. Period. +- **Use parallel delegation.** `delegate_task_async` to all specialists at once, then collect with `check_task_status`. Don't serialize what can be concurrent. +- **Ask side questions.** If FE needs to know the API shape, FE should `delegate_task` directly to BE — don't relay through the lead.