Compare commits

..

12 Commits

Author SHA1 Message Date
c994599c55 Merge pull request 'ci: rename .github/workflows -> .gitea/workflows (post-suspension sweep)' (#5) from ci-rename-github-to-gitea into main
All checks were successful
CI / validate (push) Successful in 1m14s
2026-05-10 21:18:23 +00:00
d490e4ad8f ci: rename .github/workflows -> .gitea/workflows (post-suspension sweep)
All checks were successful
CI / validate (pull_request) Successful in 57s
CI / validate (push) Successful in 56s
GitHub org Molecule-AI was suspended 2026-05-06; SCM moved to Gitea
(git.moleculesai.app). The wholesale `git push --mirror` migration left
workflow files under .github/workflows/, which Gitea Actions does NOT
read - it reads .gitea/workflows/ exclusively.

This rename + the cross-repo `uses:` path rewrite are the minimum
edits to make CI fire on this repo again. The workflow content itself
is not modified (other than the path rewrites and lowercasing of the
old `Molecule-AI` org reference to the post-suspension `molecule-ai`).

Refs: feedback_post_suspension_migration_must_sweep_dormant_repos
2026-05-10 14:13:31 -07:00
c9b572637d chore(ci): remove recovery marker (rerun delivered, see internal#233)
Some checks failed
CI / validate (push) Failing after 1s
2026-05-10 19:51:58 +00:00
590b176c06 chore(ci): re-fire after incident recovery 2026-05-10 (see internal#233; revert me)
Some checks failed
CI / validate (push) Failing after 1s
2026-05-10 19:51:20 +00:00
69ac9788d6 Merge pull request 'chore: plugin hygiene — .gitignore Python ignores + __pycache__ cleanup' (#4) from plugin/hygiene into main
Some checks failed
CI / validate (push) Failing after 1s
2026-05-10 16:23:07 +00:00
139de5a9f8 chore: append Python ignores to .gitignore
Some checks failed
CI / validate (push) Failing after 2s
CI / validate (pull_request) Failing after 1s
2026-05-10 16:19:50 +00:00
319a3a8bc5 Merge pull request 'test(molecule-dev): add 26-test smoke suite + coverage rationale' (#3) from plugin/test-coverage into main
Some checks failed
CI / validate (push) Failing after 2s
2026-05-10 14:01:01 +00:00
57f861d4ff test(molecule-dev): add 26-test smoke suite + coverage rationale
Some checks failed
CI / validate (push) Failing after 1s
CI / validate (pull_request) Failing after 3s
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-10 13:59:48 +00:00
362b3b505f docs: write substantive README.md
All checks were successful
CI / validate (push) Successful in 1m48s
2026-05-10 08:33:55 +00:00
security-auditor
37fd5ff954 ci: re-trigger after orchestrator restarted runners 1-8
All checks were successful
CI / validate (push) Successful in 1m17s
Per saved memory feedback_runner_config_partial_deploy: orchestrator
identified that runners 1-8 last restarted before AGENT_TOOLSDIRECTORY
+ RUNNER_TOOL_CACHE were added; cycle 7 retrigger landed ~50% on stale
runners. Orchestrator restarted 1-8 at ~09:37; this empty commit
re-triggers CI on the now-consistent runner pool.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 02:41:04 -07:00
security-auditor
8429002615 ci: re-trigger after runner-config v2 (AGENT_TOOLSDIRECTORY etc.)
Some checks failed
CI / validate (push) Failing after 19s
Empty commit to re-run CI against the act_runner config that landed
in /opt/molecule/runners/config.yaml (cycle ~58 internal#46 Phase 3).
No source change. CI now runs setup-python with /tmp/hostedtoolcache,
which works (verified in cycle 6 task 1022 log, careful-bash#2).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 02:27:31 -07:00
1717069b1f Merge pull request 'fix(ci): lowercase 'molecule-ai/' in cross-repo workflow refs' (#2) from fix/lowercase-org-slug into main
Some checks failed
CI / validate (push) Failing after 20s
2026-05-07 08:59:37 +00:00
6 changed files with 309 additions and 7 deletions

5
.gitea/workflows/ci.yml Normal file
View File

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

View File

@ -1,5 +0,0 @@
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,3 +19,15 @@
# 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,19 +1,64 @@
# molecule-dev # molecule-dev
Molecule AI plugin. Install via the Molecule AI platform plugin system. Molecule AI codebase conventions, quality standards, past bugs, and coordination workflows. Agents operating in Molecule AI workspaces load these rules automatically.
## Usage ## What it provides
### 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.

34
tests/README.md Normal file
View File

@ -0,0 +1,34 @@
# 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

211
tests/test_dev_smoke.py Normal file
View File

@ -0,0 +1,211 @@
#!/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)