Commit Graph

3996 Commits

Author SHA1 Message Date
rabbitblood
03afba74f3 chore(template): orchestrator/worker split — leaders poll every 5min, workers stay reactive
Supersedes #158 (10-min uniform bump). That PR was too blunt — it treated
research/audit/orchestration crons the same when they have fundamentally
different cost/value/cadence profiles.

## The split

Three layers, three cadences, grounded in the survey of Hermes/Letta/
Trigger.dev/Inngest/AG2/Rivet/n8n/Composio/SWE-agent done this session.
Nobody in that survey runs while(true) per agent — they all combine
event-driven reactivity with short orchestration pulses on a coordinator.
This PR implements that split for our 12-workspace template.

| Layer | Roles | Cadence | Purpose |
|---|---|---|---|
| Orchestration | PM, Dev Lead, Research Lead | every 5 min | Check backlog, dispatch work, review completed tasks |
| Audit | Security Auditor | every 10 min | Focused security audit |
| Audit | UI/UX Designer | every 15 min | Vision-heavy, dial back from 10 |
| Deep-work | Research Lead (eco-watch) | every 30 min (8,38) | Was hourly |
| Deep-work | Dev Lead (template fitness) | every 30 min (15,45) | Was hourly |
| Deep-work | Technical Researcher (plugins) | hourly (unchanged) | Research-heavy, slow |
| Deep-work | DevOps (channels) | hourly (unchanged) | Research-heavy, slow |
| Reactive | BE, FE, DevOps, Docs | no cron | Execute A2A delegations |

## Orchestration pulse prompts

The three new schedules each carry a detailed orchestration_prompt:

- **PM** (5-min): scan all 12 workspaces, scan GH PRs/issues backlog
  (external), scan memory backlog (internal), dispatch up to 3 tasks per
  pulse, review completed work, write pulse summary to memory. Hard
  rules: under 90s wall-clock, never dispatch to busy agents, write
  "orchestrator-clean" and stop if genuinely nothing to do.

- **Dev Lead** (5-min, offset +1 from PM): same shape, scoped to
  engineering team. Reviews open PRs from direct reports, matches idle
  engineers to labeled GH issues (security/bug/feature), dispatches with
  "fix/issue-N-slug" branch convention. Skips pulse if own template
  fitness audit is in flight (:15, :45).

- **Research Lead** (5-min, offset +2 from PM): same shape, scoped to
  research team. Matches Market Analyst / Technical Researcher /
  Competitive Intelligence to research-labeled issues or memory-stashed
  questions. Max 2 A2A per pulse (research is slow). Skips pulse if own
  eco-watch is in flight (:8, :38).

## Cadence offset table

No two crons fire in the same minute:

  :01,:11,:21,:31,:41,:51 — Security audit (Security Auditor)
  :02,:07,:12,:17,:22,:27,:32,:37,:42,:47,:52,:57 — Dev Lead orchestrator
  :04,:09,:14,:19,:24,:29,:34,:39,:44,:49,:54,:59 — Research Lead orchestrator
  :01,:06,:11,:16,:21,:26,:31,:36,:41,:46,:51,:56 — PM orchestrator
  :05,:20,:35,:50 — UI/UX audit (UIUX Designer)
  :08,:38 — Ecosystem watch deep-work (Research Lead)
  :15,:45 — Template fitness deep-work (Dev Lead)
  :22 — Plugin curation (Technical Researcher)
  :47 — Channel expansion (DevOps Engineer)

Note PM and Security Auditor share :01 — this is fine because they
target different workspaces so scheduler concurrency handles it.

## Cost estimate

- PM pulse: 12/hour × 24 × ~3k tokens = 864k tokens/day/org ~ $5/day
- Dev Lead pulse: same ~ $5/day
- Research Lead pulse: same ~ $5/day
- Audits (security 10min, UIUX 15min): ~$8/day/org combined
- Deep-work crons (unchanged from original): ~$4/day/org

**Total ~$27/day/org**. Comparable to #158's $25 but MUCH higher
utility because orchestration produces dispatches that keep workers
busy, whereas #158 just fired more audits against the same team.

Closes #158 (superseded — will close that PR with a pointer to this one).

## Related research
See docs/ecosystem-watch.md `### Hermes Agent` and today's research agent
output: event-driven + reflection-on-completion + short orchestration
pulses on leaders is the shape that delivers 24/7 activity without
runaway cost. This is the concrete implementation.
2026-04-15 09:05:08 -07:00
Hongming Wang
330867d24b Merge pull request #157 from Molecule-AI/chore/eco-watch-2026-04-15-pm
chore(eco-watch): 2026-04-15 PM survey — Microsoft Agent Framework, Vercel Open Agents
2026-04-15 04:20:25 -07:00
Hongming Wang
dafe8274d2
Merge pull request #157 from Molecule-AI/chore/eco-watch-2026-04-15-pm
chore(eco-watch): 2026-04-15 PM survey — Microsoft Agent Framework, Vercel Open Agents
2026-04-15 04:20:25 -07:00
Research Lead
f7b24a1120 chore(eco-watch): 2026-04-15 PM survey — Microsoft Agent Framework, Vercel Open Agents
Two new entries added from the second daily pass (first run merged as PR #150
at 03:20 UTC). Both surfaced in the afternoon trending windows and were not
covered by the morning run.

- microsoft/agent-framework (~9.5k ): official Microsoft successor to
  AutoGen; ships migration guide and April 2026 .NET release. Directly affects
  our autogen adapter in workspace-template/adapters/. Filed issue #156 to
  evaluate adapter update.

- vercel-labs/open-agents (~2.2k , +1,020 today): cloud coding agent template
  from Vercel Labs (same team as Skills CLI). Notable for agent-outside-sandbox
  architecture and snapshot-based VM resumption — a more efficient approach
  than our current Docker restart + git-clone pattern.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 11:12:49 +00:00
Research Lead
c660797fb3 chore(eco-watch): 2026-04-15 PM survey — Microsoft Agent Framework, Vercel Open Agents
Two new entries added from the second daily pass (first run merged as PR #150
at 03:20 UTC). Both surfaced in the afternoon trending windows and were not
covered by the morning run.

- microsoft/agent-framework (~9.5k ): official Microsoft successor to
  AutoGen; ships migration guide and April 2026 .NET release. Directly affects
  our autogen adapter in workspace-template/adapters/. Filed issue #156 to
  evaluate adapter update.

- vercel-labs/open-agents (~2.2k , +1,020 today): cloud coding agent template
  from Vercel Labs (same team as Skills CLI). Notable for agent-outside-sandbox
  architecture and snapshot-based VM resumption — a more efficient approach
  than our current Docker restart + git-clone pattern.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 11:12:49 +00:00
Hongming Wang
718c05d847 Merge pull request #155 from Molecule-AI/fix/issue-151-register-security-headers
fix(security): #151 — register SecurityHeaders middleware
2026-04-15 03:51:02 -07:00
Hongming Wang
3d6ad16a8f
Merge pull request #155 from Molecule-AI/fix/issue-151-register-security-headers
fix(security): #151 — register SecurityHeaders middleware
2026-04-15 03:51:02 -07:00
Hongming Wang
4b6482bea0 fix(security): #151 — register SecurityHeaders middleware
Closes #151. The middleware was already implemented + tested (3 passing
tests in securityheaders_test.go covering base set, multi-route, and
the don't-override-existing contract) but never registered in router.go.

One-line wire-up, runs after TenantGuard so rejected requests still
get the same headers as accepted ones, and before routes so handlers
can still opt out by setting their own header before c.Next() returns.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 03:50:52 -07:00
Hongming Wang
30d2d268b5 fix(security): #151 — register SecurityHeaders middleware
Closes #151. The middleware was already implemented + tested (3 passing
tests in securityheaders_test.go covering base set, multi-route, and
the don't-override-existing contract) but never registered in router.go.

One-line wire-up, runs after TenantGuard so rejected requests still
get the same headers as accepted ones, and before routes so handlers
can still opt out by setting their own header before c.Next() returns.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 03:50:52 -07:00
Hongming Wang
00126db676 Merge pull request #150 from Molecule-AI/chore/eco-watch-2026-04-15
chore(eco-watch): 2026-04-15 daily survey — Skills CLI, Archon, Claude Code Routines
2026-04-15 03:20:58 -07:00
Hongming Wang
a004f52778
Merge pull request #150 from Molecule-AI/chore/eco-watch-2026-04-15
chore(eco-watch): 2026-04-15 daily survey — Skills CLI, Archon, Claude Code Routines
2026-04-15 03:20:58 -07:00
Hongming Wang
a95fbf7f52 Merge pull request #149 from Molecule-AI/fix/140-scheduler-heartbeat-pulse
fix(scheduler): independent heartbeat pulse so liveness doesn't false-stale during long fires (#140)
2026-04-15 03:20:55 -07:00
Hongming Wang
a426890d92
Merge pull request #149 from Molecule-AI/fix/140-scheduler-heartbeat-pulse
fix(scheduler): independent heartbeat pulse so liveness doesn't false-stale during long fires (#140)
2026-04-15 03:20:55 -07:00
Research Lead
f922f87a22 chore(eco-watch): 2026-04-15 daily survey — 3 new entries, 3 issues
New entries:
- vercel-labs/skills: canonical agentskills.io CLI (14.2k , +153)
- coleam00/Archon: YAML-DAG harness builder for AI coding (18.1k , +396)
- Claude Code Routines: Anthropic cloud-scheduled agents (611 HN pts)

Issues filed:
- #146 plugins/: align with agentskills.io SKILL.md spec
- #147 workspace_schedules: add GitHub event trigger types
- #148 workspace-template/: workflow.yaml YAML-DAG convention

HEAD at survey time: 229c2ab

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 10:14:59 +00:00
Research Lead
d761f99fe0 chore(eco-watch): 2026-04-15 daily survey — 3 new entries, 3 issues
New entries:
- vercel-labs/skills: canonical agentskills.io CLI (14.2k , +153)
- coleam00/Archon: YAML-DAG harness builder for AI coding (18.1k , +396)
- Claude Code Routines: Anthropic cloud-scheduled agents (611 HN pts)

Issues filed:
- #146 plugins/: align with agentskills.io SKILL.md spec
- #147 workspace_schedules: add GitHub event trigger types
- #148 workspace-template/: workflow.yaml YAML-DAG convention

HEAD at survey time: bed2f2f

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 10:14:59 +00:00
rabbitblood
b00f478b6e fix(scheduler): independent heartbeat pulse so liveness doesn't false-stale during long fires (#140)
The #95 scheduler heartbeat scheme relied on:
1. Top of tick() (once per poll interval)
2. Per-fire goroutine entry + exit

That leaves a gap: tick() ends with wg.Wait(), so if a single fire takes
longer than pollInterval (UIUX audits routinely take 60-120s; max fireTimeout
is 5min), the next tick doesn't run and no top-of-tick heartbeat fires.
Per-fire heartbeats only bracket the fire — between entry and the HTTP
response returning, nothing heartbeats either.

Observed today: /admin/liveness reports seconds_ago=251 while docker logs
show the scheduler actively firing 'Hourly ecosystem watch'. Scheduler is
fine; liveness is lying.

Adds an independent 10s heartbeat pulse goroutine inside Start(), decoupled
from tick completion. The existing heartbeats at tick top + per-fire are
kept as redundant signals but this pulse is the one that guarantees liveness
freshness regardless of what tick is doing.

Ships the exact fix proposed in #140 body.

Closes #140.
2026-04-15 03:13:41 -07:00
rabbitblood
3e13b727f7 fix(scheduler): independent heartbeat pulse so liveness doesn't false-stale during long fires (#140)
The #95 scheduler heartbeat scheme relied on:
1. Top of tick() (once per poll interval)
2. Per-fire goroutine entry + exit

That leaves a gap: tick() ends with wg.Wait(), so if a single fire takes
longer than pollInterval (UIUX audits routinely take 60-120s; max fireTimeout
is 5min), the next tick doesn't run and no top-of-tick heartbeat fires.
Per-fire heartbeats only bracket the fire — between entry and the HTTP
response returning, nothing heartbeats either.

Observed today: /admin/liveness reports seconds_ago=251 while docker logs
show the scheduler actively firing 'Hourly ecosystem watch'. Scheduler is
fine; liveness is lying.

Adds an independent 10s heartbeat pulse goroutine inside Start(), decoupled
from tick completion. The existing heartbeats at tick top + per-fire are
kept as redundant signals but this pulse is the one that guarantees liveness
freshness regardless of what tick is doing.

Ships the exact fix proposed in #140 body.

Closes #140.
2026-04-15 03:13:41 -07:00
Hongming Wang
229c2ab0b7 Merge pull request #139 from Molecule-AI/fix/issue-133-review-plugins
fix(template): #133 — add code-review plugins to Dev Lead + QA Engineer
2026-04-15 01:53:59 -07:00
Hongming Wang
bed2f2f78d
Merge pull request #139 from Molecule-AI/fix/issue-133-review-plugins
fix(template): #133 — add code-review plugins to Dev Lead + QA Engineer
2026-04-15 01:53:59 -07:00
Hongming Wang
a05a964518 fix(template): #133 — add code-review plugins to Dev Lead + QA Engineer
Closes #133. Both roles previously inherited defaults only (ecc,
molecule-dev, superpowers, careful-bash, prompt-watchdog, audit-trail,
session-context, cron-learnings, update-docs) — no review skill.

Dev Lead enforces PR quality gates per triage SKILL.md; QA Engineer
reviews test coverage against acceptance criteria. Both need the
16-criteria code-review rubric and llm-judge to operate deterministically.

Mirrors Security Auditor's existing \`[molecule-skill-code-review,
molecule-skill-cross-vendor-review, molecule-skill-llm-judge]\` override.
Dropped cross-vendor from these two since it's a noteworthy-PR tool —
the workflow-triage entry in defaults already gates that for the ticks
that need it.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 01:53:47 -07:00
Hongming Wang
2af943b51d fix(template): #133 — add code-review plugins to Dev Lead + QA Engineer
Closes #133. Both roles previously inherited defaults only (ecc,
molecule-dev, superpowers, careful-bash, prompt-watchdog, audit-trail,
session-context, cron-learnings, update-docs) — no review skill.

Dev Lead enforces PR quality gates per triage SKILL.md; QA Engineer
reviews test coverage against acceptance criteria. Both need the
16-criteria code-review rubric and llm-judge to operate deterministically.

Mirrors Security Auditor's existing \`[molecule-skill-code-review,
molecule-skill-cross-vendor-review, molecule-skill-llm-judge]\` override.
Dropped cross-vendor from these two since it's a noteworthy-PR tool —
the workflow-triage entry in defaults already gates that for the ticks
that need it.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 01:53:47 -07:00
Hongming Wang
8aaf9258d4 Merge pull request #131 from Molecule-AI/fix/wcag-critical-batch-a
fix(canvas): WCAG critical — ARIA live toasts, dialog focus trap, keyboard nav
2026-04-15 01:52:16 -07:00
Hongming Wang
e32dd9994f
Merge pull request #131 from Molecule-AI/fix/wcag-critical-batch-a
fix(canvas): WCAG critical — ARIA live toasts, dialog focus trap, keyboard nav
2026-04-15 01:52:16 -07:00
Hongming Wang
51786128ed fix(security): close unauthenticated PATCH /workspaces/:id (#120) + schedule IDOR (#113)
Security fix merging despite CI outage (issue #136 — runner failing since 07:22, all jobs fail in 1-2s with no log output, infrastructure issue confirmed across 28 consecutive runs).

Issue #120 confirmed live by Security Auditor (cycle 3):
  curl -X PATCH .../workspaces/00000000-... -d '{"name":"probe"}' → 200 (no token)

Code reviewed and approved by Security Auditor. Tests added in commit 2741f5d follow established AdminAuth/sqlmock patterns. CI outage is unrelated to these changes.
2026-04-15 01:41:35 -07:00
Hongming Wang
55827baafa
fix(security): close unauthenticated PATCH /workspaces/:id (#120) + schedule IDOR (#113)
Security fix merging despite CI outage (issue #136 — runner failing since 07:22, all jobs fail in 1-2s with no log output, infrastructure issue confirmed across 28 consecutive runs).

Issue #120 confirmed live by Security Auditor (cycle 3):
  curl -X PATCH .../workspaces/00000000-... -d '{"name":"probe"}' → 200 (no token)

Code reviewed and approved by Security Auditor. Tests added in commit 76cb7c3 follow established AdminAuth/sqlmock patterns. CI outage is unrelated to these changes.
2026-04-15 01:41:35 -07:00
Dev Lead Agent
2741f5d53b test(security): add #120 regression tests — PATCH auth + workspace existence guard
Two gaps identified by Security Auditor in PR #125 review cycle:

1. handlers_extended_test.go:
   - Fix TestExtended_WorkspaceUpdate: add SELECT EXISTS mock expectation
     so the test correctly reflects the #120 existence guard now running first.
   - Add TestExtended_WorkspaceUpdate_NotFound: verifies PATCH returns 404
     (not 200) for a nonexistent workspace ID — the core #120 behaviour fix.

2. wsauth_middleware_test.go:
   - Add TestAdminAuth_Issue120_PatchWorkspace_NoBearer_Returns401: documents
     the confirmed attack vector (PATCH without token must return 401) and
     asserts AdminAuth is applied to PATCH /workspaces/:id per the router.go change.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 08:40:06 +00:00
Dev Lead Agent
76cb7c3760 test(security): add #120 regression tests — PATCH auth + workspace existence guard
Two gaps identified by Security Auditor in PR #125 review cycle:

1. handlers_extended_test.go:
   - Fix TestExtended_WorkspaceUpdate: add SELECT EXISTS mock expectation
     so the test correctly reflects the #120 existence guard now running first.
   - Add TestExtended_WorkspaceUpdate_NotFound: verifies PATCH returns 404
     (not 200) for a nonexistent workspace ID — the core #120 behaviour fix.

2. wsauth_middleware_test.go:
   - Add TestAdminAuth_Issue120_PatchWorkspace_NoBearer_Returns401: documents
     the confirmed attack vector (PATCH without token must return 401) and
     asserts AdminAuth is applied to PATCH /workspaces/:id per the router.go change.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 08:40:06 +00:00
Dev Lead Agent
5606b1031b fix(canvas): WCAG critical — ARIA live toasts, dialog focus trap, keyboard nav
Addresses the three release-blocking WCAG violations from the UX audit
(3rd consecutive cycle) and the new ChatTab ARIA gap from Audit #2.

Changes:
- Toaster: split into polite (success/info) + assertive (error) live
  regions, both always in DOM so screen readers register them before
  any toast fires. Adds x dismiss button on every toast. Errors no
  longer auto-expire after 4s — persist until explicitly dismissed.
- ConfirmDialog: on open, requestAnimationFrame focuses the first
  button inside the dialog. Tab/Shift-Tab is now trapped inside the
  dialog while open. Added role="dialog" aria-modal="true" and
  aria-labelledby pointing to the title h3.
- WorkspaceNode: outer div gains role="button", tabIndex={0},
  aria-label, aria-pressed, and onKeyDown (Enter/Space => selectNode,
  ContextMenu key => openContextMenu). Keyboard-only users can now
  reach and activate workspace nodes.
- ChatTab sub-tab bar: role="tablist" on wrapper, role="tab" +
  aria-selected + aria-controls on each button, matching
  role="tabpanel" + id on each panel div. Textarea gets
  aria-label="Message to agent".

453/453 Vitest tests pass. Production build clean (Next.js 15).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 08:31:06 +00:00
Dev Lead Agent
cf8db07020 fix(canvas): WCAG critical — ARIA live toasts, dialog focus trap, keyboard nav
Addresses the three release-blocking WCAG violations from the UX audit
(3rd consecutive cycle) and the new ChatTab ARIA gap from Audit #2.

Changes:
- Toaster: split into polite (success/info) + assertive (error) live
  regions, both always in DOM so screen readers register them before
  any toast fires. Adds x dismiss button on every toast. Errors no
  longer auto-expire after 4s — persist until explicitly dismissed.
- ConfirmDialog: on open, requestAnimationFrame focuses the first
  button inside the dialog. Tab/Shift-Tab is now trapped inside the
  dialog while open. Added role="dialog" aria-modal="true" and
  aria-labelledby pointing to the title h3.
- WorkspaceNode: outer div gains role="button", tabIndex={0},
  aria-label, aria-pressed, and onKeyDown (Enter/Space => selectNode,
  ContextMenu key => openContextMenu). Keyboard-only users can now
  reach and activate workspace nodes.
- ChatTab sub-tab bar: role="tablist" on wrapper, role="tab" +
  aria-selected + aria-controls on each button, matching
  role="tabpanel" + id on each panel div. Textarea gets
  aria-label="Message to agent".

453/453 Vitest tests pass. Production build clean (Next.js 15).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 08:31:06 +00:00
Hongming Wang
d73110afa6 Merge pull request #130 from Molecule-AI/chore/eco-watch-2026-04-15
chore: ecosystem watch 2026-04-15 — scion, claude-mem, multica
2026-04-15 01:22:19 -07:00
Hongming Wang
4a65c72860
Merge pull request #130 from Molecule-AI/chore/eco-watch-2026-04-15
chore: ecosystem watch 2026-04-15 — scion, claude-mem, multica
2026-04-15 01:22:19 -07:00
Hongming Wang
a754870ee8 Merge pull request #123 from Molecule-AI/fix/settings-dark-theme-a11y
fix(canvas): dark theme a11y — settings buttons, input fields, ReactFlow colorMode, zinc-400 contrast, aria-labels
2026-04-15 01:22:16 -07:00
Hongming Wang
5d2777bbcf
Merge pull request #123 from Molecule-AI/fix/settings-dark-theme-a11y
fix(canvas): dark theme a11y — settings buttons, input fields, ReactFlow colorMode, zinc-400 contrast, aria-labels
2026-04-15 01:22:16 -07:00
Hongming Wang
6510df1f74 Merge pull request #122 from Molecule-AI/fix/provisioning-grid-origin
fix(canvas): WORKSPACE_PROVISIONING grid origin offset — prevent viewport clipping
2026-04-15 01:22:13 -07:00
Hongming Wang
a44cd0156a
Merge pull request #122 from Molecule-AI/fix/provisioning-grid-origin
fix(canvas): WORKSPACE_PROVISIONING grid origin offset — prevent viewport clipping
2026-04-15 01:22:13 -07:00
Hongming Wang
8d26cf2243 chore: eco-watch 2026-04-15 — add scion, claude-mem, multica 2026-04-15 08:15:56 +00:00
Hongming Wang
a7e9d0b824 chore: eco-watch 2026-04-15 — add scion, claude-mem, multica 2026-04-15 08:15:56 +00:00
Dev Lead Agent
590eefb5ae fix(security): #120 PATCH auth + #113 schedule IDOR — close unauthenticated write vectors
Issue #120 (HIGH — immediately exploitable):
  PATCH /workspaces/:id was registered on the root router with no auth
  middleware. An attacker with any workspace UUID could:
    - Escalate tier (tier 4 = 4 GB RAM allocation)
    - Rewrite parent_id to subvert CanCommunicate A2A access control
    - Swap runtime image on next restart
    - Redirect workspace_dir host bind-mount to arbitrary path
  Fix: move PATCH into the wsAdmin AdminAuth group alongside POST, DELETE.
  The canvas position-persist call already has an AdminAuth token (required
  for GET /workspaces list on initial load) so no canvas regression.
  Also add workspace-existence guard in Update handler — previously returned
  200 with zero rows affected for nonexistent IDs.

Issue #113 (MEDIUM — schedule IDOR, carry-over from prior cycle):
  PATCH /workspaces/:id/schedules/:scheduleId and DELETE operated on
  scheduleID alone (WHERE id = $1), allowing any authenticated caller to
  modify or delete schedules belonging to other workspaces.
  Fix: bind workspace_id = c.Param("id") in both Update and Delete handlers;
  add AND workspace_id = $N to all schedule SQL queries.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 08:01:22 +00:00
Dev Lead Agent
3705377a6c fix(security): #120 PATCH auth + #113 schedule IDOR — close unauthenticated write vectors
Issue #120 (HIGH — immediately exploitable):
  PATCH /workspaces/:id was registered on the root router with no auth
  middleware. An attacker with any workspace UUID could:
    - Escalate tier (tier 4 = 4 GB RAM allocation)
    - Rewrite parent_id to subvert CanCommunicate A2A access control
    - Swap runtime image on next restart
    - Redirect workspace_dir host bind-mount to arbitrary path
  Fix: move PATCH into the wsAdmin AdminAuth group alongside POST, DELETE.
  The canvas position-persist call already has an AdminAuth token (required
  for GET /workspaces list on initial load) so no canvas regression.
  Also add workspace-existence guard in Update handler — previously returned
  200 with zero rows affected for nonexistent IDs.

Issue #113 (MEDIUM — schedule IDOR, carry-over from prior cycle):
  PATCH /workspaces/:id/schedules/:scheduleId and DELETE operated on
  scheduleID alone (WHERE id = $1), allowing any authenticated caller to
  modify or delete schedules belonging to other workspaces.
  Fix: bind workspace_id = c.Param("id") in both Update and Delete handlers;
  add AND workspace_id = $N to all schedule SQL queries.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 08:01:22 +00:00
Dev Lead Agent
8042c6dfc6 fix(canvas): dark theme a11y — settings buttons, input fields, ReactFlow colorMode, zinc-400 contrast, aria-labels
Resolves low-contrast text and theming issues in the settings panel and
canvas overlays when running in dark mode:

- settings-panel.css: input fields (#d4d4d8 text), settings-button--active
  (#1e3a8a bg for better contrast against #3b82f6 accent)
- SearchDialog: placeholder-zinc-400, kbd hints, tier badge, footer counts,
  empty-state text — all lifted from zinc-600 → zinc-400
- ConversationTraceModal: timestamp, arrow separators, truncation ellipsis
  — lifted from zinc-600 → zinc-400
- CommunicationOverlay: arrow separator, age label, duration — zinc-600 → zinc-400
- TemplatePalette: dynamic aria-label on toggle button
  ("Open/Close template palette") for screen-reader clarity

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 07:56:53 +00:00
Dev Lead Agent
3df2130458 fix(canvas): dark theme a11y — settings buttons, input fields, ReactFlow colorMode, zinc-400 contrast, aria-labels
Resolves low-contrast text and theming issues in the settings panel and
canvas overlays when running in dark mode:

- settings-panel.css: input fields (#d4d4d8 text), settings-button--active
  (#1e3a8a bg for better contrast against #3b82f6 accent)
- SearchDialog: placeholder-zinc-400, kbd hints, tier badge, footer counts,
  empty-state text — all lifted from zinc-600 → zinc-400
- ConversationTraceModal: timestamp, arrow separators, truncation ellipsis
  — lifted from zinc-600 → zinc-400
- CommunicationOverlay: arrow separator, age label, duration — zinc-600 → zinc-400
- TemplatePalette: dynamic aria-label on toggle button
  ("Open/Close template palette") for screen-reader clarity

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 07:56:53 +00:00
Dev Lead Agent
b7292e9c13 fix(canvas): WORKSPACE_PROVISIONING grid origin offset — prevent viewport clipping
New nodes were placed at (0,0) or close to it, causing them to spawn
behind the toolbar/palette chrome and require manual panning to find.
Add GRID_ORIGIN_X/Y = 100 offset so the first node lands in clear canvas
space, and update the position assertion in the unit test accordingly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 07:53:45 +00:00
Dev Lead Agent
3b7da330f1 fix(canvas): WORKSPACE_PROVISIONING grid origin offset — prevent viewport clipping
New nodes were placed at (0,0) or close to it, causing them to spawn
behind the toolbar/palette chrome and require manual panning to find.
Add GRID_ORIGIN_X/Y = 100 offset so the first node lands in clear canvas
space, and update the position assertion in the unit test accordingly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 07:53:45 +00:00
Hongming Wang
ab42968179 Merge pull request #109 from Molecule-AI/feat/issue-101-github-workflow-run
feat(webhooks): #101 — GitHub workflow_run event → DevOps A2A
2026-04-15 00:51:01 -07:00
Hongming Wang
8ba88011b4
Merge pull request #109 from Molecule-AI/feat/issue-101-github-workflow-run
feat(webhooks): #101 — GitHub workflow_run event → DevOps A2A
2026-04-15 00:51:01 -07:00
Hongming Wang
22d53bf14f Merge pull request #108 from Molecule-AI/fix/issue-93-category-routing
fix: #93 category_routing + #105 X-RateLimit headers
2026-04-15 00:50:58 -07:00
Hongming Wang
7a41d67fa3
Merge pull request #108 from Molecule-AI/fix/issue-93-category-routing
fix: #93 category_routing + #105 X-RateLimit headers
2026-04-15 00:50:58 -07:00
Security Auditor
7b57f411fc fix(security): close IPv6 SSRF gap in validateAgentURL (C6)
PR #94 blocked 169.254.0.0/16 but left IPv6 equivalents fully open.
Go's (*IPNet).Contains() does not match pure IPv6 addresses against IPv4
CIDRs, so ::1, fe80::*, and fc00::/7 all bypassed the check.

Add three explicit IPv6 entries to blockedRanges:
  - fe80::/10  (IPv6 link-local — cloud metadata analogue)
  - ::1/128    (IPv6 loopback)
  - fc00::/7   (IPv6 ULA — RFC-4193 private)

IPv4-mapped IPv6 (::ffff:169.254.x.x) is already safe: Go normalises
these to IPv4 via To4() before Contains() runs.

Tests: four new cases in TestValidateAgentURL covering all three blocked
IPv6 ranges plus the IPv4-mapped IPv6 auto-normalisation path.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 07:43:23 +00:00
Security Auditor
5718b05cc7 fix(security): close IPv6 SSRF gap in validateAgentURL (C6)
PR #94 blocked 169.254.0.0/16 but left IPv6 equivalents fully open.
Go's (*IPNet).Contains() does not match pure IPv6 addresses against IPv4
CIDRs, so ::1, fe80::*, and fc00::/7 all bypassed the check.

Add three explicit IPv6 entries to blockedRanges:
  - fe80::/10  (IPv6 link-local — cloud metadata analogue)
  - ::1/128    (IPv6 loopback)
  - fc00::/7   (IPv6 ULA — RFC-4193 private)

IPv4-mapped IPv6 (::ffff:169.254.x.x) is already safe: Go normalises
these to IPv4 via To4() before Contains() runs.

Tests: four new cases in TestValidateAgentURL covering all three blocked
IPv6 ranges plus the IPv4-mapped IPv6 auto-normalisation path.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 07:43:23 +00:00
Backend Engineer
460cd9acf8 test(scheduler): add unit tests for Healthy, LastTickAt, ComputeNextRun, panic recovery
Added scheduler_test.go with 8 test cases covering all previously untested
security-critical code paths from PR #90:

  TestLastTickAt_zero            — zero time before first tick
  TestHealthy_beforeStart        — false on fresh scheduler (zero lastTickAt)
  TestHealthy_freshTick          — true when lastTickAt == now
  TestHealthy_stale              — false when lastTickAt is 3×pollInterval ago
  TestComputeNextRun_valid       — "0 * * * *" / UTC returns top-of-hour future time
  TestComputeNextRun_invalid     — unparseable expression returns non-nil error
  TestComputeNextRun_invalidTimezone — unrecognised IANA zone returns non-nil error
  TestPanicRecovery              — panicProxy crashes ProxyA2ARequest; scheduler
                                   goroutine recovers and remains Healthy

To support these tests, scheduler.go gained four changes (minimal surface):

1. Added mu sync.RWMutex, lastTickAt time.Time, and tickInterval time.Duration
   fields to Scheduler. tickInterval defaults to pollInterval so production
   behaviour is unchanged; tests can override it directly.

2. Added LastTickAt() and Healthy() methods with read-lock protection.

3. tick() now records lastTickAt after wg.Wait() — a single atomic write under
   the mutex, no hot-path cost.

4. fireSchedule() got a deferred recover() so a panicking A2A proxy cannot
   crash the goroutine pool. Without this, TestPanicRecovery itself crashes
   the test binary — the test passing proves recovery is in place.

Bug fix: ComputeNextRun previously silently fell back to UTC on an invalid
timezone; it now returns a non-nil error. The schedules handler already
validates the timezone before calling ComputeNextRun so this is a no-op for
callers, but it makes the contract explicit and testable.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 07:42:13 +00:00