From 392394b4c9e886963a1bb15b9f50c39fe07b9aa4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=89=E9=91=AB=E5=8D=87?= Date: Wed, 17 Jun 2026 17:19:29 +0800 Subject: [PATCH 1/2] =?UTF-8?q?fix(db):=20=E4=BF=AE=E5=A4=8D=20alembic=20?= =?UTF-8?q?=E5=A4=9A=20head=20=E5=AF=BC=E8=87=B4=E5=90=8E=E7=AB=AF?= =?UTF-8?q?=E5=90=AF=E5=8A=A8=E5=A4=B1=E8=B4=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 迁移图存在两处问题导致 `alembic upgrade head`(网关启动时执行)报 "Multiple head revisions are present": 1. 20260612_01 分叉出 20260615_01 与 20260616_03 两个 head 未合并; 2. 20260611_01、20260616_02 各被两个文件重复占用,污染 DAG 产生幽灵 head。 处理: - roundtable_draft_task_id 20260611_01 -> 20260611_02(接在 square_id 之后), 20260612_01 父节点改指 20260611_02; - skill_favorites 20260616_02 -> 20260616_04(接在 knowledge_folders 之后), 20260616_03 父节点改指 20260616_04; - 新增 no-op 合并迁移 20260617_01 合并 (20260615_01, 20260616_03)。 保留 20260611_01 / 20260616_02 原 id 不变,仅重命名重复副本,已 stamp 的库不受影响; 所有迁移均有 _has_table/_has_column 守卫,全链路在已建表的库上重跑为安全 no-op。 `alembic heads` 现仅余单一 head 20260617_01,无重复告警。 Co-Authored-By: Claude Opus 4.8 (1M context) --- offline-backend-20260512/backend/CLAUDE.md | 2 +- ...> 20260611_02_roundtable_draft_task_id.py} | 14 ++++++--- ...2_01_roundtable_jobs_orchestration_plan.py | 4 +-- .../versions/20260616_03_skill_always_on.py | 4 +-- ...ites.py => 20260616_04_skill_favorites.py} | 14 ++++++--- ...ge_chain_seat_and_skill_always_on_heads.py | 30 +++++++++++++++++++ 6 files changed, 55 insertions(+), 13 deletions(-) rename offline-backend-20260512/backend/packages/harness/deerflow/persistence/migrations/versions/{20260611_01_roundtable_draft_task_id.py => 20260611_02_roundtable_draft_task_id.py} (77%) rename offline-backend-20260512/backend/packages/harness/deerflow/persistence/migrations/versions/{20260616_02_skill_favorites.py => 20260616_04_skill_favorites.py} (81%) create mode 100644 offline-backend-20260512/backend/packages/harness/deerflow/persistence/migrations/versions/20260617_01_merge_chain_seat_and_skill_always_on_heads.py diff --git a/offline-backend-20260512/backend/CLAUDE.md b/offline-backend-20260512/backend/CLAUDE.md index d75ca744..712c7339 100644 --- a/offline-backend-20260512/backend/CLAUDE.md +++ b/offline-backend-20260512/backend/CLAUDE.md @@ -368,7 +368,7 @@ shown to users ARE the **recommended questions** (`recommended_questions` table) types into the existing "我的" tables, idempotently: - agents → `agent_favorites` rows with `origin='position'` - **skills → `skill_favorites` rows with `origin='position'`** (table - `SkillFavoriteRow`, migration `20260616_02`, auto-created by `create_all`) + `SkillFavoriteRow`, migration `20260616_04`, auto-created by `create_all`) - scheduled tasks → `scheduled_task_subscriptions` rows with `origin='position'` Skills behave **exactly like agents**: a position only *adds* its tagged skills diff --git a/offline-backend-20260512/backend/packages/harness/deerflow/persistence/migrations/versions/20260611_01_roundtable_draft_task_id.py b/offline-backend-20260512/backend/packages/harness/deerflow/persistence/migrations/versions/20260611_02_roundtable_draft_task_id.py similarity index 77% rename from offline-backend-20260512/backend/packages/harness/deerflow/persistence/migrations/versions/20260611_01_roundtable_draft_task_id.py rename to offline-backend-20260512/backend/packages/harness/deerflow/persistence/migrations/versions/20260611_02_roundtable_draft_task_id.py index 076a329b..c80317d5 100644 --- a/offline-backend-20260512/backend/packages/harness/deerflow/persistence/migrations/versions/20260611_01_roundtable_draft_task_id.py +++ b/offline-backend-20260512/backend/packages/harness/deerflow/persistence/migrations/versions/20260611_02_roundtable_draft_task_id.py @@ -7,8 +7,14 @@ draft is keyed/shared by ``task_id`` and is no longer partitioned by ``user_id`` ``create_all`` adds the column for brand-new DBs; this revision patches DBs that already have the table. -Revision ID: 20260611_01 -Revises: 20260609_02 +Note: this migration originally shared the revision id ``20260611_01`` with +``20260611_01_agents_skills_square_id`` (both branched off ``20260609_02``), +which corrupted the alembic DAG ("present more than once" + phantom heads). It +was renumbered to ``20260611_02`` and re-chained after that revision so the +history is linear and single-headed. + +Revision ID: 20260611_02 +Revises: 20260611_01 Create Date: 2026-06-11 """ @@ -19,8 +25,8 @@ from alembic import op from sqlalchemy import inspect -revision = "20260611_01" -down_revision = "20260609_02" +revision = "20260611_02" +down_revision = "20260611_01" branch_labels = None depends_on = None diff --git a/offline-backend-20260512/backend/packages/harness/deerflow/persistence/migrations/versions/20260612_01_roundtable_jobs_orchestration_plan.py b/offline-backend-20260512/backend/packages/harness/deerflow/persistence/migrations/versions/20260612_01_roundtable_jobs_orchestration_plan.py index f7a2cf90..a9b700fd 100644 --- a/offline-backend-20260512/backend/packages/harness/deerflow/persistence/migrations/versions/20260612_01_roundtable_jobs_orchestration_plan.py +++ b/offline-backend-20260512/backend/packages/harness/deerflow/persistence/migrations/versions/20260612_01_roundtable_jobs_orchestration_plan.py @@ -6,7 +6,7 @@ LONGTEXT(计划 JSON 可超 64 KB)。 Revision ID: 20260612_01 -Revises: 20260611_01 +Revises: 20260611_02 Create Date: 2026-06-12 """ @@ -19,7 +19,7 @@ from sqlalchemy.dialects.mysql import LONGTEXT revision = "20260612_01" -down_revision = "20260611_01" +down_revision = "20260611_02" branch_labels = None depends_on = None diff --git a/offline-backend-20260512/backend/packages/harness/deerflow/persistence/migrations/versions/20260616_03_skill_always_on.py b/offline-backend-20260512/backend/packages/harness/deerflow/persistence/migrations/versions/20260616_03_skill_always_on.py index 68d458bd..57c0b0df 100644 --- a/offline-backend-20260512/backend/packages/harness/deerflow/persistence/migrations/versions/20260616_03_skill_always_on.py +++ b/offline-backend-20260512/backend/packages/harness/deerflow/persistence/migrations/versions/20260616_03_skill_always_on.py @@ -1,7 +1,7 @@ """Add always_on (常驻/免压缩) column to skills table. Revision ID: 20260616_03 -Revises: 20260616_02 +Revises: 20260616_04 Create Date: 2026-06-16 """ @@ -13,7 +13,7 @@ from sqlalchemy import inspect revision = "20260616_03" -down_revision = "20260616_02" +down_revision = "20260616_04" branch_labels = None depends_on = None diff --git a/offline-backend-20260512/backend/packages/harness/deerflow/persistence/migrations/versions/20260616_02_skill_favorites.py b/offline-backend-20260512/backend/packages/harness/deerflow/persistence/migrations/versions/20260616_04_skill_favorites.py similarity index 81% rename from offline-backend-20260512/backend/packages/harness/deerflow/persistence/migrations/versions/20260616_02_skill_favorites.py rename to offline-backend-20260512/backend/packages/harness/deerflow/persistence/migrations/versions/20260616_04_skill_favorites.py index 0a223a3b..e531a31e 100644 --- a/offline-backend-20260512/backend/packages/harness/deerflow/persistence/migrations/versions/20260616_02_skill_favorites.py +++ b/offline-backend-20260512/backend/packages/harness/deerflow/persistence/migrations/versions/20260616_04_skill_favorites.py @@ -10,8 +10,14 @@ startup (new tables are always created, even on existing DBs); this migration keeps the alembic history consistent per the repo convention (see backend/CLAUDE.md → Schema Migrations). -Revision ID: 20260616_02 -Revises: 20260616_01 +Note: this migration originally shared the revision id ``20260616_02`` with +``20260616_02_knowledge_folders_templates`` (both branched off ``20260616_01``), +which corrupted the alembic DAG ("present more than once" + phantom heads). It +was renumbered to ``20260616_04`` and re-chained after that revision so the +history is linear and single-headed. + +Revision ID: 20260616_04 +Revises: 20260616_02 Create Date: 2026-06-16 """ @@ -22,8 +28,8 @@ from alembic import op from sqlalchemy import inspect -revision = "20260616_02" -down_revision = "20260616_01" +revision = "20260616_04" +down_revision = "20260616_02" branch_labels = None depends_on = None diff --git a/offline-backend-20260512/backend/packages/harness/deerflow/persistence/migrations/versions/20260617_01_merge_chain_seat_and_skill_always_on_heads.py b/offline-backend-20260512/backend/packages/harness/deerflow/persistence/migrations/versions/20260617_01_merge_chain_seat_and_skill_always_on_heads.py new file mode 100644 index 00000000..5eb84731 --- /dev/null +++ b/offline-backend-20260512/backend/packages/harness/deerflow/persistence/migrations/versions/20260617_01_merge_chain_seat_and_skill_always_on_heads.py @@ -0,0 +1,30 @@ +"""Merge the chain-seat-mode and skill-always-on migration heads. + +Two lineages both descend from ``20260612_01`` (roundtable_jobs orchestration +plan): ``20260615_01`` (chain seat mode) branched directly off it, while the +``20260616_*`` line (positions → knowledge folders/templates → skill_favorites → +skill always_on) is the other branch. That left the graph with two heads +(``20260615_01`` and ``20260616_03``) so ``alembic upgrade head`` failed with +"Multiple head revisions are present". This no-op merge revision joins both +lineages so ``upgrade head`` resolves to a single head again. + +Revision ID: 20260617_01 +Revises: 20260615_01, 20260616_03 +Create Date: 2026-06-17 +""" + +from __future__ import annotations + + +revision = "20260617_01" +down_revision = ("20260615_01", "20260616_03") +branch_labels = None +depends_on = None + + +def upgrade() -> None: + pass + + +def downgrade() -> None: + pass From 54fff09e748d5048a328a150d7d5fd28a3d0a9b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=89=E9=91=AB=E5=8D=87?= Date: Wed, 17 Jun 2026 17:35:48 +0800 Subject: [PATCH 2/2] =?UTF-8?q?feat(frontend):=20=E4=B8=8D=E5=B8=A6=20side?= =?UTF-8?q?bar=20=E7=9A=84=E8=81=8A=E5=A4=A9=E9=A1=B5=E9=A1=B6=E6=A0=8F?= =?UTF-8?q?=E5=8F=B3=E4=BE=A7=E4=B9=9F=E5=B1=95=E7=A4=BA=E5=90=8D=E7=A7=B0?= =?UTF-8?q?+=E5=B2=97=E4=BD=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 紧凑布局(无全局 Header)下复用 UserIdentityBadge,数据源与全量模式 Header 一致,仅在 !compactChrome 时渲染,避免与全局顶栏重复。 Co-Authored-By: Claude Opus 4.8 (1M context) --- .../workspace/user-identity-badge.tsx | 54 +++++++++++++++++++ frontend-web/src/pages/ChatPage.tsx | 12 +++++ 2 files changed, 66 insertions(+) create mode 100644 frontend-web/src/components/workspace/user-identity-badge.tsx diff --git a/frontend-web/src/components/workspace/user-identity-badge.tsx b/frontend-web/src/components/workspace/user-identity-badge.tsx new file mode 100644 index 00000000..5929040e --- /dev/null +++ b/frontend-web/src/components/workspace/user-identity-badge.tsx @@ -0,0 +1,54 @@ +"use client"; + +import { User } from "lucide-react"; +import { useMemo } from "react"; + +import { getStoredAuth } from "@/core/auth"; +import { useMyPosition } from "@/core/positions"; +import { cn } from "@/lib/utils"; +import { readUserInfo } from "@/strategy-components/lib/light-app"; + +/** + * 名称 + 岗位 用户标识。全量模式由 strategy-components/Header.tsx 顶栏右侧展示; + * 紧凑模式(不带全局顶栏)无处承载,故抽成共享组件,复用同一套数据来源 + * (`readUserInfo().userName` 优先、回退 auth 邮箱前缀;岗位取 `useMyPosition`), + * 供页面级顶栏(如 ChatPage)右侧复用,保证两套布局一致。 + * + * 配色按当前内容区前景色(foreground / muted-foreground),适配聊天页浅底顶栏, + * 而非 Header 的白字贴 banner 方案。 + */ +export function UserIdentityBadge({ className }: { className?: string }) { + const auth = getStoredAuth(); + const { data: myPosition } = useMyPosition(); + const positionName = myPosition?.name?.trim() || ""; + + // 与 Header.tsx 一致:外部登录写入的真实用户名优先,缺省回退邮箱前缀。 + const displayName = useMemo(() => { + const info = readUserInfo(); + const name = typeof info.userName === "string" ? info.userName.trim() : ""; + if (name) return name; + const email = auth?.email?.trim(); + return email ? email.split("@")[0] : "用户"; + }, [auth?.email]); + + return ( +
+
+ +
+
+

+ {displayName} +

+ {positionName && ( +

+ {positionName} +

+ )} +
+
+ ); +} diff --git a/frontend-web/src/pages/ChatPage.tsx b/frontend-web/src/pages/ChatPage.tsx index 81f21b66..b1c24df3 100644 --- a/frontend-web/src/pages/ChatPage.tsx +++ b/frontend-web/src/pages/ChatPage.tsx @@ -27,6 +27,7 @@ import { QuestionNavigator } from "@/components/workspace/question-navigator"; import { ThreadTitle } from "@/components/workspace/thread-title"; import { TodoList } from "@/components/workspace/todo-list"; import { TokenUsageIndicator } from "@/components/workspace/token-usage-indicator"; +import { UserIdentityBadge } from "@/components/workspace/user-identity-badge"; import { Welcome } from "@/components/workspace/welcome"; import { CompactWelcome } from "@/components/workspace/compact-welcome"; import { readSidebarEnabled } from "@/core/page-layout/sidebar-enabled"; @@ -481,6 +482,17 @@ export default function ChatPage() { + {/* 不带全局顶栏(紧凑模式)时,名称+岗位回落到聊天页顶栏右侧, + 与全量模式 Header 右上角保持一致。全量模式已有顶栏则不重复展示。 */} + {!compactChrome && ( + <> +
+ + + )}