Commit Graph

396 Commits

Author SHA1 Message Date
PM Bot
409a249ca6 chore(test): remove dead constants from wsauth_middleware_test.go (#358)
PR #357 deleted the grace-period tests that used hasLiveTokenQuery and
workspaceExistsQuery, but the constants themselves (and the stale comment
describing the old HasAnyLiveToken-based dispatch) were not removed.

Remove both dead const declarations and update the header comment to
reflect the strict-enforcement contract introduced by #357.

Closes #358.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-16 05:02:11 +00:00
Hongming Wang
d09e72c5fd Merge pull request #357 from Molecule-AI/fix/issue-351-remove-tokenless-grace-period
All CI green. Merges strict WorkspaceAuth — removes tokenless grace period that enabled zombie workspace enumeration (#351).
2026-04-15 21:57:17 -07:00
Hongming Wang
b2b0045913 fix(security): remove WorkspaceAuth tokenless grace period (#351)
Severity HIGH. #318 closed the fake-UUID fail-open for WorkspaceAuth
but left the grace period intact for *real* workspaces with no live
tokens. Zombie test-artifact workspaces from prior DAST runs still
exist in the DB with empty configs and no tokens, so they pass
WorkspaceExists=true but HasAnyLiveToken=false — and fell through the
grace period, leaking every global-secret key name to any
unauthenticated caller on the Docker network.

Phase 30.1 shipped months ago; every production workspace has gone
through multiple boot cycles and acquired a token since. The
"legacy workspaces grandfathered" window no longer serves legitimate
traffic. Removing it entirely is the cleanest fix — and does NOT
affect registration (which is on /registry/register, outside this
middleware's scope).

New contract (strict):

  every /workspaces/:id/* request MUST carry
  Authorization: Bearer <token-for-this-workspace>

Any missing/mismatched/revoked/wrong-workspace bearer → 401. No
existence check, no fallback. The wsauth.WorkspaceExists helper is
kept in the package for any future caller but no longer used here.

Tests:
- TestWorkspaceAuth_351_NoBearer_Returns401_NoDBCalls — new, covers
  fake UUID / zombie / pre-token in one sub-table. Asserts zero DB
  calls on missing bearer.
- Existing C4/C8 + #170 tests updated to drop the stale
  HasAnyLiveToken sqlmock expectations.
- Renamed TestWorkspaceAuth_Issue170_SecretDelete_FailOpen_NoTokens
  to _NoTokensStillRejected and flipped the assertion from 200 to 401.
- Dropped TestWorkspaceAuth_318_ExistsQueryError_Returns500 — the
  code path it covered no longer exists.

Full platform test sweep green.

Closes #351

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 21:52:44 -07:00
Hongming Wang
742d061787 Merge pull request #350 from Molecule-AI/chore/eco-watch-2026-04-16b
chore(eco-watch): 2026-04-16b survey — AgentScope + Plannotator
2026-04-15 21:47:50 -07:00
Research Lead
93720565b0 chore(eco-watch): 2026-04-16b survey — AgentScope + Plannotator
Add two new entries to docs/ecosystem-watch.md:

- **AgentScope** (modelscope/agentscope, ~23.8k , Apache 2.0,
  v1.0.18 March 26 2026): Alibaba/ModelScope multi-agent framework
  with MCP support, MsgHub typed routing, and OpenTelemetry
  observability. No canvas or workspace lifecycle — framework-layer
  complement, not a platform competitor.

- **Plannotator** (backnotprop/plannotator, ~4.3k , Apache 2.0+MIT,
  v0.17.10 April 13 2026): Browser-based agent plan annotation tool
  with structured feedback types (delete/insert/replace/comment).
  Directly informs our hitl.py feedback schema. Filed #349 to add
  structured feedback types to resume_task.

HEAD at survey time: 0897f9e

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-16 04:40:51 +00:00
Hongming Wang
0897f9e59c Merge pull request #346 from Molecule-AI/chore/issue-342-auditor-prompt-drift
chore(auditor): close #319 + #337 prompt drift on Security Auditor (#342)
2026-04-15 21:31:06 -07:00
Hongming Wang
d8183e16cc Merge pull request #343 from Molecule-AI/fix/issue-337-webhook-secret-constant-time
fix(security): constant-time webhook_secret comparison (#337)
2026-04-15 21:31:02 -07:00
Hongming Wang
c6a721fd56 Merge pull request #341 from Molecule-AI/fix/publish-platform-image-keychain-again
fix(ci): disable osxkeychain credsStore on self-hosted runner (#199 follow-up)
2026-04-15 21:30:59 -07:00
Hongming Wang
c7477047c2 Merge pull request #338 from Molecule-AI/fix/issue-328-transcript-fail-closed
fix(security): /transcript fails closed when auth token missing (#328)
2026-04-15 21:30:56 -07:00
Hongming Wang
2da48dda13 chore(auditor): close #319 + #337 prompt drift on Security Auditor (#342)
Two recent platform-level security changes (#319 channel_config
encryption, #337 constant-time webhook_secret compare) were not
reflected in the Security Auditor's system prompt or the schedule cron
prompt. That meant the auditor wouldn't proactively look for the
*next* instance of either class — a new credential field added to
channel_config without being added to sensitiveFields, or a new
secret comparison using raw `!=`, would slip through until a human
happened to notice.

Updated two files:

1. org-templates/molecule-dev/security-auditor/system-prompt.md
   Added two bullets to "What You Check":
   - Secret comparisons must use subtle.ConstantTimeCompare /
     crypto.timingSafeEqual (cites #337 as the repo's recent instance)
   - Secret storage at rest: any new channel_config credential field
     must be added to sensitiveFields and exercised in both the
     Encrypt (write) and Decrypt (read) boundary helpers, and the
     ec1: prefix must never leak into API responses (cites #319)

2. org-templates/molecule-dev/org.yaml
   Same two checks added to the Security Auditor's 12-hour cron
   prompt's "MANUAL REVIEW of every changed file" section. Wording
   is concrete enough to paste into a grep: "flag any `!=` / `==` /
   bytes.Equal against a user-supplied value that gates auth".

Pure config / prompt — no code changes, no tests to write. YAML parse
verified, TestPlugins_UnionWithDefaults still passes.

Closes #342

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 21:24:34 -07:00
Hongming Wang
7af8f33bcc fix(security): constant-time webhook_secret comparison (#337)
Severity LOW. The /webhooks/:type handler compared the Telegram
X-Telegram-Bot-Api-Secret-Token header against the decrypted
webhook_secret using Go's `!=` operator, which short-circuits on the
first mismatched byte. Under low-latency Docker-network conditions an
attacker could time response latency byte-by-byte and converge on the
real secret, then inject Telegram-formatted messages into any channel.

Fix: switch to crypto/subtle.ConstantTimeCompare, which runs in time
proportional to the length of the shorter input regardless of content
match. Same posture as the cdp-proxy token compare in host-bridge
(which already used timingSafeEqual).

Risk profile over the public internet is low (Telegram webhooks have
natural jitter that masks the signal), but the defensive pattern
matters for consistency across all secret comparisons.

Closes #337

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 21:23:12 -07:00
Hongming Wang
94a9f92c50 fix(security): scope PausePollersForToken to requesting workspace (closes #329)
CI 5/6 pass (E2E cancel = run-supersession pattern). Dev Lead review 04:21:  Approved. Fixes cross-tenant token exposure: PausePollersForToken now scoped to requesting workspace_id via SQL WHERE clause. Closes #329.
2026-04-15 21:22:50 -07:00
Hongming Wang
9ea6fc23e0 chore(eco-watch): 2026-04-16 daily survey — Gemini CLI + open-multi-agent
CI fully green. Dev Lead review:  Approved. Docs-only: adds Gemini CLI and open-multi-agent entries to ecosystem-watch.md; files issues #332 (gemini-cli adapter) and #333 (PM goal-decomp skill).
2026-04-15 21:22:37 -07:00
Hongming Wang
aa2a283835 fix(ci): explicitly disable osxkeychain credsStore for self-hosted runner
#273 tried to fix the macOS Keychain -25308 error by pointing
DOCKER_CONFIG at a per-run temp dir with `{"auths": {}}`. That was
necessary but not sufficient: Docker on macOS inherits `osxkeychain` as
the default credsStore even when config.json doesn't declare one
(comes from Docker Desktop's bundled binding), so the login-action
still tried to call /usr/local/bin/docker-credential-osxkeychain which
fails with -25308 from the non-interactive launchd session.

Evidence: after #273, publish-platform-image still failed on every
main merge with:

  error saving credentials: error storing credentials - err: exit
  status 1, out: `User interaction is not allowed. (-25308)`

Fix: write a config.json that explicitly sets `credsStore: ""` and
clears `credHelpers`, forcing Docker to store creds in the inline
`auths` map of this disposable config.json instead of reaching for
the keychain. Also print config.json at diagnostic time so a future
regression surfaces in the log instead of at login.

No runtime / test impact — this only changes what the runner writes
to the workflow's temp DOCKER_CONFIG directory.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 21:20:06 -07:00
Hongming Wang
0e46afa4b9 fix(security): hitl task-id ownership + wire fail_open_if_no_scanner in loader (closes #265, #268)
Security audit cycle 13: hitl.py LGTM (workspace-scoped task IDs). Loader.py fix applied (commit 0557f73): fail_open_if_no_scanner now read from config and forwarded to scan_skill_dependencies(); regression test added. CI 5/6 pass (E2E cancel = run-supersession pattern). Closes #265. Closes #268.
2026-04-15 21:18:52 -07:00
Hongming Wang
e1cdb5c9c6 fix(security): /transcript endpoint fails closed when auth token missing (#328)
Severity HIGH. The /transcript route in main.py used `if expected:`
around the bearer-token compare, so `get_token()` returning None (no
/configs/.auth_token on disk — bootstrap window, deleted file, OSError)
silently skipped the entire auth check. Any container on
molecule-monorepo-net could GET /transcript during the provisioning
window and walk away with the full session log (user messages, Claude
tool calls, assistant replies).

The platform's TranscriptHandler always has a valid token (it acquired
one at workspace registration), so tightening this gate has no
legitimate-caller impact. Only unauthenticated sniffers lose access,
which was never the intended contract of #287.

Fix:

1. Extracted the auth gate into `workspace-template/transcript_auth.py`
   — a 20-line module with no heavy imports so the security-critical
   code is unit-testable without standing up the full uvicorn/a2a/httpx
   stack (the former inline guard could only be tested end-to-end,
   which explains why the regression shipped in #287).

2. `transcript_authorized(expected, auth_header)` returns False when
   `expected` is None or empty — the #328 fix — and otherwise does
   strict equality against "Bearer <expected>".

3. main.py's inline handler calls the extracted function:
     if not _transcript_authorized(get_token(), auth_header):
         return 401

4. New tests/test_transcript_auth.py covers: None token, empty token,
   valid bearer, wrong bearer, missing header, case-sensitive prefix,
   whitespace fuzzing. All 7 pass.

Closes #328

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 21:17:37 -07:00
Hongming Wang
2eec33a279 chore(org): wire molecule-compliance + molecule-audit + molecule-freeze-scope (closes #322)
Config-only YAML. CI green on all 6 checks (E2E cancel = run-supersession pattern). Adds missing plugin wiring: Security Auditor→compliance+audit, Backend→compliance, QA→compliance, DevOps→freeze-scope. Closes #322.
2026-04-15 21:13:26 -07:00
Hongming Wang
bf7614750a docs(glossary): add terminology disambiguation table (closes #320)
CI fully green (all 6 checks pass). Docs-only: adds docs/glossary.md, links from README.md and CLAUDE.md. Closes #320.
2026-04-15 21:13:04 -07:00
Hongming Wang
bf2022acf1 fix(security): encrypt channel_config bot_token at rest (closes #319)
CI fully green. Dev Lead code review:  clean, all read/write paths verified, tests cover round-trip + idempotency + legacy plaintext. Closes #319.
2026-04-15 21:09:34 -07:00
Hongming Wang
027d2d213f fix(security): close WorkspaceAuth fail-open on non-existent workspace IDs (#318)
CI fully green. Security Audit cycle 15 LGTM. Closes #318. Closes #325.
2026-04-15 21:02:29 -07:00
Hongming Wang
02cd80c5f6 chore(template): widen idle-loop to Market Analyst + Competitive Intelligence (wave 2)
Expands autonomous orchestration reach to Market Analyst and Competitive Intelligence roles.
2026-04-15 20:29:41 -07:00
Hongming Wang
c71bd04cf1 fix(template): Telegram channel for Security Auditor + DevOps Engineer (#246 #247)
Closes #246
Closes #247

Critical security findings and CI build-break alerts are now pushed via Telegram instead of waiting for someone to manually check memory/logs.
2026-04-15 19:57:34 -07:00
Hongming Wang
2d1d2c6a97 Merge pull request #314 from Molecule-AI/fix/issue-310-llm-judge-be-fe
feat(template): add molecule-skill-llm-judge to Backend + Frontend Engineer (#310)
2026-04-15 19:51:00 -07:00
Hongming Wang
af06c1e702 feat(template): add molecule-skill-llm-judge to Backend + Frontend Engineer (#310)
Backend Engineer and Frontend Engineer were missing molecule-skill-llm-judge
while Dev Lead, QA Engineer, and Security Auditor already have it.

llm-judge lets engineers self-gate their PR against the issue body before
requesting review, catching 'shipped the wrong thing' before Dev Lead sees it.
No new plugins needed — already installed org-wide.

Closes #310
2026-04-16 02:48:08 +00:00
Hongming Wang
5451164cba fix(security): add bearer token auth to /transcript endpoint (#287)
Closes #287

Any container on molecule-monorepo-net could previously read the full Claude session log without authentication. Guard uses get_token() from platform_auth — skipped only before workspace registration (dev-mode).
2026-04-15 19:47:23 -07:00
airenostars
1fb9712fa4 feat(reno-stars): citation-builder — one backlink directory per day (#299)
Closes #301

Co-authored-by: airenostars <noreply@github.com>
2026-04-15 19:47:20 -07:00
Hongming Wang
9b08c34707 Merge pull request #308 from Molecule-AI/fix/uiux-cron-cadence-hourly
fix(template): UIUX Designer cron from 15min to hourly (#306)
2026-04-15 19:22:29 -07:00
Hongming Wang
dd10c0d1a2 fix(template): UIUX Designer cron from 15min to hourly (#306)
Closes #306. The cron expression was "5,20,35,50 * * * *" (every 15
min = 96 ticks/day) despite the schedule being named "Hourly UI/UX
audit". Each tick launches Chromium, takes 8 screenshots, runs them
through Claude vision, and delegates to PM — 768 vision calls/day
from one workspace with no meaningful delta between ticks (canvas UI
only changes on deploys).

Changed to "5 * * * *" (hourly, at :05 past the hour). 6x reduction
in cost + noise.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 19:22:19 -07:00
Hongming Wang
9b2fe1e7b4 Merge pull request #307 from Molecule-AI/fix/backend-engineer-security-scan
feat(template): add molecule-security-scan to Backend Engineer (#303)
2026-04-15 19:21:19 -07:00
Hongming Wang
3fefad4534 feat(template): add molecule-security-scan to Backend Engineer (#303)
Closes #303. Surfaces CVE/secret scanning at dev time instead of
waiting for the Security Auditor's 12h cron. Backend Engineer's
plugin list: [molecule-hitl, molecule-skill-code-review,
molecule-security-scan].

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 19:21:11 -07:00
Hongming Wang
84d5e395d4 fix(a2a-tools): auth_headers on recall_memory + commit_memory (#304)
Adds auth_headers to recall_memory and commit_memory in a2a_tools.py. Fixes the #215-class auth regression for A2A memory tools. Test mocks updated to accept headers kwarg.
2026-04-15 19:12:18 -07:00
Hongming Wang
1453e57fb7 Merge pull request #297 from Molecule-AI/fix/cdp-plist-chmod-600
fix(security): chmod 600 macOS launchd plist (#296)
2026-04-15 18:20:55 -07:00
Hongming Wang
5ccceb92f9 fix(security): chmod 600 macOS launchd plist containing CDP token (#296)
One-liner oversight from #295: the macOS install path wrote the plist
with the default umask (~0644), leaving CDP_PROXY_TOKEN world-readable
to any local user account. The Linux path already writes to a chmod
600 env-file — this brings macOS to parity.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 18:20:48 -07:00
Hongming Wang
7955a9cd76 Merge pull request #295 from Molecule-AI/fix/cdp-proxy-bind-localhost
fix(security): token-auth on cdp-proxy to prevent LAN exposure (#293)
2026-04-15 18:00:30 -07:00
Hongming Wang
8cc325eb3b fix(security): token-auth on cdp-proxy to prevent LAN exposure (#293)
HIGH finding from security-auditor on PR #291 (merged tick-37). The
cdp-proxy bound to 0.0.0.0:9223 with no authentication, exposing
Chrome DevTools Protocol — full remote control of any tab, including
cookie/localStorage exfiltration — to anyone on the same WiFi/LAN.

Root cause: Docker Desktop on macOS routes host.docker.internal
through the VM network interface, not loopback. Binding to 127.0.0.1
would break the primary use case (containers reaching the host
Chrome). The design trade was "bind wide for reachability, accept LAN
exposure" — #293 makes that trade unacceptable.

Fix: bearer token auth on every HTTP + WebSocket request. The proxy
REFUSES TO START without a token — no unauth mode.

Three-file change:

1. cdp-proxy.cjs
   - Read token from CDP_PROXY_TOKEN env OR ~/.molecule-cdp-proxy-token
   - Fail loudly if neither is set (exit 1 with install-host-bridge.sh
     pointer)
   - Validate X-CDP-Proxy-Token header via crypto.timingSafeEqual on
     every HTTP request AND every WS upgrade
   - Strip the header before forwarding to Chrome (defense in depth —
     token never leaks into Chrome's request log)

2. install-host-bridge.sh
   - New ensure_token() function generates a 64-char hex token via
     openssl rand -hex 32 (fallback to /dev/urandom). Written to
     ~/.molecule-cdp-proxy-token with chmod 600.
   - macOS: token injected into launchd plist EnvironmentVariables
   - Linux: written to ~/.molecule-cdp-proxy.env (chmod 600) and
     referenced via systemd EnvironmentFile — avoids embedding the
     token in the often world-readable unit file
   - Install reuses existing token if present (16+ chars); uninstall
     preserves token file so a reinstall keeps the same token
   - Verify command now includes the token header
   - Documents container-side bind-mount pattern
     (-v ~/.molecule-cdp-proxy-token:/run/secrets/cdp-proxy-token:ro)

3. lib/connect.js
   - New loadProxyToken() with precedence: env var >
     /run/secrets/cdp-proxy-token > ~/.molecule-cdp-proxy-token
   - Attaches X-CDP-Proxy-Token header on both /json/version probe +
     final puppeteer.connect() call via headers: {} option
     (puppeteer-core v21+ supports this natively)
   - Host-direct fallback (CDP port 9222 on loopback) unchanged —
     Chrome's own port is loopback-only so it doesn't need the token

Attack surface now:
  - LAN attacker must also steal the token file from the user's home
    directory (requires shell access) OR the env var (requires
    launchd/systemd process inspection as the same user) — reduces to
    local-privilege-escalation territory
  - Containers on the same Docker network still have access (they
    mount the token by design) — intentional, any workspace-template
    install already runs inside the platform's trust boundary

Not fixing in this PR:
  - Rate limiting on /json/version (low priority — probe-and-mine is
    expensive even without)
  - IP allowlist on top of token auth (diminishing returns)
  - Rotating the token periodically (user can rm ~/.molecule-cdp-proxy-token
    and reinstall)

Closes #293.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 18:00:02 -07:00
Hongming Wang
5ff32c533c Merge pull request #271 from Molecule-AI/fix/seo-builder-delegate-code-blockers
fix(reno-stars): SEO Builder delegates code blockers to Dev Leader, not human
2026-04-15 17:56:09 -07:00
Hongming Wang
72d30c0b14 Merge pull request #270 from Molecule-AI/feat/workspace-transcript-endpoint
feat: GET /workspaces/:id/transcript — live agent session log
2026-04-15 17:55:41 -07:00
Hongming Wang
261ec275ea Merge pull request #292 from Molecule-AI/feat/reno-stars-social-publish-helpers
feat(reno-stars): social-publish skill with 7 battle-tested helpers
2026-04-15 17:53:58 -07:00
Hongming Wang
0e346f8a7c Merge pull request #291 from Molecule-AI/feat/browser-automation-cdp-proxy-bundled
feat(browser-automation): bundle host-bridge CDP proxy for portable Chrome access
2026-04-15 17:53:31 -07:00
airenostars
39179dfcb3 fix(reno-stars): SEO Builder delegates code blockers to Dev Leader, not human
Issue surfaced in SEO Builder Run 10 (2026-04-15):
- Marketing Leader found 2 code-level metadata blockers
  (white-rock page.tsx override + en.json description >160c)
- Telegram report listed them under "⚠️ ACTION ITEMS (human)"
- User: "it should automatically report to dev team instead of
  just asking CEO to do it"

Fix: when seo-builder finds a code-level blocker it can't fix via
DB, it delegates to the Dev Leader sibling workspace via A2A instead
of flagging for human. Only genuine human actions (Yelp email
verification, Google account-linked operations) stay in the human
bucket.

Also clarify marketing-leader/CLAUDE.md so the "DO NOT DELEGATE"
rule doesn't accidentally block this pattern — it's now explicit
that sibling handoff for scope mismatches is allowed (as opposed
to delegating down the hierarchy to spawn sub-agents, which stays
forbidden).
2026-04-15 17:47:27 -07:00
airenostars
36ed90d807 fix(transcript): validate workspace URL to prevent SSRF (#272)
`TranscriptHandler.Get` previously proxied `agent_card->>'url'` directly
to the outbound HTTP client with no validation. Since `agent_card` is
attacker-writable via /registry/register, a workspace-token holder
could point it at cloud metadata (169.254.169.254), link-local ranges,
or non-http schemes and pivot the platform container against internal
services (IMDS, Redis, Postgres, other containers on the Docker net).

Four required fixes per reviewer:

1. `validateWorkspaceURL(u *url.URL)` — runs before `httpClient.Do`:
   - scheme must be http/https (rejects file://, gopher://, ftp://)
   - cloud metadata hostname blocklist (GCP + Azure + plain "metadata")
   - IMDS IP blocklist (169.254.169.254)
   - IPv4/IPv6 link-local blocklist (169.254/16, fe80::/10, multicast)
   - IPv6 unique-local fd00::/8 blocklist
   - loopback + docker.internal still allowed for local dev

2. Query-param allowlist — `target.RawQuery = c.Request.URL.RawQuery`
   forwarded everything verbatim, letting a caller smuggle params the
   upstream transcript endpoint didn't intend to expose. Replaced with
   an allowlist of `since` and `limit`.

3. Sanitized error string — `fmt.Sprintf("workspace unreachable: %v", err)`
   leaked the actual internal host/IP via `net.OpError`. Now logs the
   real error server-side and returns a plain "workspace unreachable"
   to the caller.

4. 10 new regression test cases:
   - `TestTranscript_Rejects{CloudMetadataIP,NonHTTPScheme,MetadataHostname,LinkLocalIPv6}`
     exercise the handler end-to-end with each attack URL and assert
     400 before the HTTP client fires.
   - `TestValidateWorkspaceURL` table-drives the validator across
     localhost/public/docker-internal (allowed) + IMDS/GCP/Azure/file/
     gopher/link-local/multicast (rejected).
   - `TestTranscript_ProxyPropagatesAllowlistedQueryParams` asserts
     `secret=leak&cmd=rm` is stripped while `since=42&limit=7` pass
     through.

Also fixed a pre-existing test bug: `seedWorkspace` was issuing a real
SQL Exec against sqlmock with no expectation set, so the prior test
helpers silently failed in CI. Replaced with `expectWorkspaceURLLookup`
which programs the mock correctly. All 11 tests now pass.

Closes #272

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 17:46:55 -07:00
airenostars
f927e6272e feat(reno-stars): social-publish skill with 7 battle-tested helpers
Add a new `social-publish` skill under the Marketing Leader template
containing verbatim copies of 7 puppeteer-core helper scripts that reliably
publish video posts to Facebook, Instagram, X, LinkedIn, TikTok, YouTube,
and Google Business Profile. Each helper encapsulates hours of debugging
from the 2026-04-15 incident (Lexical editor mirror selection, FB Reel
Next-button disambiguation, post-publish upsell dismissal, TikTok
beforeunload race, GBP iframe scoping, etc).

Rewrite the existing social-media-poster / monitor / engage skills to
delegate publishing to these helpers instead of freestyling puppeteer
per run. Mirror the same delegation note into the social-media-specialist
skill copies so both the Marketing Leader and its specialist agent follow
the same rule.

Not implemented as a platform plugin: the helpers are dom-specific to
Reno Stars Chrome sessions (profile path, account IDs, hardcoded URLs)
and belong in org-template content rather than a generic platform
capability.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 17:34:13 -07:00
airenostars
51a21ea04f feat(browser-automation): bundle host-bridge CDP proxy + connect helper
The plugin now ships everything a user needs to wire Chrome on their
host to workspaces inside Docker:

- host-bridge/cdp-proxy.cjs — rewrites the Host header so Chrome accepts
  DevTools Protocol connections from container-originated traffic, and
  forwards both HTTP (tab list, screenshots) and WebSocket upgrades.

- host-bridge/install-host-bridge.sh — one-command install on macOS
  (launchd user agent) or Linux (systemd --user unit). `uninstall`
  subcommand cleans up. No root required.

- skills/browser-automation/lib/connect.js — the mandatory helper
  consumers already use; re-exported here so the plugin is self-contained.

- SKILL.md — documents the one-time host setup and the existing
  defaultViewport:null + disconnect-not-close rules. The 2026-04-15
  social-media-poster incident (3h debug chasing phantom "sessions
  expired" errors on an 800x600 viewport) is captured inline.

Smoke-tested on macOS: install script registered the agent, proxy
listens on 0.0.0.0:9223, and a live workspace container
(ws-bee4d521-3d3) successfully reached Chrome via
host.docker.internal:9223.

This replaces ad-hoc per-user CDP proxies and makes the plugin
usable by any Molecule operator, not just the Reno Stars org.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 17:29:46 -07:00
Hongming Wang
ea68b5a378 Merge pull request #290 from Molecule-AI/chore/ci-e2e-api-concurrency-group
chore(ci): serialize e2e-api across runs to prevent docker collision
2026-04-15 17:29:40 -07:00
Hongming Wang
7e18f8b15e Merge pull request #285 from Molecule-AI/fix/memory-tools-auth-headers
fix(memory-tools): #215-class — auth_headers on commit_memory + search_memory HTTP fallback
2026-04-15 17:29:24 -07:00
Hongming Wang
6898391dd0 fix(tests): update memory fakes for auth_headers kwarg + activity overwrite
The #215-class fix in memory.py (859a60e) adds headers=_headers to the
direct-httpx commit_memory + search_memory paths, but 9 existing tests
in test_memory.py had FakeAsyncClient.post/get signatures like
`async def post(self, url, json):` with no headers kwarg. Python
raised TypeError: unexpected keyword argument 'headers' on every call,
commit_memory caught it and returned {success: False}, tests failed.

Fixes applied:

1. Add `headers=None` to every FakeAsyncClient.post + .get signature
   across test_memory.py. Uses replace_all so all 9+ fakes match.

2. For tests that capture a single captured["url"]:
   - test_commit_memory_uses_awareness_client_when_configured
   - test_commit_memory_uses_platform_fallback_without_awareness
   - test_commit_memory_httpx_201_success
   filter to only capture /memories URLs. Without the filter, the
   subsequent _record_memory_activity fire-and-forget post to /activity
   overwrites captured["url"] and the assertion fails.

3. For test_commit_memory_promoted_packet_logs_skill_promotion: bump
   expected captured["calls"] from 3 to 4. Pre-fix, the memory_write
   /activity call (from _record_memory_activity #125) was silently
   dropped because the fake rejected headers=; post-fix it succeeds
   and lands in the captured list alongside the skill_promotion
   /activity and /registry/heartbeat calls. Also extend that test's
   fake to accept /registry/heartbeat (was raising AssertionError).

Total: 36/36 memory tests pass. Full workspace-template suite 1189/1189.

This is strictly test-infrastructure work — zero production code
changed. CI never caught the break because the Mac mini runner has
been stuck for ~4 hours (tick-33/34/35/36 reports).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 17:29:15 -07:00
rabbitblood
9923159f2e fix(memory-tools): #215-class — auth_headers on commit_memory + search_memory HTTP fallback
Context: platform now gates `GET /workspaces/:id/memories` and
`POST /workspaces/:id/memories` behind workspace auth (post-#166 /
#167 AdminAuth wave). The `builtin_tools.memory` tool had three HTTP
call sites:

  1. commit_memory POST fallback (line 121)        ← NO auth_headers
  2. search_memory GET fallback (line 269)         ← NO auth_headers
  3. activity-log helper POST (line 371)           ← HAS auth_headers

Path 3 was already fixed. Paths 1 + 2 silently 401 every call, but the
tool's error-handling path returns `{"success": False}` without surfacing
the auth failure to the agent. Result: the agent sees an empty memory
backlog on every call and assumes there's nothing to do.

## Discovered today

Technical Researcher is the first workspace opted in to the idle-loop
pilot from #216 (reflection-on-completion pattern). The pilot fires
every 10 min, the agent calls `search_memory "research-backlog:..."` as
the first step, gets back an empty result, writes "tr-idle clean" to
memory, and stops. Clean-idle outcome every tick, 9 consecutive ticks.

Looking at TR's activity_logs response bodies:

    "Memory auth has failed on every tick this session — skipping the call"
    "tr-idle — step 2 done. Memory unavailable (auth token missing..."
    "tr-idle 04:15 — clean (memory auth still down, 3rd consecutive tick)"

The AGENT knew the memory calls were failing. The platform 401 error
was surfacing in the tool response, but our instrumentation wasn't
counting it as a defect — we saw "tr-idle clean" writes and assumed
the pilot was working as designed. It was actually silently broken.

## Fix

Import `platform_auth.auth_headers` lazily (same pattern as the
activity-log path already uses), attach `headers=_auth()` to both
httpx call sites. Matches the #225 fix for the register call.

## Not in this PR

- awareness_client.py also makes HTTP calls to a separate AWARENESS_URL
  service (not the platform), which may or may not need the same fix
  depending on that service's auth posture. Out of scope for this PR.

- TR's specific token problem: TR's `/configs/.auth_token` file is
  empty because it was re-provisioned via `apply_template: true`
  (recovery path from the failed-volume incident) and Phase 30.1
  only mints a token on FIRST register per workspace. This fix
  doesn't help TR until TR gets a fresh token — tracked separately.

## Test plan

- [x] Python syntax check on memory.py passes
- [ ] CI: all memory-related tests should still pass (the new code
      paths only add header passing, no shape change)
- [ ] Real-world verification: after TR gets a fresh token, idle-loop
      pilot should produce a dispatch within 10 min (seeded backlog
      already in place from this session)

## Related
- #215 / #225 — register call auth_headers fix (same pattern)
- #216 — TR idle-loop pilot (couldn't measure until this lands)
- #166 / #167 — platform AdminAuth wave that surfaced this gap
2026-04-15 17:26:26 -07:00
Hongming Wang
3d0e093b11 chore(ci): serialize e2e-api across runs to prevent docker collision
Now that the Molecule-AI org has two self-hosted Apple-silicon runners
(`hongming-m1-mini` + `hongming-m1-mini-2`) servicing the same label set,
two CI runs could execute the e2e-api job concurrently. Each run starts
fixed-name docker containers (`molecule-ci-postgres`, `molecule-ci-redis`)
bound to host ports 15432/16379 — a collision means the second run fails
with "container name already in use" or "port already in use".

Adds a workflow-level `concurrency: e2e-api` group to the job so GitHub
Actions serializes e2e-api executions globally regardless of which runner
picks them up. `cancel-in-progress: false` ensures later runs queue
rather than cancelling the in-flight one (we want every PR's e2e check
to actually execute, not get skipped by a newer push).

Tradeoff: e2e-api is now effectively single-threaded across the whole
org. Measured duration is ~1-2 min per run, so the added serialization
latency is small relative to total CI wall time. All other jobs still
parallelize across both runners.
2026-04-15 17:06:41 -07:00
Hongming Wang
2c8049752e Merge pull request #289 from Molecule-AI/fix/code-review-plugin-on-engineers
feat(template): add molecule-skill-code-review to Frontend/Backend/DevOps Engineer (#280)
2026-04-15 16:55:47 -07:00
Hongming Wang
ed25fa11da feat(template): add molecule-skill-code-review to Frontend/Backend/DevOps Engineer (#280)
Closes #280. Self-review rubric now runs on the same workspaces that
raise PRs, not just on the reviewers. Dev Lead uses the same
16-criteria rubric in review, so catching issues pre-PR cuts the
review loop.

- Frontend Engineer: new plugins: [molecule-skill-code-review]
- Backend Engineer: plugins extended from [molecule-hitl] to
  [molecule-hitl, molecule-skill-code-review]
- DevOps Engineer: plugins extended from [molecule-hitl] to
  [molecule-hitl, molecule-skill-code-review]

The issue didn't explicitly call out DevOps Engineer but the reasoning
applies — DevOps Engineer writes Dockerfiles + CI workflows + infra
scripts that Dev Lead reviews with the same rubric. Including here
for consistency.

Verified all 5 reviewer/engineer roles' plugin lists via
walk-script:
  Dev Lead:        [code-review, llm-judge]
  Frontend Eng:    [code-review]                         ← NEW
  Backend Eng:     [hitl, code-review]                   ← NEW
  DevOps Eng:      [hitl, code-review]                   ← NEW
  Security Aud:    [code-review, cross-vendor, llm-judge,
                    security-scan, hitl]

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 16:55:24 -07:00