Compare commits

..

No commits in common. "main" and "fix/lowercase-org-slug" have entirely different histories.

6 changed files with 7 additions and 309 deletions

View File

@ -1,5 +0,0 @@
name: CI
on: [push, pull_request]
jobs:
validate:
uses: molecule-ai/molecule-ci/.gitea/workflows/validate-plugin.yml@main

5
.github/workflows/ci.yml vendored Normal file
View File

@ -0,0 +1,5 @@
name: CI
on: [push, pull_request]
jobs:
validate:
uses: molecule-ai/molecule-ci/.github/workflows/validate-plugin.yml@main

12
.gitignore vendored
View File

@ -19,15 +19,3 @@
# Workspace auth tokens # Workspace auth tokens
.auth-token .auth-token
.auth_token .auth_token
# Python bytecode (append only — do not remove entries above)
__pycache__/
*.pyc
*.py[cod]
*$py.class
.Python
*.egg-info/
*.egg
.pytest_cache/
build/
dist/
.eggs/

View File

@ -1,64 +1,19 @@
# molecule-dev # molecule-dev
Molecule AI codebase conventions, quality standards, past bugs, and coordination workflows. Agents operating in Molecule AI workspaces load these rules automatically. Molecule AI plugin. Install via the Molecule AI platform plugin system.
## What it provides ## Usage
### Codebase conventions (`rules/codebase-conventions.md`)
Patterns, idioms, and anti-patterns specific to the Molecule AI codebase. Covers:
- Python style (type hints, docstrings, error handling)
- Go conventions (context usage, error wrapping)
- Directory structure and naming
- Deprecation policy
### Review loop skill (`review-loop`)
Guides the agent through a structured review before each commit:
1. Scope check — does the diff match the ticket?
2. Style check — conventions followed?
3. Test check — coverage sufficient?
4. Security check — secrets or injection risks?
5. Sign-off — agent declares the change ready
## Install
### In org template (org.yaml) ### In org template (org.yaml)
```yaml ```yaml
plugins: plugins:
- molecule-dev - molecule-dev
``` ```
### From URL (community install) ### From URL (community install)
``` ```
github://Molecule-AI/molecule-ai-plugin-molecule-dev github://Molecule-AI/molecule-ai-plugin-molecule-dev
``` ```
## Runtimes
- `claude_code` — primary
- `deepagents` — supported
- `hermes` — supported
## Architecture
```
rules/
codebase-conventions.md # Prose rules loaded into agent memory
adapters/
claude_code.py # Registers rules + review-loop skill
skills/
review-loop/ # SKILL.md + scripts/
runbooks/
local-dev-setup.md # Setup guide for contributors
```
## Known issues
See [known-issues.md](known-issues.md).
## License ## License
Business Source License 1.1 — © Molecule AI. Business Source License 1.1 — © Molecule AI.

View File

@ -1,34 +0,0 @@
# Test Coverage Rationale — molecule-dev
## Why This Plugin Has Limited Unit-Test Coverage
`molecule-dev` is a **skill+rules plugin** — it provides codebase conventions
(rules/codebase-conventions.md) and a review-loop coordination skill
(review-loop/SKILL.md) via prose documentation.
## What We Test (and Why)
| What | Why |
|------|-----|
| `plugin.yaml` schema | Verifies all 1 rule, 1 skill, 2 adapters registered |
| `rules/` directory + file | Declared rule file exists and is non-empty |
| `skills/review-loop` SKILL.md | Frontmatter parses, body has `#` heading |
| Adapters (2) | claude_code + deepagents adapters are wired |
| `known-issues.md` | Active Issues + Severity Definitions + Reporting sections present |
| `README.md` | H1 heading, Install section, Architecture section |
## What We Cannot Unit-Test Here
- **SKILL.md prose content** — review-loop guidance is prose; its quality is
a documentation review concern, not a unit-test concern.
- **Codebase conventions** — the rules in `rules/codebase-conventions.md` are
prose; their correctness is a team convention concern.
## Integration Tests
If you want to test that agents use the review-loop skill correctly:
1. Install `molecule-dev` on a test workspace
2. Ask the agent to coordinate a multi-stakeholder feature
3. Verify the agent follows the Round 1 → Round 2 → Round 3 structure
4. Verify QA findings are routed back and re-verified

View File

@ -1,211 +0,0 @@
#!/usr/bin/env python3
"""
Smoke tests for molecule-dev.
Rationale: This is a skill+rules plugin it provides codebase conventions
(rules) and a review-loop coordination skill. Smoke tests verify all artifacts
exist and parse correctly. See tests/README.md.
Run: python tests/test_dev_smoke.py
"""
import os
import sys
import unittest
REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
def load_manifest():
import yaml
with open(os.path.join(REPO_ROOT, 'plugin.yaml')) as f:
return yaml.safe_load(f)
class TestPluginManifest(unittest.TestCase):
"""Verify plugin.yaml is well-formed."""
@classmethod
def setUpClass(cls):
cls.manifest = load_manifest()
def test_plugin_yaml_loads(self):
self.assertIsInstance(self.manifest, dict)
def test_name(self):
self.assertEqual(self.manifest['name'], 'molecule-dev')
def test_version_semver(self):
v = self.manifest['version']
self.assertRegex(v, r'^\d+\.\d+\.\d+$', f"Version {v!r} not semver")
def test_description_present(self):
self.assertGreater(len(self.manifest.get('description', '')), 10)
def test_runtimes_include_claude_code(self):
self.assertIn('claude_code', self.manifest.get('runtimes', []))
def test_rules_declared(self):
rules = self.manifest.get('rules', [])
self.assertIsInstance(rules, list)
self.assertGreater(len(rules), 0)
def test_skills_declared(self):
skills = self.manifest.get('skills', [])
self.assertIsInstance(skills, list)
self.assertGreater(len(skills), 0)
class TestRules(unittest.TestCase):
"""Verify declared rules files exist and are non-empty."""
RULES_DIR = os.path.join(REPO_ROOT, 'rules')
@classmethod
def setUpClass(cls):
cls.rules = load_manifest().get('rules', [])
def test_rules_directory_exists(self):
self.assertTrue(os.path.isdir(self.RULES_DIR))
def test_each_declared_rule_file_exists(self):
for rule in self.rules:
filename = os.path.basename(rule)
path = os.path.join(self.RULES_DIR, filename)
self.assertTrue(
os.path.isfile(path),
f"Rule {rule!r} declared but file not found at {path}"
)
def test_each_rule_file_is_nonempty(self):
for rule in self.rules:
filename = os.path.basename(rule)
path = os.path.join(self.RULES_DIR, filename)
size = os.path.getsize(path)
self.assertGreater(size, 100, f"Rule {rule!r} is suspiciously small ({size} bytes)")
class TestSkills(unittest.TestCase):
"""Verify declared skills have SKILL.md with valid frontmatter."""
SKILLS_DIR = os.path.join(REPO_ROOT, 'skills')
@classmethod
def setUpClass(cls):
cls.skills = load_manifest().get('skills', [])
def test_skills_directory_exists(self):
self.assertTrue(os.path.isdir(self.SKILLS_DIR))
def test_each_declared_skill_directory_exists(self):
for skill in self.skills:
path = os.path.join(self.SKILLS_DIR, skill)
self.assertTrue(
os.path.isdir(path),
f"Skill {skill!r} declared but directory not found at {path}"
)
def test_each_skill_has_skill_md(self):
for skill in self.skills:
path = os.path.join(self.SKILLS_DIR, skill, 'SKILL.md')
self.assertTrue(os.path.isfile(path), f"Skill {skill!r} missing SKILL.md at {path}")
def test_each_skill_md_has_frontmatter(self):
import yaml
for skill in self.skills:
path = os.path.join(self.SKILLS_DIR, skill, 'SKILL.md')
with open(path) as f:
content = f.read()
self.assertTrue(
content.startswith('---'),
f"{skill}: SKILL.md must have YAML frontmatter"
)
parts = content.split('---', 2)
self.assertEqual(len(parts), 3, f"{skill}: SKILL.md must have opening and closing ---")
_, frontmatter, _ = parts
data = yaml.safe_load(frontmatter)
self.assertIsInstance(data, dict)
self.assertIn('name', data, f"{skill}: frontmatter must have 'name'")
def test_each_skill_body_has_heading(self):
for skill in self.skills:
path = os.path.join(self.SKILLS_DIR, skill, 'SKILL.md')
with open(path) as f:
content = f.read()
parts = content.split('---', 2)
_, _, body = parts
self.assertRegex(
body.lstrip(), r'^# ',
f"{skill}: SKILL.md body must start with # heading"
)
class TestAdapters(unittest.TestCase):
"""Verify runtime adapters exist."""
def test_claude_code_adapter_exists(self):
path = os.path.join(REPO_ROOT, 'adapters', 'claude_code.py')
self.assertTrue(os.path.isfile(path))
def test_claude_code_adapter_imports_adaptor(self):
path = os.path.join(REPO_ROOT, 'adapters', 'claude_code.py')
with open(path) as f:
content = f.read()
self.assertIn('Adaptor', content)
def test_deepagents_adapter_exists(self):
path = os.path.join(REPO_ROOT, 'adapters', 'deepagents.py')
self.assertTrue(os.path.isfile(path))
class TestKnownIssues(unittest.TestCase):
"""Verify known-issues.md structure."""
KI_PATH = os.path.join(REPO_ROOT, 'known-issues.md')
def test_file_exists(self):
self.assertTrue(os.path.isfile(self.KI_PATH))
def test_has_active_issues_section(self):
with open(self.KI_PATH) as f:
self.assertIn('Active Issues', f.read())
def test_has_severity_definitions(self):
with open(self.KI_PATH) as f:
content = f.read()
self.assertIn('Severity Definitions', content)
def test_has_reporting_section(self):
with open(self.KI_PATH) as f:
content = f.read()
self.assertIn('Reporting', content)
class TestReadme(unittest.TestCase):
"""Verify README.md has required sections."""
README_PATH = os.path.join(REPO_ROOT, 'README.md')
def test_readme_exists(self):
self.assertTrue(os.path.isfile(self.README_PATH))
def test_readme_has_h1(self):
with open(self.README_PATH) as f:
first_line = f.readline().strip()
self.assertTrue(
first_line.startswith('# '),
f"README must start with # heading, got: {first_line!r}"
)
def test_readme_has_install_section(self):
with open(self.README_PATH) as f:
content = f.read()
self.assertIn('Install', content)
def test_readme_has_architecture_section(self):
with open(self.README_PATH) as f:
content = f.read()
self.assertIn('Architecture', content)
if __name__ == '__main__':
unittest.main(verbosity=2)