Commit Graph

842 Commits

Author SHA1 Message Date
Hongming Wang
be4e3bb485
Merge pull request #900 from Molecule-AI/fix/ci-go-mod-replace
fix(ci): remove go.mod replace /plugin — unblocks all CI
2026-04-17 18:17:11 -07:00
Hongming Wang
a891bf9b4b fix(ci): remove go.mod replace /plugin — add it at Docker build time only
The replace directive `=> /plugin` breaks CI builds where go build runs
natively (no /plugin directory). Move the replace to Dockerfile RUN so
it only applies during Docker builds where the plugin is COPYed.

Fixes: "replacement directory /plugin does not exist" on CI runner.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 18:08:53 -07:00
rabbitblood
f7706051aa fix: strip CRLF in entrypoint.sh at every container start
Windows Docker Desktop copies host files with CRLF even when
.gitattributes says eol=lf. The entrypoint now strips \r from all
hook .sh/.py files before dropping to agent user. Permanent fix for
the #507 CRLF regression that reappeared after every restart.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 18:06:04 -07:00
rabbitblood
43878d5b8f Merge branch 'main' of https://github.com/Molecule-AI/molecule-core 2026-04-17 17:52:18 -07:00
rabbitblood
92d80c0ee4 feat(telegram): poll for callback_query — CEO decision buttons work locally
Adds callback_query to AllowedUpdates in Telegram polling. When CEO
clicks Yes/No inline keyboard buttons:
1. Acknowledges press (removes loading spinner)
2. Updates message with 'CEO approved/rejected'
3. Routes 'CEO_DECISION: approve:xyz' as inbound to the agent

Only one workspace polls per bot token (Triage Operator) — other
workspaces with Telegram use outbound-only via direct API.

Fixed: duplicate pollers causing 'terminated by other getUpdates'
errors — removed PM/DevLead/ResearchLead Telegram channel rows
(they send outbound via direct Telegram API calls, not channel manager).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 17:52:10 -07:00
Hongming Wang
a7e0ac7912
Merge pull request #881 from Molecule-AI/fix/issue-838-memory-secret-redact
fix(security): SAFE-T1201 — redact secrets in commit_memory before persistence (#838)
2026-04-17 17:17:19 -07:00
Hongming Wang
006c8f49c8
Merge pull request #882 from Molecule-AI/fix/issue-819-hibernate-toctou
fix(platform): atomic hibernate — TOCTOU race in HibernateWorkspace (closes #819)
2026-04-17 17:17:16 -07:00
molecule-ai[bot]
bcd256946f
Merge pull request #890 from Molecule-AI/test/issue-790-crash-resume-integration
test(integration): crash-resume integration tests for Temporal checkpoints (#790)
2026-04-18 00:02:48 +00:00
molecule-ai[bot]
159c90e0f5
Merge pull request #798 from Molecule-AI/feat/issue-499-clean-3
feat(hermes): stacked system messages — persona + tools + reasoning policy (#499)
2026-04-18 00:02:29 +00:00
Molecule AI Backend Engineer
9d171bda7f feat(hermes): stacked system messages — persona + tools + reasoning policy (#499)
HermesA2AExecutor now supports sending system context as ordered, separate
role=system messages instead of a single concatenated string — the model
format recommended by NousResearch.

Changes:
- HermesA2AExecutor.__init__: new system_blocks kwarg (list[str|None]|None)
  stored as an independent copy; None blocks and empty strings silently skipped
- _build_messages(): when system_blocks is not None, emits each non-empty
  block as a separate {"role": "system"} entry in Hermes-recommended order
  (persona → tools context → reasoning policy); falls through to legacy
  system_prompt path when system_blocks is None (backward compatible)

Backward compatibility: existing callers that pass a single system_prompt
string continue to work identically — no changes required.

Tests (12 new, 47 total):
  - system_blocks stored as independent copy (mutation safe)
  - three-block stacked ordering preserved
  - empty / None blocks silently skipped
  - all-empty list → zero system messages
  - system_blocks overrides system_prompt when both provided
  - legacy system_prompt path unchanged
  - stacked blocks appear in the live API call kwargs

Closes #499

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 23:53:12 +00:00
rabbitblood
fe250b256b fix: restore plugin COPY in Dockerfile — permanently fixes token endpoint
The Dockerfile COPY for molecule-ai-plugin-github-app-auth was lost
during a rebase earlier this session. Without it, the platform binary
compiled without the TokenProvider interface implementation, causing
/admin/github-installation-token to return 'no token provider registered'.

This forced hourly rolling restarts to refresh GH_TOKEN (the env var
from provision time expires after ~60 min). Each restart also required
re-applying 6 manual patches and caused ~2 min of A2A downtime where
agents reported peers as 'unresponsive'.

With this fix, the gh-wrapper in each container auto-refreshes tokens
via the platform endpoint on every gh call. Zero restarts needed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 16:47:30 -07:00
Molecule AI Backend Engineer
4b8f4108cd fix(security): SAFE-T1201 — redact secrets in commit_memory before persistence
Adds `redactSecrets()` to the MemoriesHandler, scrubbing known credential
patterns before every INSERT into agent_memories, regardless of scope.

Closes #838. Satisfies SAFE-T1201 gate.

Patterns redacted (with `[REDACTED:<CLASS>]` replacement):
- Env-var assignments: `*_API_KEY=`, `*_TOKEN=`, `*_SECRET=`
- HTTP Bearer tokens
- sk-... prefixed keys (OpenAI / Anthropic format)
- ctx7_... tokens (context7)
- Base64 blobs ≥ 33 chars

The audit log SHA-256 hash now reflects the sanitised content (not the
raw input) so the forensic trail remains consistent with what was stored.

Tests added:
- TestRedactSecrets_CleanContent_PassesThrough
- TestRedactSecrets_APIKeyPattern_IsRedacted (API_KEY / TOKEN / SECRET)
- TestRedactSecrets_BearerToken_IsRedacted
- TestRedactSecrets_SKToken_IsRedacted
- TestRedactSecrets_Ctx7Token_IsRedacted
- TestRedactSecrets_Base64Blob_IsRedacted
- TestCommitMemory_SecretInContent_IsRedactedBeforeInsert

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 23:38:57 +00:00
rabbitblood
cbee9a7237 chore: extract molecule-medo plugin to standalone repo
molecule-medo now lives at Molecule-AI/molecule-ai-plugin-molecule-medo
(same pattern as all other plugins). Removed the gitignore exception
that kept it in the monorepo.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 16:11:50 -07:00
rabbitblood
595aa3681d chore: move spike/ → docs/spikes/ — keep explorations out of repo root
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 16:09:12 -07:00
molecule-ai[bot]
18cb498bca
Merge pull request #840 from Molecule-AI/feat/issue-800-opencode-mcp-bridge
feat(platform): opencode MCP bridge — remote A2A tools over HTTP (#800)
2026-04-17 22:15:38 +00:00
molecule-ai[bot]
9bce00d856
chore: sync opencode.md with main (conflict resolution post PR#842 merge)
PR#842 merged the docs/opencode.json to main with the correct MCP URL path.
PR#840 branch had an older version — sync to main's content to resolve conflict.
2026-04-17 22:14:59 +00:00
molecule-ai[bot]
00e3753f37
chore: sync opencode.json with main (conflict resolution post PR#842 merge)
PR#842 merged the docs/opencode.json to main with the correct MCP URL path.
PR#840 branch had an older version — sync to main's content to resolve conflict.
2026-04-17 22:14:57 +00:00
molecule-ai[bot]
c5a1318de8
fix(mcp): add TODO(#838) in toolCommitMemory + document X-Workspace-ID trust in toolDelegateTask
Security Auditor pre-merge conditions for PR#840:

C5: toolCommitMemory passes content directly to DB insert without secret
redaction. Gap is tracked to #838 (platform-wide _redactSecrets pass).
Adds inline TODO(#838) comment at the insert site so the gap is visible
in-code, not only in the issue tracker.

C6: toolDelegateTask sets X-Workspace-ID but no bearer token on the
outbound A2A call. The /workspaces/:id/a2a route is intentionally outside
WorkspaceAuth (by design in router.go). CanCommunicate is enforced before
the request is constructed, and callerID was authenticated by WorkspaceAuth
on the MCP bridge entry point. Documents this trust assumption at the call
site.
2026-04-17 22:13:55 +00:00
molecule-ai[bot]
d898b4f7bc
Merge pull request #842 from Molecule-AI/feat/issue-813-814-opencode-template
feat(opencode): org-template + integration guide for remote MCP auth (closes #813, closes #814)
2026-04-17 22:12:10 +00:00
molecule-ai[bot]
4f8837cc20
fix(opencode): update URL example in opencode.md + add WORKSPACE_ID env var
The inline JSON example still showed the bare ${MOLECULE_MCP_URL} without
the /workspaces/${WORKSPACE_ID}/mcp path. Updated to match opencode.json fix
in previous commit (9542348). Added WORKSPACE_ID to the env section.
2026-04-17 22:06:37 +00:00
molecule-ai[bot]
9542348ebf
fix(opencode): add full MCP path to opencode.json URL
Security Auditor FINDING-1: bare ${MOLECULE_MCP_URL} missing the router path.
Fix adds /workspaces/${WORKSPACE_ID}/mcp so opencode reaches MCPHandler.
Unblocks PR#842 merge.
2026-04-17 22:06:05 +00:00
rabbitblood
a6ba22d8ec fix(slack): tables as monospace blocks + ASCII dividers + strikethrough
Tables: Slack has no table syntax. Converter now detects markdown tables
and renders them as monospace code blocks with aligned columns.

Dividers: replaced unicode em-dash (caused encoding artifacts) with
plain ASCII dashes.

Strikethrough: ~~text~~ converts to ~text~ (Slack native).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 15:01:46 -07:00
rabbitblood
ea574723df fix(slack): restore FetchChannelHistory — was lost during branch juggling
The function was defined on a feature branch, referenced by manager.go
and slack_test.go, but never made it to main after the rebase. This
caused go build to fail with 'undefined: FetchChannelHistory', which
Docker masked by using a cached binary from the last successful build.

That cached binary had neither the mrkdwn blocks nor the Level 3
context injection — explaining why Slack messages showed raw markdown
despite the source having the converter.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 14:55:53 -07:00
rabbitblood
e3ada13adf fix(slack): use blocks API for mrkdwn rendering + restore Level 3
Slack's chat.postMessage renders the text field as plain text when
username override is used. Switching to blocks with type=mrkdwn
forces rich formatting (bold, links, code, dividers).

Also restores FetchWorkspaceChannelContext that was lost in rebase.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 14:47:07 -07:00
molecule-ai[bot]
c50d83ecf0
fix(canvas): a11y — keyboard access, role=alert, close label, ProvisioningTimeout (#830 #831 #832 #833)
Closes #830, Closes #831, Closes #832, Closes #833

QA-approved (verified via A2A relay — QA token-blocked). All 4 fixes confirmed against local source:
- #830: role=alert + aria-live=assertive on error elements (MemoryInspectorPanel)
- #831: TeamMemberChip role=button + tabIndex + aria-label + onKeyDown Enter/Space (WorkspaceNode)
- #832: aria-label='Close workspace panel' + aria-hidden on SVG (SidePanel)
- #833: ProvisioningTimeout uncommented and mounted in Canvas tree

731/731 tests pass, build clean, use client check clean.
2026-04-17 21:44:17 +00:00
rabbitblood
a3579d92b2 fix(slack): restore mrkdwn converter + FetchWorkspaceChannelContext after rebase
Both were lost during the PR #844 rebase — the converter was in the
source but the binary couldn't compile because FetchWorkspaceChannelContext
was missing from manager.go (interface mismatch). Previous deploys
silently used the cached old binary without the converter.

Also removed unused 'log' import that blocked compilation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 14:38:53 -07:00
Hongming Wang
4abf58826f
Merge pull request #851 from Molecule-AI/fix/slack-mrkdwn-formatting
fix(slack): convert Markdown → mrkdwn before posting
2026-04-17 14:27:17 -07:00
rabbitblood
1de7e5788a fix(slack): convert Markdown to mrkdwn before posting
Agents output standard Markdown (Claude Code default) but Slack uses
its own mrkdwn format. Without conversion:
  **bold** shows as literal **bold**
  ### heading shows as literal ###
  [text](url) shows as raw markdown link

Converter handles:
  **bold** → *bold* (Slack bold is single asterisk)
  ### heading → *heading* (bold text, no headings in Slack)
  [text](url) → <url|text> (Slack link format)
  --- → ——— (visual separator)
  `code` and ```blocks``` pass through unchanged

6 new tests: bold, heading, link, hr, code block, mixed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 14:26:41 -07:00
Hongming Wang
bb8de02059
Merge pull request #844 from Molecule-AI/feat/slack-bot-api-channels
feat(slack): Bot API adapter with per-agent identity + fix pgvector migration guard
2026-04-17 14:16:44 -07:00
rabbitblood
15600b41ae test(slack): add 12 unit tests for Slack adapter
Covers: message splitting (short/long/newline boundary), config
validation (bot_token/webhook/missing), FetchChannelHistory edge
cases (empty token/channel), adapter type/name.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 14:16:13 -07:00
rabbitblood
847d0b88e8 feat(slack): Level 3 — ambient cross-agent context from Slack channels
When a cron fires, the scheduler now fetches the last 10 messages from
the workspace's Slack channel via conversations.history and prepends them
to the cron prompt as '[Slack channel context — recent team messages]'.

This gives each agent ambient awareness of what peers are doing:
- Backend sees Frontend posted 'PR #840 ready for review' → can check
- Security Auditor sees Backend posted 'new endpoint added' → plans review
- PM sees all engineering activity → better synthesis in rollup

Implementation:
- slack.go: FetchChannelHistory() calls conversations.history, filters
  bot's own messages, returns last N as SlackHistoryMessage structs
- manager.go: FetchWorkspaceChannelContext() looks up the workspace's
  Slack config, fetches history, formats as readable context block
- scheduler.go: ChannelBroadcaster interface extended with
  FetchWorkspaceChannelContext; fireSchedule injects context before
  the cron prompt (prepended, not appended, so the agent sees team
  context BEFORE its task instructions)

Best-effort: if Slack API fails or workspace has no channels, the
prompt is unchanged. Truncated to 200 chars per message, 10 messages
max to keep prompt overhead bounded.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 14:15:51 -07:00
rabbitblood
95d0bc25a3 fix(slack): address code review — 6 critical + improvement fixes
Code review findings addressed:

Critical:
1. Bot echo loop: add bot_id + subtype='bot_message' check in ParseWebhook
   to prevent outbound auto-posts from triggering inbound → infinite loop
2. Connection leak: close resp.Body immediately after reading instead of
   defer inside loop (was holding N connections open for N chunks)
3. Cancelled context: auto-post goroutine now uses context.Background()
   with 30s timeout instead of inheriting fireCtx (which gets cancelled
   by deferred cancel() when fireSchedule returns)
4. Slug validation: regex ^[a-zA-Z0-9 _-]+$ rejects path traversal and
   special chars in [slug] routing

Improvements:
5. Shared HTTP client (slackHTTPClient) for connection pooling instead of
   per-request &http.Client{}
6. Rune-safe truncation in BroadcastToWorkspaceChannels for CJK/emoji
7. Log async HandleInbound errors instead of silently discarding
8. url_verification challenge properly returned (c.JSON with challenge)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 14:15:51 -07:00
rabbitblood
65bc6a8ca5 feat(channels): [slug] routing for inbound Slack messages
Humans type [backend] what's #800? in a shared #mol-engineering channel
and the message routes specifically to Backend Engineer's workspace.

Matching logic (case-insensitive):
  [pm]         → PM
  [backend]    → Backend Engineer
  [dev-lead]   → Dev Lead
  [security]   → Security Auditor (prefix match on 'security-auditor')

Unknown slugs return the available agent list for that channel so the
user knows what slugs are valid.

Messages without a [slug] prefix route to the first matching workspace
(backward compat with Level 2).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 14:15:51 -07:00
rabbitblood
3f161a41eb feat(slack): Level 1 auto-post + Level 2 inbound routing
Level 1 — Auto-post cron output to Slack:
- scheduler.go: captures A2A response body, extracts agent text via
  extractResponseSummary(), broadcasts to workspace's configured Slack
  channels on successful non-empty cron completions
- manager.go: adds BroadcastToWorkspaceChannels() — fans out to all
  enabled channels for a workspace (engineering+firehose for eng agents,
  research+firehose for research agents, etc.)
- main.go: wires scheduler → channel manager via SetChannels()
- Truncates output to 500 chars for Slack readability

Level 2 — Inbound Slack messages route to workspaces:
Already implemented by the existing webhook handler (POST /webhooks/slack)
+ the ParseWebhook method in slack.go which handles both Events API JSON
payloads and slash command form-encoded payloads. Needs Slack App Events
API URL configured to: https://<platform-host>/webhooks/slack

Also in this commit:
- slack.go: dual-mode adapter (bot_token + webhook fallback)
- 031 migration: pgvector guard wraps entire DO block

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 14:15:51 -07:00
rabbitblood
735aae6564 feat(slack): upgrade adapter to Bot API with per-agent identity + fix pgvector migration
Slack adapter: adds chat.postMessage mode alongside legacy webhooks.
When bot_token is configured, uses chat:write.customize for per-agent
display name + emoji on every message. Each of the 15 active agents
posts with a distinct identity (PM 💼, Backend ⚙️, etc.).

5 channels configured:
  #mol-engineering — PM, Dev Lead, Frontend, Backend, QA, Security, UIUX, Docs
  #mol-research    — Research Lead, Market Analyst, Tech Researcher, Competitive Intel
  #mol-ops         — DevOps, Triage, Offensive Security
  #mol-ceo-feed    — PM synthesized rollup (CEO-facing)
  #mol-firehose    — all agents (raw feed)

Tested live: 5 test messages across 4 channels, all ok=true.

pgvector migration: moved ALTER TABLE + CREATE INDEX inside the DO
block so the entire migration is skipped when pgvector extension is
unavailable (was crashing platform on restart — the guard caught
CREATE EXTENSION but execution continued to ALTER TABLE which used
the non-existent vector type).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 14:15:51 -07:00
Molecule AI Backend Engineer
34f5a3cbe2 fix(platform): atomic hibernate via UPDATE WHERE active_tasks=0 (#819)
Replaces the racy SELECT-then-Stop two-step in HibernateWorkspace with a
three-step atomic pattern that eliminates the TOCTOU window (SAFE-819):

  1. Atomic claim: single UPDATE WHERE id=$1
                   AND status IN ('online','degraded')
                   AND active_tasks = 0
     — rowsAffected=0 means another caller already claimed it or tasks
       arrived; we abort immediately without calling Stop.

  2. provisioner.Stop: safe because status='hibernating' blocks new task
     routing between step 1 and step 2 (no new task can be dispatched).

  3. Final UPDATE to 'hibernated': records the completed hibernation.

Also adds stopFnOverride func(ctx, id) to WorkspaceHandler (always nil in
production) so tests can count Stop calls without a running Docker daemon.

Tests added/updated (13 total across 2 files):
  - TestHibernateWorkspace_ActiveTasksNotHibernated
  - TestHibernateWorkspace_AlreadyHibernatingNotHibernated
  - TestHibernateWorkspace_SuccessPath
  - TestHibernateWorkspace_ConcurrentOnlyOneStop
  - TestHibernateWorkspace_DBErrorOnClaim
  - Updated 3 existing HibernateWorkspace tests + 1 HTTP handler test

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 20:52:20 +00:00
Hongming Wang
80b99ab219
Merge pull request #843 from Molecule-AI/fix/pgvector-migration-guard
fix(migrations): wrap entire pgvector migration in DO block — unblocks E2E
2026-04-17 13:31:49 -07:00
Hongming Wang
feb5ca5eab fix: correct RAISE NOTICE parameter — %% → % for Postgres syntax
The migration SQL is read as raw SQL (not through Go fmt.Sprintf),
so %% is two parameters, not an escaped percent. Postgres RAISE
uses single % for parameter substitution.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 13:20:58 -07:00
Hongming Wang
119b6225f9 fix(migrations): wrap entire pgvector migration in DO block guard
The ALTER TABLE and CREATE INDEX referenced vector(1536) outside the
exception-handling DO block, so when pgvector wasn't installed they
crashed the migration runner — blocking ALL E2E runs on main.

Fix: move all DDL inside the single DO block so the EXCEPTION handler
catches any pgvector-related failure and skips the entire migration.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 12:36:42 -07:00
Hongming Wang
e4acbf2fc5
Merge pull request #771 from Molecule-AI/feat/issue-765-mcp-eval-ci
feat(ci): add mcp-eval quality gate for @molecule-ai/mcp-server (#765)
2026-04-17 12:35:30 -07:00
molecule-ai[bot]
b39a653f12
chore(env): add MOLECULE_MCP_URL + MOLECULE_MCP_TOKEN for opencode integration (#813) 2026-04-17 19:26:50 +00:00
molecule-ai[bot]
7e707d08ee
docs(opencode): integration guide — token scoping, tools, SAFE-T1401 note (closes #814) 2026-04-17 19:26:36 +00:00
molecule-ai[bot]
abcc31f5b1
feat(opencode): add org-template opencode.json with header-based MCP auth (closes #813) 2026-04-17 19:26:10 +00:00
Molecule AI Backend Engineer
29cc845c5f feat(platform): opencode MCP bridge — remote A2A tools over HTTP (#800)
Implements sub-issues #809 (MCPHandler), #810 (tool filtering), #811
(per-token rate limiting), #813 (opencode.json), #814 (docs).

Routes (registered under wsAuth — bearer token binds to :id):
  GET  /workspaces/:id/mcp/stream  — SSE transport (backwards compat)
  POST /workspaces/:id/mcp         — Streamable HTTP transport (primary)

Security conditions from review (all mandatory):
  C1: WorkspaceAuth middleware rejects requests without valid bearer token
  C2: MCPRateLimiter (120 req/min/token, SHA-256 keyed) applied on both routes
  C3: commit_memory/recall_memory with scope=GLOBAL → permission error;
      send_message_to_user excluded unless MOLECULE_MCP_ALLOW_SEND_MESSAGE=true

Tools: list_peers, get_workspace_info, delegate_task, delegate_task_async,
check_task_status, send_message_to_user (opt-in), commit_memory, recall_memory.
All mirror workspace-template/a2a_mcp_server.py TOOLS list.

Also adds: org-templates/molecule-dev/opencode.json, docs/integrations/opencode.md,
.env.example entries for MOLECULE_MCP_ALLOW_SEND_MESSAGE and MOLECULE_MCP_URL.

Tests: 29 new tests (20 handler + 9 middleware). All passing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 19:25:22 +00:00
molecule-ai[bot]
e7a0c126ca
fix(canvas): color-code similarity badge by score tier (closes #783)
fix(canvas): color-code similarity badge by score tier (issue #783)
2026-04-17 19:24:44 +00:00
molecule-ai[bot]
45ed2fbe34
fix(gate-5): update test — zinc-400 italic + tilde assertion for low-score badge 2026-04-17 19:24:02 +00:00
molecule-ai[bot]
4bc57328bc
fix(gate-5): WCAG AA — zinc-400 italic for low-score badge per [uiux-agent] review 2026-04-17 19:23:51 +00:00
Molecule AI QA Engineer
a663c8de81 test(integration): crash-resume integration tests for Temporal checkpoints (#790)
Closes #790. Depends on feat/issue-583-1-checkpoint-persistence (PR #788).

Platform (Go) — checkpoints_integration_test.go (5 new tests):
1. ThreeStepPersistence: POST task_receive/llm_call/task_complete → GET returns
   all 3 in step_index DESC order with correct names and payloads.
2. CrashResume_HighestStepIsResumptionPoint: POST steps 0+1 only (crash before
   step 2) → GET shows step_index=1 as the resume point; task_complete absent.
3. UpsertIdempotency_LatestPayloadWins: POST same (wf_id, step_name) twice with
   different payloads → List returns only the second payload (ON CONFLICT DO UPDATE).
4. PostCascadeDelete_Returns404: simulate post ON-DELETE-CASCADE state (empty
   rows) → List returns 404 as expected after workspace deletion.
5. AuthGate_NoToken_Returns401: router-level test with WorkspaceAuth middleware;
   POST/GET/DELETE all return 401 without a bearer token (no DB calls made).

workspace-template — _save_checkpoint + 4 Python tests:
- Add async _save_checkpoint() to temporal_workflow.py: POST to the platform
  checkpoint endpoint after each activity stage; fully non-fatal (try/except
  inside the function, plus defence-in-depth try/except at every call site).
- 4 new pytest cases (test_temporal_workflow.py):
  - nonfatal_on_http_error: _save_checkpoint raises HTTPStatusError (500) →
    task_receive_activity still returns {"status":"received"}.
  - nonfatal_on_network_error: _save_checkpoint raises ConnectError →
    llm_call_activity still returns success LLMResult.
  - success_path: _save_checkpoint no-op → activity returns correctly;
    checkpoint called with correct args.
  - standalone_http_error_is_swallowed: real _save_checkpoint function swallows
    HTTP 500 from a mocked httpx.AsyncClient; returns None.

All 36 temporal workflow Python tests pass.
Go tests: Go binary not in this container; test file verified for syntax and
against the sqlmock patterns used throughout the handlers package.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 19:17:29 +00:00
molecule-ai[bot]
8116cd8aee
docs: tenant image upgrade strategies
docs: tenant image upgrade strategies
2026-04-17 19:16:30 +00:00
molecule-ai[bot]
6e9ef5f204
docs(security): SAFE-MCP audit report 2026-04-17 (closes #747)
docs(security): SAFE-MCP audit report 2026-04-17 (closes #747)
2026-04-17 19:06:42 +00:00