diff --git a/org-templates/molecule-dev/backend-engineer/workspace.yaml b/org-templates/molecule-dev/backend-engineer/workspace.yaml new file mode 100644 index 00000000..4448d85d --- /dev/null +++ b/org-templates/molecule-dev/backend-engineer/workspace.yaml @@ -0,0 +1,29 @@ +name: Backend Engineer +role: >- + Owns the Go/Gin platform layer: REST handlers, WebSocket hub, + workspace provisioner, and A2A proxy. Manages Postgres schema, + migrations, and parameterized query safety; Redis pub/sub, + heartbeat TTLs, and per-workspace key cleanup. Enforces access + control on every endpoint and structured error handling across + all platform/ code. Primary reviewer for any platform-layer PR. +tier: 3 +model: opus +files_dir: backend-engineer + # #266: HITL gate — Backend Engineer's scope includes destructive + # DB migrations + runtime config changes; the @requires_approval + # decorator stops an unattended agent from shipping a prod + # schema mutation without a human click. UNION with defaults. + # #280: molecule-skill-code-review — self-review rubric before + # raising a PR (same rubric Dev Lead applies in review). + # #303: molecule-security-scan — CVE gate at dev time, not + # just at Security Auditor's 12h cron. Catches supply-chain + # deps + secret patterns before they reach PR review. + # #310: molecule-skill-llm-judge — self-gate before PR review. + # #322: molecule-compliance — OA-03 excessive-agency cap; Backend + # Engineer is the highest tool-call-volume role (platform PRs, + # migrations, API changes) so a hard cap is a concrete guard + # against runaway loops during large refactors. +plugins: [molecule-hitl, molecule-skill-code-review, molecule-security-scan, molecule-skill-llm-judge, molecule-compliance] +idle_interval_seconds: 600 +initial_prompt_file: initial-prompt.md +idle_prompt_file: idle-prompt.md diff --git a/org-templates/molecule-dev/community-manager/workspace.yaml b/org-templates/molecule-dev/community-manager/workspace.yaml new file mode 100644 index 00000000..def080a4 --- /dev/null +++ b/org-templates/molecule-dev/community-manager/workspace.yaml @@ -0,0 +1,19 @@ +name: Community Manager +role: >- + Voice-of-the-user. Triages every inbound question + (GH Discussions, Discord, Slack), routes technical + ones to DevRel, feature requests to PM, vulnerability + reports to Security Auditor. Owns response-time SLAs + and user-feedback capture. +tier: 2 +files_dir: community-manager +canvas: {x: 1150, y: 400} +plugins: [] +idle_interval_seconds: 600 +schedules: + - name: Hourly unanswered sweep + cron_expr: "12 * * * *" + enabled: true + prompt_file: schedules/hourly-unanswered-sweep.md +initial_prompt_file: initial-prompt.md +idle_prompt_file: idle-prompt.md diff --git a/org-templates/molecule-dev/competitive-intelligence/workspace.yaml b/org-templates/molecule-dev/competitive-intelligence/workspace.yaml new file mode 100644 index 00000000..95f75c7b --- /dev/null +++ b/org-templates/molecule-dev/competitive-intelligence/workspace.yaml @@ -0,0 +1,7 @@ +name: Competitive Intelligence +role: Competitor tracking and feature comparison +files_dir: competitive-intelligence +plugins: [browser-automation] + # Idle-loop rollout wave 2 (sibling to Market Analyst). +idle_interval_seconds: 600 +idle_prompt_file: idle-prompt.md diff --git a/org-templates/molecule-dev/content-marketer/workspace.yaml b/org-templates/molecule-dev/content-marketer/workspace.yaml new file mode 100644 index 00000000..8f9422d2 --- /dev/null +++ b/org-templates/molecule-dev/content-marketer/workspace.yaml @@ -0,0 +1,20 @@ +name: Content Marketer +role: >- + Writes the blog posts, tutorials, launch write-ups, + and case studies that drive organic traffic and + credibility. Partners with DevRel on technical + narratives and SEO Analyst on keyword briefs. Never + invents benchmarks — only quotes merged PR measurements + or labels a number as design intent. +tier: 2 +files_dir: content-marketer +canvas: {x: 1300, y: 250} +plugins: [molecule-skill-llm-judge] +idle_interval_seconds: 600 +schedules: + - name: Hourly topic queue refresh + cron_expr: "41 * * * *" + enabled: true + prompt_file: schedules/hourly-topic-queue-refresh.md +initial_prompt_file: initial-prompt.md +idle_prompt_file: idle-prompt.md diff --git a/org-templates/molecule-dev/devops-engineer/workspace.yaml b/org-templates/molecule-dev/devops-engineer/workspace.yaml new file mode 100644 index 00000000..7c329f35 --- /dev/null +++ b/org-templates/molecule-dev/devops-engineer/workspace.yaml @@ -0,0 +1,44 @@ +name: DevOps Engineer +role: >- + Owns the container build pipeline: Dockerfiles for all six + runtime images (langgraph, claude-code, openclaw, crewai, + autogen, deepagents), docker-compose.infra.yml for the local + dev stack, and build-all.sh hygiene. Manages GitHub Actions + CI (platform-build, canvas-build, python-lint, + mcp-server-build), coverage thresholds, and secrets hygiene + in the pipeline. Keeps infra/scripts/setup.sh and nuke.sh + in sync whenever migrations or services change. Escalates to + Backend Engineer for schema/runtime-config changes and to + Frontend Engineer for canvas build failures. "Done" means: + all CI jobs green, all images buildable from a clean checkout, + no *.log or .env files leaked into image layers. +tier: 3 +model: opus +files_dir: devops-engineer + # #266: HITL gate — DevOps Engineer's scope covers fly deploys, + # registry pushes, CI pipeline mutations. Any of these going + # wrong affects every tenant; @requires_approval before + # destructive infra ops is the point. + # #280: molecule-skill-code-review — self-review rubric for + # Dockerfiles, CI workflows, infra scripts before PR. + # #322: molecule-freeze-scope — lock edits to infra/** during + # risky operations (CI migrations, fly secret rotations, image + # rebuilds). Plugin was an orphan for 3 weekly audits; DevOps + # is the natural home. +plugins: [molecule-hitl, molecule-skill-code-review, molecule-freeze-scope] + # #247: notify on build-break — DevOps routes CI failures + infra + # alerts via Telegram so they're not invisible until morning review. +channels: + - type: telegram + config: + bot_token: ${TELEGRAM_BOT_TOKEN} + chat_id: ${TELEGRAM_CHAT_ID} + enabled: true +idle_interval_seconds: 600 +schedules: + - name: Hourly channel expansion survey + cron_expr: "47 * * * *" + enabled: true + prompt_file: schedules/hourly-channel-expansion-survey.md +initial_prompt_file: initial-prompt.md +idle_prompt_file: idle-prompt.md diff --git a/org-templates/molecule-dev/devrel-engineer/workspace.yaml b/org-templates/molecule-dev/devrel-engineer/workspace.yaml new file mode 100644 index 00000000..dec9d9d8 --- /dev/null +++ b/org-templates/molecule-dev/devrel-engineer/workspace.yaml @@ -0,0 +1,22 @@ +name: DevRel Engineer +role: >- + Developer-facing voice of Molecule AI. Owns the code + samples, runnable tutorials, and talk-track that turn + "I've heard of this" into "I can run it". Partners with + Content Marketer for blog narratives and with PMM for + positioning. Never ships a tutorial that doesn't run + green against the current main. On every feat: PR merge, + produces a 20-line demo within 24 hours. +tier: 3 +model: opus +files_dir: devrel-engineer +canvas: {x: 1000, y: 250} +plugins: [molecule-skill-code-review, molecule-skill-llm-judge] +idle_interval_seconds: 600 +schedules: + - name: Hourly sample-coverage audit + cron_expr: "18 * * * *" + enabled: true + prompt_file: schedules/hourly-sample-coverage-audit.md +initial_prompt_file: initial-prompt.md +idle_prompt_file: idle-prompt.md diff --git a/org-templates/molecule-dev/frontend-engineer/workspace.yaml b/org-templates/molecule-dev/frontend-engineer/workspace.yaml new file mode 100644 index 00000000..f48cd761 --- /dev/null +++ b/org-templates/molecule-dev/frontend-engineer/workspace.yaml @@ -0,0 +1,24 @@ +name: Frontend Engineer +role: >- + Owns the Next.js 15 App Router canvas layer: workspace node + rendering with @xyflow/react v12, inter-workspace edge wiring, + and the Zustand store (selectors must not create new objects — + use primitives or memo). Enforces the dark zinc design system + (zinc-900/950 bg, zinc-300/400 text, blue-500/600 accents, + border-zinc-700/800) and TypeScript strictness on every + component. Adds 'use client' to any .tsx that uses hooks; gates + every commit with npm run build passing clean. Escalates to + Backend Engineer for API shape questions — never guesses. + "Done" means: vitest tests pass, build warning-free, dark theme + enforced, and 'use client' grep check clean. +tier: 3 +model: opus +files_dir: frontend-engineer + # #280: self-review rubric before raising a PR. Dev Lead uses + # the same rubric, so catching issues here cuts the review loop. + # #310: molecule-skill-llm-judge — gate own PR against issue body + # before requesting review ("shipped the wrong thing" early catch). +plugins: [molecule-skill-code-review, molecule-skill-llm-judge] +idle_interval_seconds: 600 +initial_prompt_file: initial-prompt.md +idle_prompt_file: idle-prompt.md diff --git a/org-templates/molecule-dev/market-analyst/workspace.yaml b/org-templates/molecule-dev/market-analyst/workspace.yaml new file mode 100644 index 00000000..7f7d7213 --- /dev/null +++ b/org-templates/molecule-dev/market-analyst/workspace.yaml @@ -0,0 +1,9 @@ +name: Market Analyst +role: Market sizing, trends, user research +files_dir: market-analyst +plugins: [browser-automation] + # Idle-loop rollout wave 2 (#216 → #285 → #304 validated on Technical + # Researcher 2026-04-16 02:40 UTC). Market Analyst gets the same + # reflection-on-completion pattern tuned for market research work. +idle_interval_seconds: 600 +idle_prompt_file: idle-prompt.md diff --git a/org-templates/molecule-dev/product-marketing-manager/workspace.yaml b/org-templates/molecule-dev/product-marketing-manager/workspace.yaml new file mode 100644 index 00000000..957c5f60 --- /dev/null +++ b/org-templates/molecule-dev/product-marketing-manager/workspace.yaml @@ -0,0 +1,22 @@ +name: Product Marketing Manager +role: >- + Owns positioning, messaging, and competitive framing. + Every piece of copy from marketing roots back to a + PMM positioning decision. Maintains docs/marketing/ + positioning.md + competitors.md as single-source-of- + truth. For every feat: PR merge, writes the launch + brief within 24 hours. Pulls competitor diffs from + ecosystem-watch.md hourly. +tier: 3 +model: opus +files_dir: product-marketing-manager +canvas: {x: 1150, y: 250} +plugins: [molecule-skill-code-review, molecule-skill-llm-judge] +idle_interval_seconds: 600 +schedules: + - name: Hourly competitor diff + cron_expr: "33 * * * *" + enabled: true + prompt_file: schedules/hourly-competitor-diff.md +initial_prompt_file: initial-prompt.md +idle_prompt_file: idle-prompt.md diff --git a/org-templates/molecule-dev/qa-engineer/workspace.yaml b/org-templates/molecule-dev/qa-engineer/workspace.yaml new file mode 100644 index 00000000..76f8390d --- /dev/null +++ b/org-templates/molecule-dev/qa-engineer/workspace.yaml @@ -0,0 +1,18 @@ +name: QA Engineer +role: Testing, quality assurance, test automation +tier: 3 +model: opus +files_dir: qa-engineer + # QA reviews test coverage + runs llm-judge on whether test + # deliverables actually match acceptance criteria. Issue #133. + # #322: molecule-compliance — OA-01 prompt-injection detection + # (in detect mode, not block) catches adversarial test payloads + # before they slip into production. OA-03 excessive-agency caps + # prevent runaway test loops. +plugins: [molecule-skill-code-review, molecule-skill-llm-judge, molecule-compliance] +schedules: + - name: Code quality audit (every 12h) + cron_expr: "0 6,18 * * *" + enabled: true + prompt_file: schedules/code-quality-audit-every-12h.md +initial_prompt_file: initial-prompt.md diff --git a/org-templates/molecule-dev/security-auditor/workspace.yaml b/org-templates/molecule-dev/security-auditor/workspace.yaml new file mode 100644 index 00000000..ea9b98a9 --- /dev/null +++ b/org-templates/molecule-dev/security-auditor/workspace.yaml @@ -0,0 +1,56 @@ +name: Security Auditor +role: >- + Owns security posture across the full stack: Go/Gin handlers + (SQL injection, path traversal, command injection, missing access + control), Python workspace-template (RCE via subprocess, secrets + in env/logs), Canvas (XSS in user-rendered content), and + infrastructure (Docker socket exposure, secrets in images). + Runs SAST via `gosec ./...` on every PR-touching Go file and + `bandit -r .` on Python. Performs DAST checks against the running + platform (`POST /workspaces/:id/a2a` CanCommunicate bypass + attempts, CORS header validation, rate-limit enforcement). + Escalates to Dev Lead immediately for: any SQL injection or RCE + vector, leaked secrets in committed code, missing auth on a new + endpoint. Files weekly summary to memory key + `security-audit-latest`. Definition of done: every changed file + reviewed, gosec/bandit clean (or false-positives annotated), + no open critical findings without a linked issue. +tier: 3 +model: opus +files_dir: security-auditor + # Security Auditor adds security-critical skills on top of defaults: + # - molecule-skill-code-review: multi-criteria review for security-relevant PRs + # - molecule-skill-cross-vendor-review: adversarial second opinion via non-Claude model + # (use ONLY for noteworthy PRs — auth, billing, data) + # - molecule-skill-llm-judge: cheap gate that catches "wrong thing shipped" + # - molecule-security-scan (#275): supply-chain CVE gate via Snyk/pip-audit; wraps + # builtin_tools/security_scan.py — gosec/bandit/etc + # - molecule-hitl (#266): @requires_approval before filing critical issues + # so false-positives don't spam the tracker + # - molecule-compliance (#322): OWASP Top 10 for Agentic Applications — active + # enforcement on Security Auditor's own tool calls + # - molecule-audit (#322): immutable JSON-Lines audit log (EU AI Act Art 12/13/17) + # — Security Auditor owns the report generation path +plugins: + - molecule-skill-code-review + - molecule-skill-cross-vendor-review + - molecule-skill-llm-judge + - molecule-security-scan + - molecule-hitl + - molecule-compliance + - molecule-audit + # #246: notify on critical findings — Security Auditor pushes HIGH+ + # severity alerts via Telegram so they're not invisible until next + # manual memory check. +channels: + - type: telegram + config: + bot_token: ${TELEGRAM_BOT_TOKEN} + chat_id: ${TELEGRAM_CHAT_ID} + enabled: true +schedules: + - name: Security audit (every 12h) + cron_expr: "7 6,18 * * *" + enabled: true + prompt_file: schedules/security-audit-every-12h.md +initial_prompt_file: initial-prompt.md diff --git a/org-templates/molecule-dev/seo-growth-analyst/workspace.yaml b/org-templates/molecule-dev/seo-growth-analyst/workspace.yaml new file mode 100644 index 00000000..dc5776c5 --- /dev/null +++ b/org-templates/molecule-dev/seo-growth-analyst/workspace.yaml @@ -0,0 +1,19 @@ +name: SEO Growth Analyst +role: >- + Owns organic search visibility and funnel conversion. + Metrics: keyword rank, search impressions, CTR, time- + on-page, signup conversion. Writes SEO briefs for every + Content post; audits Lighthouse + Core Web Vitals daily; + proposes A/B tests for weakest funnel step. +tier: 2 +files_dir: seo-growth-analyst +canvas: {x: 1000, y: 400} +plugins: [browser-automation] +idle_interval_seconds: 600 +schedules: + - name: Daily Lighthouse + keyword audit + cron_expr: "23 8 * * *" + enabled: true + prompt_file: schedules/daily-lighthouse-keyword-audit.md +initial_prompt_file: initial-prompt.md +idle_prompt_file: idle-prompt.md diff --git a/org-templates/molecule-dev/social-media-brand/workspace.yaml b/org-templates/molecule-dev/social-media-brand/workspace.yaml new file mode 100644 index 00000000..f2d9d57b --- /dev/null +++ b/org-templates/molecule-dev/social-media-brand/workspace.yaml @@ -0,0 +1,19 @@ +name: Social Media Brand +role: >- + Owns Molecule AI's voice on X + LinkedIn and the visual + identity across marketing surfaces. 1-2 X posts + 3-5 + replies/day; LinkedIn 2-3 posts/week. Maintains brand + guidelines (zinc dark, blue accents, system-mono code). + Every launch gets a 3-post thread within 24h. +tier: 2 +files_dir: social-media-brand +canvas: {x: 1300, y: 400} +plugins: [] +idle_interval_seconds: 600 +schedules: + - name: Hourly mention monitor + cron_expr: "27 * * * *" + enabled: true + prompt_file: schedules/hourly-mention-monitor.md +initial_prompt_file: initial-prompt.md +idle_prompt_file: idle-prompt.md diff --git a/org-templates/molecule-dev/teams/dev.yaml b/org-templates/molecule-dev/teams/dev.yaml index f681cd71..9f2198f4 100644 --- a/org-templates/molecule-dev/teams/dev.yaml +++ b/org-templates/molecule-dev/teams/dev.yaml @@ -29,194 +29,10 @@ schedules: enabled: true prompt_file: schedules/hourly-template-fitness-audit.md children: - - name: Frontend Engineer - role: >- - Owns the Next.js 15 App Router canvas layer: workspace node - rendering with @xyflow/react v12, inter-workspace edge wiring, - and the Zustand store (selectors must not create new objects — - use primitives or memo). Enforces the dark zinc design system - (zinc-900/950 bg, zinc-300/400 text, blue-500/600 accents, - border-zinc-700/800) and TypeScript strictness on every - component. Adds 'use client' to any .tsx that uses hooks; gates - every commit with npm run build passing clean. Escalates to - Backend Engineer for API shape questions — never guesses. - "Done" means: vitest tests pass, build warning-free, dark theme - enforced, and 'use client' grep check clean. - tier: 3 - model: opus - files_dir: frontend-engineer - # #280: self-review rubric before raising a PR. Dev Lead uses - # the same rubric, so catching issues here cuts the review loop. - # #310: molecule-skill-llm-judge — gate own PR against issue body - # before requesting review ("shipped the wrong thing" early catch). - plugins: [molecule-skill-code-review, molecule-skill-llm-judge] - idle_interval_seconds: 600 - initial_prompt_file: initial-prompt.md - idle_prompt_file: idle-prompt.md - - name: Backend Engineer - role: >- - Owns the Go/Gin platform layer: REST handlers, WebSocket hub, - workspace provisioner, and A2A proxy. Manages Postgres schema, - migrations, and parameterized query safety; Redis pub/sub, - heartbeat TTLs, and per-workspace key cleanup. Enforces access - control on every endpoint and structured error handling across - all platform/ code. Primary reviewer for any platform-layer PR. - tier: 3 - model: opus - files_dir: backend-engineer - # #266: HITL gate — Backend Engineer's scope includes destructive - # DB migrations + runtime config changes; the @requires_approval - # decorator stops an unattended agent from shipping a prod - # schema mutation without a human click. UNION with defaults. - # #280: molecule-skill-code-review — self-review rubric before - # raising a PR (same rubric Dev Lead applies in review). - # #303: molecule-security-scan — CVE gate at dev time, not - # just at Security Auditor's 12h cron. Catches supply-chain - # deps + secret patterns before they reach PR review. - # #310: molecule-skill-llm-judge — self-gate before PR review. - # #322: molecule-compliance — OA-03 excessive-agency cap; Backend - # Engineer is the highest tool-call-volume role (platform PRs, - # migrations, API changes) so a hard cap is a concrete guard - # against runaway loops during large refactors. - plugins: [molecule-hitl, molecule-skill-code-review, molecule-security-scan, molecule-skill-llm-judge, molecule-compliance] - idle_interval_seconds: 600 - initial_prompt_file: initial-prompt.md - idle_prompt_file: idle-prompt.md - - name: DevOps Engineer - role: >- - Owns the container build pipeline: Dockerfiles for all six - runtime images (langgraph, claude-code, openclaw, crewai, - autogen, deepagents), docker-compose.infra.yml for the local - dev stack, and build-all.sh hygiene. Manages GitHub Actions - CI (platform-build, canvas-build, python-lint, - mcp-server-build), coverage thresholds, and secrets hygiene - in the pipeline. Keeps infra/scripts/setup.sh and nuke.sh - in sync whenever migrations or services change. Escalates to - Backend Engineer for schema/runtime-config changes and to - Frontend Engineer for canvas build failures. "Done" means: - all CI jobs green, all images buildable from a clean checkout, - no *.log or .env files leaked into image layers. - tier: 3 - model: opus - files_dir: devops-engineer - # #266: HITL gate — DevOps Engineer's scope covers fly deploys, - # registry pushes, CI pipeline mutations. Any of these going - # wrong affects every tenant; @requires_approval before - # destructive infra ops is the point. - # #280: molecule-skill-code-review — self-review rubric for - # Dockerfiles, CI workflows, infra scripts before PR. - # #322: molecule-freeze-scope — lock edits to infra/** during - # risky operations (CI migrations, fly secret rotations, image - # rebuilds). Plugin was an orphan for 3 weekly audits; DevOps - # is the natural home. - plugins: [molecule-hitl, molecule-skill-code-review, molecule-freeze-scope] - # #247: notify on build-break — DevOps routes CI failures + infra - # alerts via Telegram so they're not invisible until morning review. - channels: - - type: telegram - config: - bot_token: ${TELEGRAM_BOT_TOKEN} - chat_id: ${TELEGRAM_CHAT_ID} - enabled: true - idle_interval_seconds: 600 - schedules: - - name: Hourly channel expansion survey - cron_expr: "47 * * * *" - enabled: true - prompt_file: schedules/hourly-channel-expansion-survey.md - initial_prompt_file: initial-prompt.md - idle_prompt_file: idle-prompt.md - - name: Security Auditor - role: >- - Owns security posture across the full stack: Go/Gin handlers - (SQL injection, path traversal, command injection, missing access - control), Python workspace-template (RCE via subprocess, secrets - in env/logs), Canvas (XSS in user-rendered content), and - infrastructure (Docker socket exposure, secrets in images). - Runs SAST via `gosec ./...` on every PR-touching Go file and - `bandit -r .` on Python. Performs DAST checks against the running - platform (`POST /workspaces/:id/a2a` CanCommunicate bypass - attempts, CORS header validation, rate-limit enforcement). - Escalates to Dev Lead immediately for: any SQL injection or RCE - vector, leaked secrets in committed code, missing auth on a new - endpoint. Files weekly summary to memory key - `security-audit-latest`. Definition of done: every changed file - reviewed, gosec/bandit clean (or false-positives annotated), - no open critical findings without a linked issue. - tier: 3 - model: opus - files_dir: security-auditor - # Security Auditor adds security-critical skills on top of defaults: - # - molecule-skill-code-review: multi-criteria review for security-relevant PRs - # - molecule-skill-cross-vendor-review: adversarial second opinion via non-Claude model - # (use ONLY for noteworthy PRs — auth, billing, data) - # - molecule-skill-llm-judge: cheap gate that catches "wrong thing shipped" - # - molecule-security-scan (#275): supply-chain CVE gate via Snyk/pip-audit; wraps - # builtin_tools/security_scan.py — gosec/bandit/etc - # - molecule-hitl (#266): @requires_approval before filing critical issues - # so false-positives don't spam the tracker - # - molecule-compliance (#322): OWASP Top 10 for Agentic Applications — active - # enforcement on Security Auditor's own tool calls - # - molecule-audit (#322): immutable JSON-Lines audit log (EU AI Act Art 12/13/17) - # — Security Auditor owns the report generation path - plugins: - - molecule-skill-code-review - - molecule-skill-cross-vendor-review - - molecule-skill-llm-judge - - molecule-security-scan - - molecule-hitl - - molecule-compliance - - molecule-audit - # #246: notify on critical findings — Security Auditor pushes HIGH+ - # severity alerts via Telegram so they're not invisible until next - # manual memory check. - channels: - - type: telegram - config: - bot_token: ${TELEGRAM_BOT_TOKEN} - chat_id: ${TELEGRAM_CHAT_ID} - enabled: true - schedules: - - name: Security audit (every 12h) - cron_expr: "7 6,18 * * *" - enabled: true - prompt_file: schedules/security-audit-every-12h.md - initial_prompt_file: initial-prompt.md - - name: QA Engineer - role: Testing, quality assurance, test automation - tier: 3 - model: opus - files_dir: qa-engineer - # QA reviews test coverage + runs llm-judge on whether test - # deliverables actually match acceptance criteria. Issue #133. - # #322: molecule-compliance — OA-01 prompt-injection detection - # (in detect mode, not block) catches adversarial test payloads - # before they slip into production. OA-03 excessive-agency caps - # prevent runaway test loops. - plugins: [molecule-skill-code-review, molecule-skill-llm-judge, molecule-compliance] - schedules: - - name: Code quality audit (every 12h) - cron_expr: "0 6,18 * * *" - enabled: true - prompt_file: schedules/code-quality-audit-every-12h.md - initial_prompt_file: initial-prompt.md - - name: UIUX Designer - role: User flow design, visual design review, interaction patterns, accessibility - tier: 3 - model: opus - files_dir: uiux-designer - # browser-automation for live canvas screenshots via Puppeteer - # (Chrome CDP path; recipe in the cron prompt below). - plugins: [browser-automation] - schedules: - - name: Hourly UI/UX audit with live screenshots - # #306: was "5,20,35,50 * * * *" (every 15 min — 96 - # ticks/day × 8 screenshots × vision = runaway cost). - # Hourly matches the schedule name and is sufficient - # because the canvas UI only changes on deploys. - cron_expr: "5 * * * *" - enabled: true - - prompt_file: schedules/hourly-ui-ux-audit-with-live-screenshots.md - initial_prompt_file: initial-prompt.md + - !include ../frontend-engineer/workspace.yaml + - !include ../backend-engineer/workspace.yaml + - !include ../devops-engineer/workspace.yaml + - !include ../security-auditor/workspace.yaml + - !include ../qa-engineer/workspace.yaml + - !include ../uiux-designer/workspace.yaml initial_prompt_file: initial-prompt.md diff --git a/org-templates/molecule-dev/teams/marketing.yaml b/org-templates/molecule-dev/teams/marketing.yaml index 6419665a..f36f05a5 100644 --- a/org-templates/molecule-dev/teams/marketing.yaml +++ b/org-templates/molecule-dev/teams/marketing.yaml @@ -19,125 +19,10 @@ schedules: enabled: true prompt_file: schedules/orchestrator-pulse.md children: - - name: DevRel Engineer - role: >- - Developer-facing voice of Molecule AI. Owns the code - samples, runnable tutorials, and talk-track that turn - "I've heard of this" into "I can run it". Partners with - Content Marketer for blog narratives and with PMM for - positioning. Never ships a tutorial that doesn't run - green against the current main. On every feat: PR merge, - produces a 20-line demo within 24 hours. - tier: 3 - model: opus - files_dir: devrel-engineer - canvas: {x: 1000, y: 250} - plugins: [molecule-skill-code-review, molecule-skill-llm-judge] - idle_interval_seconds: 600 - schedules: - - name: Hourly sample-coverage audit - cron_expr: "18 * * * *" - enabled: true - prompt_file: schedules/hourly-sample-coverage-audit.md - initial_prompt_file: initial-prompt.md - idle_prompt_file: idle-prompt.md - - name: Product Marketing Manager - role: >- - Owns positioning, messaging, and competitive framing. - Every piece of copy from marketing roots back to a - PMM positioning decision. Maintains docs/marketing/ - positioning.md + competitors.md as single-source-of- - truth. For every feat: PR merge, writes the launch - brief within 24 hours. Pulls competitor diffs from - ecosystem-watch.md hourly. - tier: 3 - model: opus - files_dir: product-marketing-manager - canvas: {x: 1150, y: 250} - plugins: [molecule-skill-code-review, molecule-skill-llm-judge] - idle_interval_seconds: 600 - schedules: - - name: Hourly competitor diff - cron_expr: "33 * * * *" - enabled: true - prompt_file: schedules/hourly-competitor-diff.md - initial_prompt_file: initial-prompt.md - idle_prompt_file: idle-prompt.md - - name: Content Marketer - role: >- - Writes the blog posts, tutorials, launch write-ups, - and case studies that drive organic traffic and - credibility. Partners with DevRel on technical - narratives and SEO Analyst on keyword briefs. Never - invents benchmarks — only quotes merged PR measurements - or labels a number as design intent. - tier: 2 - files_dir: content-marketer - canvas: {x: 1300, y: 250} - plugins: [molecule-skill-llm-judge] - idle_interval_seconds: 600 - schedules: - - name: Hourly topic queue refresh - cron_expr: "41 * * * *" - enabled: true - prompt_file: schedules/hourly-topic-queue-refresh.md - initial_prompt_file: initial-prompt.md - idle_prompt_file: idle-prompt.md - - name: Community Manager - role: >- - Voice-of-the-user. Triages every inbound question - (GH Discussions, Discord, Slack), routes technical - ones to DevRel, feature requests to PM, vulnerability - reports to Security Auditor. Owns response-time SLAs - and user-feedback capture. - tier: 2 - files_dir: community-manager - canvas: {x: 1150, y: 400} - plugins: [] - idle_interval_seconds: 600 - schedules: - - name: Hourly unanswered sweep - cron_expr: "12 * * * *" - enabled: true - prompt_file: schedules/hourly-unanswered-sweep.md - initial_prompt_file: initial-prompt.md - idle_prompt_file: idle-prompt.md - - name: SEO Growth Analyst - role: >- - Owns organic search visibility and funnel conversion. - Metrics: keyword rank, search impressions, CTR, time- - on-page, signup conversion. Writes SEO briefs for every - Content post; audits Lighthouse + Core Web Vitals daily; - proposes A/B tests for weakest funnel step. - tier: 2 - files_dir: seo-growth-analyst - canvas: {x: 1000, y: 400} - plugins: [browser-automation] - idle_interval_seconds: 600 - schedules: - - name: Daily Lighthouse + keyword audit - cron_expr: "23 8 * * *" - enabled: true - prompt_file: schedules/daily-lighthouse-keyword-audit.md - initial_prompt_file: initial-prompt.md - idle_prompt_file: idle-prompt.md - - name: Social Media Brand - role: >- - Owns Molecule AI's voice on X + LinkedIn and the visual - identity across marketing surfaces. 1-2 X posts + 3-5 - replies/day; LinkedIn 2-3 posts/week. Maintains brand - guidelines (zinc dark, blue accents, system-mono code). - Every launch gets a 3-post thread within 24h. - tier: 2 - files_dir: social-media-brand - canvas: {x: 1300, y: 400} - plugins: [] - idle_interval_seconds: 600 - schedules: - - name: Hourly mention monitor - cron_expr: "27 * * * *" - enabled: true - prompt_file: schedules/hourly-mention-monitor.md - initial_prompt_file: initial-prompt.md - idle_prompt_file: idle-prompt.md + - !include ../devrel-engineer/workspace.yaml + - !include ../product-marketing-manager/workspace.yaml + - !include ../content-marketer/workspace.yaml + - !include ../community-manager/workspace.yaml + - !include ../seo-growth-analyst/workspace.yaml + - !include ../social-media-brand/workspace.yaml initial_prompt_file: initial-prompt.md diff --git a/org-templates/molecule-dev/teams/research.yaml b/org-templates/molecule-dev/teams/research.yaml index 688dbfa3..5ffa97a7 100644 --- a/org-templates/molecule-dev/teams/research.yaml +++ b/org-templates/molecule-dev/teams/research.yaml @@ -24,35 +24,7 @@ schedules: enabled: true prompt_file: schedules/hourly-ecosystem-watch.md children: - - name: Market Analyst - role: Market sizing, trends, user research - files_dir: market-analyst - plugins: [browser-automation] - # Idle-loop rollout wave 2 (#216 → #285 → #304 validated on Technical - # Researcher 2026-04-16 02:40 UTC). Market Analyst gets the same - # reflection-on-completion pattern tuned for market research work. - idle_interval_seconds: 600 - idle_prompt_file: idle-prompt.md - - name: Technical Researcher - role: AI frameworks and protocol evaluation - files_dir: technical-researcher - plugins: [browser-automation] - # Idle-loop pilot (#205) — Technical Researcher is the first workspace - # to opt in to the reflection-on-completion pattern. Measure - # activity_logs delta over 24h, then roll to the rest of the research - # team if it produces useful backlog-pull dispatches. - idle_interval_seconds: 600 - schedules: - - name: Hourly plugin curation - cron_expr: "22 * * * *" - enabled: true - prompt_file: schedules/hourly-plugin-curation.md - idle_prompt_file: idle-prompt.md - - name: Competitive Intelligence - role: Competitor tracking and feature comparison - files_dir: competitive-intelligence - plugins: [browser-automation] - # Idle-loop rollout wave 2 (sibling to Market Analyst). - idle_interval_seconds: 600 - idle_prompt_file: idle-prompt.md + - !include ../market-analyst/workspace.yaml + - !include ../technical-researcher/workspace.yaml + - !include ../competitive-intelligence/workspace.yaml initial_prompt_file: initial-prompt.md diff --git a/org-templates/molecule-dev/technical-researcher/workspace.yaml b/org-templates/molecule-dev/technical-researcher/workspace.yaml new file mode 100644 index 00000000..6a26b872 --- /dev/null +++ b/org-templates/molecule-dev/technical-researcher/workspace.yaml @@ -0,0 +1,15 @@ +name: Technical Researcher +role: AI frameworks and protocol evaluation +files_dir: technical-researcher +plugins: [browser-automation] + # Idle-loop pilot (#205) — Technical Researcher is the first workspace + # to opt in to the reflection-on-completion pattern. Measure + # activity_logs delta over 24h, then roll to the rest of the research + # team if it produces useful backlog-pull dispatches. +idle_interval_seconds: 600 +schedules: + - name: Hourly plugin curation + cron_expr: "22 * * * *" + enabled: true + prompt_file: schedules/hourly-plugin-curation.md +idle_prompt_file: idle-prompt.md diff --git a/org-templates/molecule-dev/uiux-designer/workspace.yaml b/org-templates/molecule-dev/uiux-designer/workspace.yaml new file mode 100644 index 00000000..7f5af186 --- /dev/null +++ b/org-templates/molecule-dev/uiux-designer/workspace.yaml @@ -0,0 +1,19 @@ +name: UIUX Designer +role: User flow design, visual design review, interaction patterns, accessibility +tier: 3 +model: opus +files_dir: uiux-designer + # browser-automation for live canvas screenshots via Puppeteer + # (Chrome CDP path; recipe in the cron prompt below). +plugins: [browser-automation] +schedules: + - name: Hourly UI/UX audit with live screenshots + # #306: was "5,20,35,50 * * * *" (every 15 min — 96 + # ticks/day × 8 screenshots × vision = runaway cost). + # Hourly matches the schedule name and is sufficient + # because the canvas UI only changes on deploys. + cron_expr: "5 * * * *" + enabled: true + + prompt_file: schedules/hourly-ui-ux-audit-with-live-screenshots.md +initial_prompt_file: initial-prompt.md diff --git a/platform/internal/handlers/org_include.go b/platform/internal/handlers/org_include.go index 98e8a33b..c7ff5e1a 100644 --- a/platform/internal/handlers/org_include.go +++ b/platform/internal/handlers/org_include.go @@ -4,6 +4,7 @@ import ( "fmt" "os" "path/filepath" + "strings" "gopkg.in/yaml.v3" ) @@ -21,8 +22,14 @@ const maxIncludeDepth = 16 // Semantics: // - A scalar node tagged `!include` with a string value is replaced by // the parsed content of the referenced file. -// - Paths are resolved relative to `baseDir` and must stay inside it -// (same traversal defense as resolveInsideRoot). +// - Paths resolve relative to the INCLUDING file's directory (natural +// sibling/cousin refs, matches C-include / Sass @import convention). +// When the including file is the top-level org.yaml, that's baseDir. +// When it's a nested team file, that's the team file's own dir. +// - Security: every resolved absolute path must stay inside `rootDir` +// (the original baseDir from the top-level call). This allows natural +// `../sibling-dir/file.yaml` refs while still blocking traversal +// outside the org template root. // - Includes may be nested (a team file can !include a role file). // Cycles are detected via a visited set keyed on absolute path; // `maxIncludeDepth` caps total recursion depth as a belt-and-braces @@ -39,7 +46,9 @@ func resolveYAMLIncludes(data []byte, baseDir string) ([]byte, error) { } visited := map[string]bool{} - if err := expandNode(&root, baseDir, visited, 0); err != nil { + // At the top-level call, the "including file's dir" and the "security + // root" are the same. They diverge as we descend into nested includes. + if err := expandNode(&root, baseDir, baseDir, visited, 0); err != nil { return nil, err } @@ -52,9 +61,10 @@ func resolveYAMLIncludes(data []byte, baseDir string) ([]byte, error) { // expandNode walks the yaml.Node tree in-place and replaces any // `!include`-tagged scalar with the parsed content of the referenced -// file. Compound nodes (document / mapping / sequence) recurse; alias -// nodes are left alone (the yaml parser already resolves them pre-tag). -func expandNode(n *yaml.Node, baseDir string, visited map[string]bool, depth int) error { +// file. `currentDir` is the dir of the file currently being processed +// (used for path resolution); `rootDir` is the original org base dir +// (used to bound the security check). +func expandNode(n *yaml.Node, currentDir, rootDir string, visited map[string]bool, depth int) error { if n == nil { return nil } @@ -63,11 +73,11 @@ func expandNode(n *yaml.Node, baseDir string, visited map[string]bool, depth int } if n.Kind == yaml.ScalarNode && n.Tag == "!include" { - return resolveIncludeScalar(n, baseDir, visited, depth) + return resolveIncludeScalar(n, currentDir, rootDir, visited, depth) } for _, child := range n.Content { - if err := expandNode(child, baseDir, visited, depth); err != nil { + if err := expandNode(child, currentDir, rootDir, visited, depth); err != nil { return err } } @@ -75,24 +85,39 @@ func expandNode(n *yaml.Node, baseDir string, visited map[string]bool, depth int } // resolveIncludeScalar replaces an `!include ` scalar with the -// parsed content of the referenced file. The replacement happens by -// mutating *n to take on the included file's root kind/content/tag. -func resolveIncludeScalar(n *yaml.Node, baseDir string, visited map[string]bool, depth int) error { +// parsed content of the referenced file. +func resolveIncludeScalar(n *yaml.Node, currentDir, rootDir string, visited map[string]bool, depth int) error { rel := n.Value if rel == "" { return fmt.Errorf("!include at line %d: empty path", n.Line) } - if baseDir == "" { + if rootDir == "" { return fmt.Errorf("!include %q at line %d requires a dir-based org template (no baseDir in inline-template mode)", rel, n.Line) } - abs, err := resolveInsideRoot(baseDir, rel) + + // Resolve relative to the including file's dir. Result must stay + // inside the original rootDir — sibling dirs (../foo/bar.yaml) are + // fine as long as they don't escape the template root. + abs := filepath.Clean(filepath.Join(currentDir, rel)) + absRoot, err := filepath.Abs(rootDir) if err != nil { - return fmt.Errorf("!include %q at line %d: %w", rel, n.Line, err) + return fmt.Errorf("!include %q at line %d: cannot abs rootDir: %w", rel, n.Line, err) } - if visited[abs] { + absTarget, err := filepath.Abs(abs) + if err != nil { + return fmt.Errorf("!include %q at line %d: cannot abs target: %w", rel, n.Line, err) + } + // Ensure target is inside root. `filepath.Rel` returns "../..." if target + // is outside; we reject that. + rel2, err := filepath.Rel(absRoot, absTarget) + if err != nil || strings.HasPrefix(rel2, "..") || rel2 == ".." { + return fmt.Errorf("!include %q at line %d: path escapes root", rel, n.Line) + } + + if visited[absTarget] { return fmt.Errorf("!include cycle detected at %q (line %d)", rel, n.Line) } - data, err := os.ReadFile(abs) + data, err := os.ReadFile(absTarget) if err != nil { return fmt.Errorf("!include %q at line %d: %w", rel, n.Line, err) } @@ -108,24 +133,19 @@ func resolveIncludeScalar(n *yaml.Node, baseDir string, visited map[string]bool, root = root.Content[0] } - // Mark visited for the whole descent through this file, then recurse - // so nested !includes inside the included file resolve too. Each file - // gets its own baseDir (the directory containing it) so paths like - // `!include role-a/initial.yaml` inside `teams/dev.yaml` resolve - // relative to the team file's directory. - visited[abs] = true - defer delete(visited, abs) + // Mark visited for the whole descent through this file, then recurse. + // Relative refs inside the included file resolve against THAT file's + // dir (subDir), but security stays bounded by the original rootDir. + visited[absTarget] = true + defer delete(visited, absTarget) - subDir := filepath.Dir(abs) - if err := expandNode(root, subDir, visited, depth+1); err != nil { + subDir := filepath.Dir(absTarget) + if err := expandNode(root, subDir, rootDir, visited, depth+1); err != nil { return err } // Replace the !include scalar with the resolved content in-place. *n = *root - // Clear the !include tag (root's Tag is whatever kind it actually is — - // !!map / !!seq / !!str — after unmarshal, which is correct). - // If somehow root.Tag is still !include (shouldn't happen), drop it. if n.Tag == "!include" { n.Tag = "" } diff --git a/platform/internal/handlers/org_include_test.go b/platform/internal/handlers/org_include_test.go index 6b437699..a00e2814 100644 --- a/platform/internal/handlers/org_include_test.go +++ b/platform/internal/handlers/org_include_test.go @@ -149,6 +149,44 @@ func TestResolveYAMLIncludes_InlineTemplateErrors(t *testing.T) { } } +func TestResolveYAMLIncludes_SiblingDirAccess(t *testing.T) { + // Phase 4 pattern: a team file at `teams/.yaml` refers to a role + // file at `/workspace.yaml` via `..//workspace.yaml`. + // The ref escapes the team file's own dir but stays inside the org + // root — this must be allowed. + tmp := t.TempDir() + teamsDir := filepath.Join(tmp, "teams") + roleDir := filepath.Join(tmp, "my-role") + if err := os.MkdirAll(teamsDir, 0o755); err != nil { + t.Fatal(err) + } + if err := os.MkdirAll(roleDir, 0o755); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(filepath.Join(roleDir, "workspace.yaml"), []byte("name: Cousin\ntier: 2\n"), 0o644); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(filepath.Join(teamsDir, "parent.yaml"), []byte("name: Parent\nchildren:\n - !include ../my-role/workspace.yaml\n"), 0o644); err != nil { + t.Fatal(err) + } + src := []byte("workspaces:\n - !include teams/parent.yaml\n") + out, err := resolveYAMLIncludes(src, tmp) + if err != nil { + t.Fatalf("sibling-dir !include should work: %v", err) + } + var tmpl OrgTemplate + if err := yaml.Unmarshal(out, &tmpl); err != nil { + t.Fatalf("unmarshal: %v", err) + } + if len(tmpl.Workspaces) != 1 { + t.Fatalf("workspaces: %d", len(tmpl.Workspaces)) + } + kids := tmpl.Workspaces[0].Children + if len(kids) != 1 || kids[0].Name != "Cousin" { + t.Fatalf("children: %+v", kids) + } +} + // Integration check: after Phase 3 split, the real molecule-dev/org.yaml // resolves cleanly via !include and unmarshal into OrgTemplate produces // the full workspace tree. Guards against split regressions landing on