Commit Graph

3996 Commits

Author SHA1 Message Date
Hongming Wang
cfd5ec8d82
Merge pull request #2608 from Molecule-AI/ui/canvas-workspace-card-contrast
fix(canvas): WorkspaceNode + tier-config contrast in light theme
2026-05-03 17:32:22 +00:00
Hongming Wang
a4a32cded5 fix(canvas): WorkspaceNode + tier-config contrast in light theme
Cards on the canvas had multiple low-contrast surfaces in light mode:

WorkspaceNode.tsx (parent + TeamMemberChip) — same fixes both copies:
- "N sub" badge: hardcoded text-violet-300 + bg-violet-900/40 → semantic
  text-accent + bg-accent/15 + border-accent/40 (themes correctly).
- "REMOTE" pill: hardcoded violet/40 alpha → solid bg-violet-600 text-white
  (works on either surface with WCAG AA contrast).
- Runtime pill: drop /60 + /30 alpha modifiers, use solid surface-card +
  border-line tokens.
- Skill chips (online): text-good/80 + bg-emerald-950/30 (washed-out on
  warm-paper) → text-good + bg-good/15 + border-good/40 semantic.
- Skill chips (offline): text-ink-mid + bg-surface-card without alpha.
- Restart-to-apply banner: bg-sky-950/30 + text-sky-300/80 → bg-accent/10 +
  text-accent (sky-950 was nearly invisible on cream).
- Provisioning status text: text-sky-400 (poor on cream) → text-accent.
- "+N more" badges: text-ink-soft (3.5:1) → text-ink-mid (7:1).
- Active-tasks dot: bg-amber-400 + text-warm/80 → semantic bg-warm + text-warm.
- Degraded error preview: bg-amber-950/20 + text-warm/60 → bg-warm/10 +
  text-warm + border-warm/40.
- Eject icon hover: hover:text-sky-400 → hover:text-accent.
- Role text: text-ink-soft → text-ink-mid.

design-tokens.ts:
- TIER_CONFIG was dark-only: T2 (text-sky-400 + bg-sky-950/50), T3
  (text-violet-400 + bg-violet-950/50), T4 (text-warm + bg-amber-950/50).
  Migrated to solid bg + white text patterns: T2=accent, T3=violet-600,
  T4=warm. T1 stays neutral (surface-card + ink-mid). All four pass WCAG
  AA on either theme.

No globals.css changes; uses existing semantic tokens.
2026-05-03 10:28:49 -07:00
Hongming Wang
257079c7a2
Merge pull request #2605 from Molecule-AI/fix/2485-chat-history-followups
fix(chat-history): correct docstring inversion + pin empty-history JSON shape (#2485)
2026-05-03 17:24:42 +00:00
Hongming Wang
0567502316
Merge pull request #2607 from Molecule-AI/auto-sync/main-7cba0477
chore: sync main → staging (auto, ff to 7cba0477)
2026-05-03 17:23:35 +00:00
molecule-ai[bot]
7cba0477cc
Merge pull request #2606 from Molecule-AI/staging
staging → main: auto-promote 4e72f1d
2026-05-03 10:18:56 -07:00
Hongming Wang
ff3dcd37f6 fix(chat-history): correct docstring inversion + pin empty-history JSON shape (#2485)
Two follow-ups from the multi-axis review of #2474:

1. **Docstring inversion** in tool_chat_history. The doc said
   '(source_id=peer)' meant 'this workspace is the sender' — actually
   it means the *peer* is the sender (source_id is where the activity
   came FROM). Reframed to 'where the peer is either the sender or
   the recipient' to match the underlying SQL semantics.

2. **Empty-history test**. TestChatHistory had 10 tests but no
   200+[] happy-path pin. Added test_empty_history_returns_empty_json_list
   asserting result == '[]' on exact-equality (per assert-exact
   memory — substring '[]' would match envelope shapes too).

Both changes are pure docs+tests — no behaviour change.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 10:09:15 -07:00
Hongming Wang
4e72f1d1db
Merge pull request #2604 from Molecule-AI/ui/canvas-chat-contrast
fix(canvas): chat bubble + sub-tab contrast in light theme
2026-05-03 17:00:54 +00:00
Hongming Wang
e22f7969f8
Merge pull request #2603 from Molecule-AI/auto-sync/main-46c8c1de
chore: sync main → staging (auto, ff to 46c8c1de)
2026-05-03 17:00:37 +00:00
Hongming Wang
3d145da99d fix(canvas): chat bubble + sub-tab contrast in light theme
Chat bubble fixes (canvas/src/components/tabs/ChatTab.tsx):
- User bubble: bg-accent-strong/30 + text-blue-100 → bg-accent + text-white
  (translucent dark-blue overlay on warm-paper surface read as pale lavender
  with near-invisible light-blue text — a real WCAG AA failure on the
  highest-traffic surface in canvas).
- System/error bubble: bg-red-900/30 + text-red-200 → bg-bad/10 + text-bad,
  using semantic tokens so dark-mode adapts automatically.
- Agent bubble: drop /80 + /30 opacity modifiers; solid bg-surface-card +
  text-ink + border-line gives consistent contrast in both themes.
- prose-invert was unconditional, so markdown text on agent/system bubbles
  rendered as light text on a light surface in light mode. Make it apply
  only on the user bubble (the only dark surface in this component).
- Timestamp: text-ink-soft is too pale on warm-paper; use text-ink-mid for
  agent/system, white/70 for user (visible on the now-solid blue bg).

Sub-tab bar fixes (canvas/src/components/SidePanel.tsx):
- Right-edge fade was hardcoded `from-zinc-950` — that paints a dark vertical
  strip on the right edge of the tab bar in light mode. Switch to
  `from-surface` so the gradient blends into whichever theme is active.
- Inactive tab text: text-ink-soft (~3.5:1 on warm-paper) → text-ink-mid
  (~7:1). Active tab background: drop the /40 opacity so the selection is
  unambiguous on either surface.

No semantic-token additions; all changes use existing warm-paper tokens
that already work in both themes.
2026-05-03 09:58:18 -07:00
molecule-ai[bot]
46c8c1de23
Merge pull request #2602 from Molecule-AI/staging
staging → main: auto-promote 6d38b96
2026-05-03 16:49:40 +00:00
Hongming Wang
6d38b96043
Merge pull request #2601 from Molecule-AI/fix/2483-negative-cache-branch-tests
test(envelope-enrichment): pin negative-cache for non-JSON 200 + non-dict JSON 200 (#2483)
2026-05-03 16:37:30 +00:00
Hongming Wang
270a95aa67 test(envelope-enrichment): pin negative-cache for non-JSON 200 + non-dict JSON 200 (#2483)
The two missing branch tests called out by the multi-axis review of #2471.

a2a_client.enrich_peer_metadata handles two failure shapes (lines 105-112)
that the existing 12 envelope-enrichment tests don't exercise:

  1. HTTP 200, response.json() raises (non-JSON body)
  2. HTTP 200, valid JSON, but body is list/string/number not dict

Both paths land at the negative-cache write, but no test verified the
discriminator. Pin both with the same call_count == 1 assertion shape
the 5xx + network-exception tests already use.

Verified: temporarily removing the negative-cache write in either
branch makes the corresponding test fail with call_count == 2 — the
assertion correctly discriminates the contract from a fall-through.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 09:35:21 -07:00
Hongming Wang
6431bdc631
Merge pull request #2600 from Molecule-AI/auto-sync/main-72b6be82
chore: sync main → staging (auto, ff to 72b6be82)
2026-05-03 16:23:16 +00:00
molecule-ai[bot]
72b6be82b0
Merge pull request #2599 from Molecule-AI/staging
staging → main: auto-promote b425995
2026-05-03 09:18:48 -07:00
Hongming Wang
b42599585e
Merge pull request #2598 from Molecule-AI/fix/auto-promote-skip-empty-tree
fix(auto-promote): skip empty-tree promotes to break perpetual cycle
2026-05-03 15:59:05 +00:00
Hongming Wang
06bfed2e35
Merge pull request #2597 from Molecule-AI/auto-sync/main-d1eab79d
chore: sync main → staging (auto, ff to d1eab79d)
2026-05-03 15:57:47 +00:00
Hongming Wang
80b38900de fix(auto-promote): skip empty-tree promotes to break perpetual cycle
The auto-promote ↔ auto-sync chain has been generating empty PRs
indefinitely since the staging merge_queue ruleset uses MERGE
strategy:

1. Auto-promote merges PR via queue → main = merge commit M2 not in staging
2. Auto-sync opens sync-back PR. Workflow's local `git merge --ff-only`
   succeeds (PR title even says "ff to ..."), but the queue lands the
   PR via MERGE → staging = merge commit S2 not in main
3. Auto-promote sees staging ahead by 1 → opens new promote PR. Tree
   diff vs main = 0 (S2's tree == main's tree). But the gate logic
   only checks "all required workflows green", not "actual code to
   ship" → opens an empty promote PR
4. ... repeat indefinitely

Each round costs ~30-40 min wallclock, ~2 manual approvals (the queue
requires 1 review and the bot can't self-approve without admin
bypass), and one full CodeQL Go run (~15 min).

Observed today (2026-05-03) across PRs #2592#2594#2595#2596#2597 — 5 PRs, ~3 hours, all empty content.

Fix: before opening the promote PR, check that staging's tree
actually differs from main's tree. If they're identical (the
empty-merge-commit cycle), skip cleanly and let the cycle terminate.

Implementation:
- New step `Skip if staging tree == main tree` runs before the
  existing gate check.
- `git diff --quiet origin/main $HEAD_SHA` exits 0 iff trees match.
- On match: emits a step summary explaining the skip + sets
  `skip=true`; subsequent gate-check + promote steps are gated on
  `skip != 'true'` so they short-circuit.
- Fail-open: if `git fetch` errors, fall through to gate check
  (preserve existing behavior). Only skip when diff is DEFINITIVELY
  empty.

Long-term, the cleaner fix is to switch the merge_queue ruleset's
merge_method away from MERGE so FF-able PRs land cleanly without a
new commit — but that's a broader change affecting every staging
PR's commit shape. This guard is the surgical one-step break.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 08:56:44 -07:00
molecule-ai[bot]
d1eab79d28
Merge pull request #2596 from Molecule-AI/staging
staging → main: auto-promote 824a2a7
2026-05-03 15:50:12 +00:00
Hongming Wang
824a2a7657
Merge pull request #2595 from Molecule-AI/auto-sync/main-876d6ec8
chore: sync main → staging (auto, ff to 876d6ec8)
2026-05-03 15:38:22 +00:00
molecule-ai[bot]
876d6ec8c9
Merge pull request #2594 from Molecule-AI/staging
staging → main: auto-promote 63e3d38
2026-05-03 08:33:51 -07:00
Hongming Wang
63e3d385d6
Merge pull request #2592 from Molecule-AI/auto-sync/main-2e78812f
chore: sync main → staging (auto, ff to 2e78812f)
2026-05-03 15:15:01 +00:00
molecule-ai[bot]
2e78812ff9
Merge pull request #2591 from Molecule-AI/staging
staging → main: auto-promote 19cc833
2026-05-03 15:04:00 +00:00
Hongming Wang
9664d66e4b
Merge branch 'main' into staging 2026-05-03 07:48:31 -07:00
Hongming Wang
19cc83313a
Merge pull request #2589 from Molecule-AI/fix/retarget-skip-staging-head
fix(retarget): skip PRs whose head is staging (auto-promote PRs)
2026-05-03 14:36:44 +00:00
molecule-ai[bot]
097d513b65
Merge pull request #2588 from Molecule-AI/staging
staging → main: auto-promote c45aa8d
2026-05-03 07:35:05 -07:00
Hongming Wang
2b3f44c3c8 fix(retarget): skip PRs whose head is staging (auto-promote PRs)
The retarget-main-to-staging workflow tries to PATCH base=staging on
every bot-authored PR opened against main. Auto-promote staging→main
PRs have head=staging, base=main — retargeting them sets head AND
base to staging, which GitHub rejects with HTTP 422 "no new commits
between base 'staging' and head 'staging'".

This started surfacing on PR #2588 (2026-05-03 14:30) once #2586
switched the auto-promote workflow to an App token. Before #2586
the auto-promote PR was authored by github-actions[bot], which the
retarget filter happened to skip; now it's molecule-ai[bot], which
passes the bot filter and triggers the broken retarget attempt.

Add a head-ref != 'staging' guard so auto-promote PRs short-circuit
before the PATCH. The existing 422 "duplicate base" detector is
left alone — it covers a different operational case.
2026-05-03 07:34:24 -07:00
Hongming Wang
c45aa8d7ee
Merge pull request #2587 from Molecule-AI/auto-sync/main-b4e45374
chore: sync main → staging (auto, ff to b4e45374)
2026-05-03 14:19:28 +00:00
Hongming Wang
b4e45374bf
Merge pull request #2586 from Molecule-AI/fix/auto-promote-app-token
fix(auto-promote): use App token for auto-merge to fire downstream cascade (#2357)
2026-05-03 07:15:31 -07:00
Hongming Wang
f2d69f0088
Merge pull request #2585 from Molecule-AI/fix/canvas-loading-state-aria
fix(canvas): add role=status + aria-live to remaining loading states
2026-05-03 14:14:33 +00:00
Hongming Wang
bc11ed8a2b fix(auto-promote): use App token for auto-merge to fire downstream cascade (#2357)
GITHUB_TOKEN-initiated merges suppress the downstream `push` event on
main per GitHub's documented limitation:
  https://docs.github.com/en/actions/using-workflows/triggering-a-workflow#triggering-a-workflow-from-a-workflow

Result before this fix: every staging→main promote landed silently —
publish-workspace-server-image, canary-verify, and redeploy-tenants-on-main
all stayed dark. The polling tail was the SOLE cascade trigger; if it
ever 30-min-timed-out the chain dead-locked invisibly.

Symptom (from the issue body, 2026-04-30):

| Time     | Event                                            | Triggered? |
|----------|--------------------------------------------------|-----------|
| 05:48:04 | Promote PR #2352 merged (c140ad28)               | No fired  |
| 06:07:29 | Promote PR #2356 merged (5973c9bd)               | No fired  |

Fix: mint the molecule-ai App token BEFORE the promote-PR step and
hand it to the auto-merge call. App-token-initiated merges DO trigger
downstream workflow_run cascades.

The polling tail stays as defense-in-depth (with comments updated):
once we've observed >=10 successful natural cascades it can be dropped.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 07:13:26 -07:00
Hongming Wang
e2328abedc fix(canvas): add role=status + aria-live to remaining loading states
Three loading-state divs were missing the role/aria pattern that
TemplatePalette.tsx and EmptyState.tsx already follow. Screen readers
get no signal that the page is waiting:

- canvas/src/app/page.tsx — full-screen "Loading canvas..." while
  the websocket hydrates. First paint of the entire app.
- canvas/src/components/settings/TokensTab.tsx — "Loading tokens..."
- canvas/src/components/settings/OrgTokensTab.tsx — "Loading keys..."

Add role="status" + aria-live="polite" to the wrapping div so
assistive tech announces the wait and the eventual transition.
Visual rendering unchanged.
2026-05-03 07:11:48 -07:00
github-actions[bot]
bdad75ae3e
Merge pull request #2582 from Molecule-AI/staging
staging → main: auto-promote 90ba2cd
2026-05-03 07:06:58 -07:00
Hongming Wang
90ba2cd4df
Merge pull request #2580 from Molecule-AI/auto-sync/main-b002247f
chore: sync main → staging (auto, ff to b002247f)
2026-05-03 13:54:03 +00:00
Hongming Wang
b002247f12
Merge pull request #2576 from Molecule-AI/staging
staging → main: auto-promote effbcd7
2026-05-03 06:44:31 -07:00
Hongming Wang
03bcce3eb3
Merge pull request #2574 from Molecule-AI/auto-sync/main-55d85147
chore: sync main → staging (auto, ff to 55d85147)
2026-05-03 13:18:34 +00:00
Hongming Wang
c74e71d604
Merge branch 'staging' into auto-sync/main-55d85147 2026-05-03 06:07:20 -07:00
Hongming Wang
d7f88674d8
Merge pull request #2577 from Molecule-AI/fix/canvas-tier-legend-t3-t4-contract
fix(canvas): align tier text contracts with 4-tier reality (T1/T2/T3/T4)
2026-05-03 12:58:52 +00:00
Hongming Wang
7abb94dab8 fix(canvas): align tier text contracts with 4-tier reality (T1/T2/T3/T4)
The tier system in CreateWorkspaceDialog and design-tokens has been
T1 Sandboxed / T2 Standard / T3 Privileged / T4 Full Access, but two
chrome surfaces still showed the older 3-tier mapping with T3 as
"Full Access":

- Legend (bottom-left chrome on every canvas page) listed only T1/T2/T3
  and called T3 "Full Access". On a SaaS tenant the actual workspace
  badges render T4 (in amber/warm) — there was no T4 entry in the
  legend at all, so the user sees an undocumented orange badge.

- ConfigTab tier dropdown (per-workspace settings → Sandboxing) had no
  T4 option at all and called T3 "Full Access". So an existing T4
  workspace would show "T3 — Full Access" as the selected option,
  silently downgrading the displayed tier on the settings panel.

- tenant.ts isSaaSTenant() doc comment claimed SaaS workspaces are
  "inherently T3 Full Access" — wrong on both the number and the lock
  rationale (SaaS hides T1/T2/T3, not just T1/T2).

Fix:
- Legend now imports TIER_CONFIG and renders all four tiers
  (Sandboxed/Standard/Privileged/Full Access) using the same color
  swatches as the badges on workspace cards. Eliminates the previous
  drift where Legend's hardcoded sky/violet/warm chips didn't match
  the gray/sky/violet/amber actually rendered on nodes.
- ConfigTab adds the missing T4 — Full Access option and renames T3
  to Privileged.
- tenant.ts comment updated to match the picker's actual hide list.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 05:56:18 -07:00
Hongming Wang
effbcd737b
Merge pull request #2575 from Molecule-AI/fix/cascade-include-all-active-templates
fix(publish-runtime): re-add 5 templates wrongly removed from cascade — fixes #2566
2026-05-03 12:45:48 +00:00
Hongming Wang
6eb79adfd5 manifest: re-add 5 workspace templates pruned by #2536
The cascade-list-vs-manifest drift gate (PR #2556's behavior-based
test) caught my previous-commit cascade additions as 'extra-in-cascade'.
Manifest is the source of truth — restoring there.

All 5 templates have successful publish-image runs in the past 24h
(verified before the cascade fix), and continuous-synth-e2e defaults
to langgraph as its primary canary. None deprecated.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 05:43:07 -07:00
Hongming Wang
8f48a38550 fix(publish-runtime): re-add 5 templates wrongly removed from cascade (#2566)
The PR #2536 cascade prune ('deprecated, no shipping images') was
empirically wrong. Re-confirmed 2026-05-03:

- continuous-synth-e2e.yml defaults to langgraph as its primary canary
- All 5 'deprecated' templates have successful publish-image runs in
  the past 24h: langgraph, crewai, autogen, deepagents, gemini-cli

Symptom this fixes — issue #2566 (priority-high, failing 36+h):

  Synthetic E2E (staging): langgraph adapter A2A failure
  'Received Message object in task mode' — failing for >36h

Today at 11:06 commit e1628c4 fixed the underlying a2a-sdk strict-mode
issue in workspace/a2a_executor.py. publish-runtime fired at 11:13 and
cascaded — but only to claude-code, hermes, openclaw, codex. langgraph
was excluded by the prune, so its image stayed on the broken runtime
and the synth E2E (which defaults to langgraph) kept failing despite
the fix being live in PyPI.

After this lands + the next runtime publish fires, langgraph image
re-bakes with the fix and synth-E2E goes green.

Test plan:

- [x] yaml-validate the workflow
- [ ] After merge, watch publish-runtime cascade to all 9 templates
- [ ] Confirm langgraph publish-image fires + succeeds
- [ ] Confirm next continuous-synth-e2e run goes green

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 05:41:53 -07:00
github-actions[bot]
55d85147f7
Merge pull request #2573 from Molecule-AI/staging
staging → main: auto-promote dc6425f
2026-05-03 05:34:23 -07:00
github-actions[bot]
f7e8f98cf7
Merge pull request #2570 from Molecule-AI/staging
staging → main: auto-promote 173e22e
2026-05-03 12:22:52 +00:00
Hongming Wang
dc6425fe39
Merge pull request #2571 from Molecule-AI/fix/synth-e2e-model-slug-by-runtime
fix(synth-e2e): branch MODEL_SLUG by runtime so langgraph gets colon-form
2026-05-03 12:22:19 +00:00
Hongming Wang
cbc69f5e7e fix(synth-e2e): branch MODEL_SLUG by runtime so langgraph gets colon-form
The original script hardcoded `MODEL_SLUG="openai/gpt-4o"` (slash) and
claimed "non-hermes runtimes ignore the prefix" — wrong for langgraph,
which delegates model resolution to langchain's `init_chat_model`. That
function requires `<provider>:<model>` (colon) and treats slash-form as
OpenRouter routing, falling through without auth even when
OPENAI_API_KEY is set.

Surfaced 2026-05-03 after the a2a-sdk v1 contract bugs (PR
#2558+#2563+#2567) cleared the masking layers — synth-E2E firing
2026-05-03T12:14 returned a properly-shaped task with state=failed +
"Could not resolve authentication method" inside the agent body.

continuous-synth-e2e.yml defaults E2E_RUNTIME=langgraph for the cron,
so every firing hit this. Hermes still gets the slash-form it
needs; claude-code uses the entry-id pattern.

Adds E2E_MODEL_SLUG override for operator-dispatched runs that want
to pin a specific slug.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 05:17:55 -07:00
Hongming Wang
c71f641b12
Merge pull request #2569 from Molecule-AI/fix/redeploy-canary-default
ci(redeploy): fix stale canary_slug default 'hongmingwang' → 'hongming'
2026-05-03 12:08:26 +00:00
Hongming Wang
173e22e091
Merge pull request #2568 from Molecule-AI/auto-sync/main-c0838d63
chore: sync main → staging (auto, ff to c0838d63)
2026-05-03 12:07:29 +00:00
Hongming Wang
60a516bc8d ci(redeploy): fix stale canary_slug default 'hongmingwang' → 'hongming'
The workflow_dispatch input default and the workflow_run env fallback
both pointed at 'hongmingwang', which doesn't match any current prod
tenant (slugs are: hongming, chloe-dong, reno-stars). CP silently
skipped the missing canary and put every tenant in batch-1 in parallel,
defeating the canary-first soak gate that exists to catch image-boot
regressions before they hit the whole fleet.

Concrete example from today's c0838d6 redeploy at 11:53Z (run 25278434388):
the dispatched body was `{"target_tag":"staging-c0838d6","canary_slug":"hongmingwang",...}`
and the CP response showed all 3 tenants in `"phase":"batch-1"` — no
soak, no canary. The deploy happened to be safe, but a broken image
would have hit hongming + chloe-dong + reno-stars simultaneously.

Fixed in three places: the runtime ordering comment, the
workflow_dispatch default, and the env fallback used by the
workflow_run trigger. Comment documents the rationale so the next
slug rename doesn't silently regress this again.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 05:06:01 -07:00
Hongming Wang
c0838d637e
Merge pull request #2562 from Molecule-AI/staging
staging → main: auto-promote bb63e60
2026-05-03 04:49:36 -07:00
Hongming Wang
493ab2566e
Merge pull request #2567 from Molecule-AI/fix/synth-e2e-openai-key
ci(synth-e2e): wire MOLECULE_STAGING_OPENAI_KEY into provisioned tenant
2026-05-03 11:45:17 +00:00