Merge pull request #433 from Molecule-AI/feat/externalize-prompts-phase4

feat(org-templates): Phase 4 — atomize each role to <role>/workspace.yaml
This commit is contained in:
Hongming Wang 2026-04-16 03:19:43 -07:00 committed by GitHub
commit 3e50b95800
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 443 additions and 370 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 <path>` 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 = ""
}

View File

@ -149,6 +149,44 @@ func TestResolveYAMLIncludes_InlineTemplateErrors(t *testing.T) {
}
}
func TestResolveYAMLIncludes_SiblingDirAccess(t *testing.T) {
// Phase 4 pattern: a team file at `teams/<x>.yaml` refers to a role
// file at `<role>/workspace.yaml` via `../<role>/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