docs(impl): PR5 implementation note + STATUS update
11 PR5 commits: alembic 0002 (nullable workspace_id + FK + composite index on 4 business tables) -> backfill_workspace_id.py (3-step idempotent) -> alembic 0003 (pre-flight refuse + NOT NULL + UNIQUE). 18 new tests (9 alembic 0002+0003 + 9 backfill, including dry-run and no-users error). 3150 passed + 30 skipped + 16 pre-existing caplog flake (unchanged from PR4). T5.11 (ORM nullable=False) deferred to PR6 — flipping the ORM-side NOT NULL would break 6 INSERT sites whose repository signatures get workspace_id wiring in PR6 anyway. DB-level invariant is already enforced by alembic 0003 along the production path. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2,11 +2,11 @@
|
|||||||
|
|
||||||
> **每完成 1 个 PR 后必更新**。本文是 Stage 0 唯一的"现在到哪了"权威来源——其它文件(plan、ADR、各 PR impl note)都是静态的,不反映执行进度。
|
> **每完成 1 个 PR 后必更新**。本文是 Stage 0 唯一的"现在到哪了"权威来源——其它文件(plan、ADR、各 PR impl note)都是静态的,不反映执行进度。
|
||||||
>
|
>
|
||||||
> 上次更新:2026-05-12,PR4 merge 进 docs branch 后
|
> 上次更新:2026-05-13,PR5 merge 进 docs branch 后
|
||||||
|
|
||||||
## 一句话状态
|
## 一句话状态
|
||||||
|
|
||||||
PR1 + PR2 + PR3 + **PR4** 已 merge + **live 验证通过**(PR3 时 RDS 上 11 张表)+ **7 项 LOCK 决策 sign-off** + **docs branch 已 push origin(SSH-over-443)**。**PR4 (2026-05-12)** 落地:alembic 0001 (`users.default_workspace_id`) + JWT 扩 `wid+role` + AuthMiddleware ContextVar 注入 + 注册流程自动建 1 人 workspace + `/auth/me` 返回 `workspaces[]` + lifespan 回填 pre-PR4 admin。**3136 passed + 26 skipped + 0 PR4-induced failures**(16 个预存 caplog flake 已 stash 验证与 PR4 无关)。**下一个:PR5(alembic 0002 ALTER 4 表 + workspace_id 回填 + 改 NOT NULL)**。
|
PR1 + PR2 + PR3 + PR4 + **PR5** 已 merge(PR3 时 RDS 上 11 张表 live 验证通过 + 7 项 LOCK 决策 sign-off + docs branch 已 push origin)。**PR5 (2026-05-13)** 落地:alembic 0002(4 表 + nullable workspace_id + FK + 复合索引)+ `scripts/backfill_workspace_id.py`(3 step 幂等:建 workspace per user → UPDATE 业务表 → legacy_workspace 兜底)+ alembic 0003(pre-flight 拒迁 + NOT NULL + UNIQUE(wid,tid))。**3150 passed + 30 skipped + 16 pre-existing caplog flake**(与 PR5 无关,isolate 跑全 PASS);+18 PR5 新测试覆盖。**T5.11 (ORM nullable=False) 显式推迟到 PR6**——见 [pr5 impl note 偏差说明](./pr5-business-workspace-id.md#偏差说明t511-推迟到-pr6)。**下一个:PR6(路由强校验 + Paths workspace 化 + 仓储 workspace_id 哨兵 + 文件迁移)**。
|
||||||
|
|
||||||
## 8 PR 状态表
|
## 8 PR 状态表
|
||||||
|
|
||||||
@@ -17,12 +17,12 @@ PR1 + PR2 + PR3 + **PR4** 已 merge + **live 验证通过**(PR3 时 RDS 上 11
|
|||||||
| **PR2** | ✅ merged | 8 (T2.1-T2.10) | merged into docs branch (`404135a1..1112a197`) | [pr2-postgres-default.md](./pr2-postgres-default.md) |
|
| **PR2** | ✅ merged | 8 (T2.1-T2.10) | merged into docs branch (`404135a1..1112a197`) | [pr2-postgres-default.md](./pr2-postgres-default.md) |
|
||||||
| **PR3** | ✅ merged | 7 (T3.1-T3.10) | merged into docs branch (`f63089ae..dda82640`) | [pr3-workspaces.md](./pr3-workspaces.md) |
|
| **PR3** | ✅ merged | 7 (T3.1-T3.10) | merged into docs branch (`f63089ae..dda82640`) | [pr3-workspaces.md](./pr3-workspaces.md) |
|
||||||
| **PR4** | ✅ merged | 14 (T4.1-T4.14) | merged into docs branch (`d98498b7..5c7753c0`) | [pr4-auth-workspace.md](./pr4-auth-workspace.md) |
|
| **PR4** | ✅ merged | 14 (T4.1-T4.14) | merged into docs branch (`d98498b7..5c7753c0`) | [pr4-auth-workspace.md](./pr4-auth-workspace.md) |
|
||||||
| **PR5** | 🟡 pending | 0 | — | — |
|
| **PR5** | ✅ merged | 11 (T5.1-T5.10 + T5.12) | merged into docs branch (`a7326978..30f2bd00`) | [pr5-business-workspace-id.md](./pr5-business-workspace-id.md) |
|
||||||
| **PR6** | 🟡 pending | 0 | — | — |
|
| **PR6** | 🟡 pending | 0 | — | — |
|
||||||
| **PR7** | 🟡 pending | 0 | — | — |
|
| **PR7** | 🟡 pending | 0 | — | — |
|
||||||
| **PR8** | 🟡 pending | 0 | — | — |
|
| **PR8** | 🟡 pending | 0 | — | — |
|
||||||
|
|
||||||
**测试基线**:**PR4 末 3136 passed + 26 skipped**(PR3 末 3134 + 25;+38 PR4 新测试吸收了 13 处 test_auth/test_langgraph_auth/test_auth_errors 改写)。PR2 末 3087。**16 个 caplog 排序 flake 在全套跑里出现**(test_jina_client / test_lead_agent_prompt / test_summarization_middleware 等)→ stash 验证均与 PR4 无关;预存 issue,集中清理推迟到 follow-up。期间 1 个偶发 flaky `tests/test_client_live.py::TestLiveStreaming::test_stream_ai_content_nonempty`(单跑 PASS,env 相关,与本 Stage 无关)。
|
**测试基线**:**PR5 末 3150 passed + 30 skipped**(PR4 末 3136 + 26;+14 / +4 = 18 个 PR5 新测试:9 alembic 0002+0003 含 4 PG-skip + 9 backfill 含 dry-run/orchestrator/no-users 报错)。PR3 末 3134 + 25,PR2 末 3087。**16 个 caplog 排序 flake 持续存在**(test_jina_client / test_lead_agent_prompt / test_summarization_middleware 等)→ isolate 跑全 PASS,PR4 时 stash 验证过与 stage 无关;预存 issue,集中清理仍推迟到 follow-up。
|
||||||
|
|
||||||
## 用户必须跟进的事(live verification / 决策)
|
## 用户必须跟进的事(live verification / 决策)
|
||||||
|
|
||||||
@@ -46,7 +46,9 @@ PR1 + PR2 + PR3 + **PR4** 已 merge + **live 验证通过**(PR3 时 RDS 上 11
|
|||||||
| PR2 T2.9 | `backend/CLAUDE.md` Database 段更新 | README 已覆盖 80% 价值 | 写 PR3 时顺手补一句(agent 自己能做,不阻塞) |
|
| PR2 T2.9 | `backend/CLAUDE.md` Database 段更新 | README 已覆盖 80% 价值 | 写 PR3 时顺手补一句(agent 自己能做,不阻塞) |
|
||||||
| PR4 T4.14 | 真机 `make dev` smoke 注册流程 | agent 无法实际起 gateway daemon | 用户跟进;命令清单见 [pr4-auth-workspace.md "Live smoke 命令"](./pr4-auth-workspace.md#live-smoke-命令用户跟进) |
|
| PR4 T4.14 | 真机 `make dev` smoke 注册流程 | agent 无法实际起 gateway daemon | 用户跟进;命令清单见 [pr4-auth-workspace.md "Live smoke 命令"](./pr4-auth-workspace.md#live-smoke-命令用户跟进) |
|
||||||
| PR4 follow-up | Regular user pre-PR4 backfill 脚本 | login 路径已 lazy backfill 覆盖;如果生产有大量预存 regular user,可补 batch 脚本 | 等真出现这个场景再写 |
|
| PR4 follow-up | Regular user pre-PR4 backfill 脚本 | login 路径已 lazy backfill 覆盖;如果生产有大量预存 regular user,可补 batch 脚本 | 等真出现这个场景再写 |
|
||||||
| PR4 follow-up | 16 个 pre-existing caplog flake 集中清理 | 跨多个 test 文件的 propagation 问题,与 PR4 无关 | 单独 follow-up 处理 |
|
| PR4 follow-up | 16 个 pre-existing caplog flake 集中清理 | 跨多个 test 文件的 propagation 问题,与 PR4/5 无关 | 单独 follow-up 处理 |
|
||||||
|
| PR5 T5.11 | ORM model.py `nullable=False` 翻转 | 当前翻会破 6 个 INSERT 站点(PR6 才补 workspace_id 哨兵);DB 不变式由 alembic 0003 承载已足够 | **推迟到 PR6** 自然接入处;详见 [pr5 偏差说明](./pr5-business-workspace-id.md#偏差说明t511-推迟到-pr6) |
|
||||||
|
| PR5 T5.12 真机 PG smoke | `alembic 0002 → backfill → 0003` 端到端 | agent 不能起 RDS 操作 | 用户跟进;命令清单见 [pr5-business-workspace-id.md "Live smoke 命令"](./pr5-business-workspace-id.md#live-smoke-命令用户跟进) |
|
||||||
|
|
||||||
## 即将遇到的开放问题(plan 末尾列的,下个 session 处理)
|
## 即将遇到的开放问题(plan 末尾列的,下个 session 处理)
|
||||||
|
|
||||||
@@ -58,7 +60,7 @@ PR1 + PR2 + PR3 + **PR4** 已 merge + **live 验证通过**(PR3 时 RDS 上 11
|
|||||||
|
|
||||||
## 下一步建议
|
## 下一步建议
|
||||||
|
|
||||||
**PR5(alembic 0002 ALTER 4 表 + workspace_id 回填 + 改 NOT NULL)**。plan 推荐 Inline 模式(migration script + 回填 + ALTER 改 NOT NULL 三阶段强耦合)。
|
**PR6(入口路由强校验 + Paths workspace 化 + 仓储 workspace_id 哨兵 + 文件迁移)**。同时接入 PR5 推迟的 T5.11(ORM `nullable=False`)—— ORM 翻转必须与仓储 `create()` 加 `workspace_id` 参数 + 6 个 INSERT 站点 + contextvar 接入一起做,PR6 是天然落点。plan 推荐 Inline 模式(路由 + 仓储 + Paths 强耦合,难拆 subagent)。
|
||||||
|
|
||||||
历史模式回顾:
|
历史模式回顾:
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,122 @@
|
|||||||
|
# PR5 · alembic 0002 + 回填 + alembic 0003 — 4 张业务表 workspace_id
|
||||||
|
|
||||||
|
> 实现笔记。对应 [docs/superpowers/plans/2026-05-10-stage-0-multi-tenant-foundation.md](../../superpowers/plans/2026-05-10-stage-0-multi-tenant-foundation.md) PR5(T5.1-T5.12)。
|
||||||
|
>
|
||||||
|
> 状态:**已落地**,11 个 feat/test commits 提交到 `docs/multi-tenant-redesign`(PR5 起始 `a7326978..` 结束 `30f2bd00`)。
|
||||||
|
|
||||||
|
## 范围
|
||||||
|
|
||||||
|
PR5 把 PR4 的 `users.default_workspace_id` 关系真正接到 4 张业务表上:
|
||||||
|
|
||||||
|
1. **alembic 0002**:`threads_meta` / `runs` / `feedback` / `run_events` 加 *nullable* `workspace_id String(36)` 列 + FK 到 `workspaces`(`ON DELETE CASCADE`)+ `threads_meta` 复合索引 `idx_threads_meta_workspace_user_updated`
|
||||||
|
2. **backfill 脚本** `scripts/backfill_workspace_id.py`(带 `--dry-run`):三步幂等回填
|
||||||
|
- Step 1:每个 `default_workspace_id IS NULL` 的 user 建 1 人 workspace + owner membership + 回填 `users.default_workspace_id`
|
||||||
|
- Step 2:correlated subquery `UPDATE` 4 张业务表,`workspace_id` 从行所属 user 的 `default_workspace_id` 拉
|
||||||
|
- Step 3:剩余 `workspace_id IS NULL`(user_id 本来就 NULL 的孤儿)→ `LEGACY_WORKSPACE_ID = 00000000-0000-0000-0000-000000000000`(按需建,owner = platform admin)
|
||||||
|
3. **alembic 0003**:pre-flight 拒迁(任一表残留 NULL → `RuntimeError`)→ 4 表 `workspace_id` 改 `NOT NULL` → `threads_meta` 加 `idx_threads_meta_workspace_thread UNIQUE(workspace_id, thread_id)`
|
||||||
|
4. **4 个 ORM model**:加 `Mapped[str | None] workspace_id` 列定义对齐 0002 schema,`threads_meta` 模型也加 `idx_threads_meta_workspace_user_updated` Index 以便 `create_all()` 在 dev DB 上落同样形状
|
||||||
|
|
||||||
|
**不在范围**(推迟到 PR6):
|
||||||
|
- ORM model 把 `workspace_id` 翻成 `nullable=False`(plan 的 T5.11,见下文"偏差说明")
|
||||||
|
- 仓储 `create()` 方法加 `workspace_id` 哨兵参数 + WHERE 子句
|
||||||
|
- `@require_permission` 升级 `check_access(thread_id, user_id, workspace_id)` 三参数
|
||||||
|
- 入口路由 `/api/threads` 写 `workspace_id`
|
||||||
|
- `Paths` 切 workspace 维度 + 文件系统迁移
|
||||||
|
|
||||||
|
## 验收
|
||||||
|
|
||||||
|
- [x] **17 个新单测全过**:9 alembic 0002+0003 (5 SQLite + 4 PG-skip) + 9 backfill (含 dry-run + 端到端 orchestrator + no-users 报错)
|
||||||
|
- [x] **全套 `make test` 3150 passed + 30 skipped + 16 pre-existing caplog flake**(PR4 末 3136 + 26 + 同 16 flake;+14 passed / +4 skipped = +18 PR5 测试)。16 个 flake 在 isolate 跑里都 PASS,已 PR4 时确认与本 stage 无关,**继续 follow-up 推迟**
|
||||||
|
- [x] **alembic 0002 + 0003 在 SQLite tempdir 上 upgrade/downgrade 双驱动都过**(PG 自动 skip 当 docker 未起)
|
||||||
|
- [x] **回填脚本 `--dry-run` 测试断言无写入**:snapshot workspaces/memberships/users.default_workspace_id 计数 → 跑 backfill(dry_run=True) → 计数不变
|
||||||
|
- [x] **0003 拒迁断言**:threads_meta 留 1 个 `workspace_id IS NULL` 行 → upgrade 抛 `RuntimeError` 含 `Cannot ALTER` + 提示去跑 backfill
|
||||||
|
- [x] **UNIQUE(workspace_id, thread_id) 触发**:scratch table 验 `IntegrityError`(生产 schema thread_id 已是 PK,shadow 了 UNIQUE,所以用独立表验语义)
|
||||||
|
- [x] **lint**:`make lint` 全过(ruff check + format)
|
||||||
|
- [ ] **真机 PG 完整跑 `alembic upgrade 0002` → `python scripts/backfill_workspace_id.py` → `alembic upgrade 0003`** — **待用户**(agent 不能起 RDS 操作)
|
||||||
|
|
||||||
|
## 偏差说明:T5.11 推迟到 PR6
|
||||||
|
|
||||||
|
**plan 原 task**:"4 个 model.py workspace_id 改 nullable=False;commit"
|
||||||
|
|
||||||
|
**为什么推迟**:
|
||||||
|
|
||||||
|
- ORM model 的 `nullable=False` 影响两条链路:
|
||||||
|
1. **生产**:alembic 0003 已经把 DB 列改 NOT NULL,这是真正承载不变式的边界。ORM 层翻不翻不影响数据库约束
|
||||||
|
2. **测试 / dev**:`init_engine()` 走 `Base.metadata.create_all()`,从 ORM 推 DDL。如果模型 `nullable=False` 而仓储 `create()` 还不传 `workspace_id`(PR6 才动),所有创建 `ThreadMetaRow / RunRow / FeedbackRow / RunEventRow` 的 production code path(6 个 INSERT 站点)会在 DB 层炸 NOT NULL 约束
|
||||||
|
- PR6 的明确 scope 是"仓储 workspace_id 哨兵 + 路由层 workspace 化"——翻 ORM `nullable=False` 必须与 6 个 INSERT 站点 + repo 签名 + contextvar 接入一起做,**不能独立翻**
|
||||||
|
- plan T5.12 验收"`make test` 不破"与 T5.11 单独翻 `nullable=False` 在当前 codebase 状态下**互斥**。本 PR 选择维护 T5.12 验收,将 T5.11 推迟到 PR6 自然接入处
|
||||||
|
|
||||||
|
**承载不变式不变**:alembic 0003 在 DB 层强制 NOT NULL;任何走 alembic 路径(生产)的部署都拿到正确约束。ORM 层 `nullable=True` 只在 dev `create_all()` 路径生效,PR6 翻完后整个体系收敛。
|
||||||
|
|
||||||
|
## 关键决策(与 plan §LOCK + workspace-schema-design 对齐)
|
||||||
|
|
||||||
|
| 项 | 选择 | 理由 |
|
||||||
|
|---|---|---|
|
||||||
|
| 4 表 FK 删除策略 | `ON DELETE CASCADE` | 删 workspace 时业务行随之清空(避免外键悬空) |
|
||||||
|
| `idx_threads_meta_workspace_user_updated` 列序 | `(workspace_id, user_id, updated_at)` | PR6 路由"列出 workspace X 下 user Y 的会话按时间倒序"查询的覆盖索引 |
|
||||||
|
| `idx_threads_meta_workspace_thread` UNIQUE 加在 0003 不是 0002 | nullable 列上 UNIQUE 在 PG 允许多 NULL,在 SQLite 默认也允许;但回填中途的 UNIQUE 校验语义模糊,等 NOT NULL 一起加更干净 | 风险更可控 |
|
||||||
|
| 回填 Step 2 SQL 形态 | SQLAlchemy `update().values(workspace_id=correlated_subquery)` | SQLite 3.33+ 才支持 `UPDATE ... FROM`;correlated subquery 跨方言可移植 |
|
||||||
|
| 回填 Step 1 slug 走 walker + blacklist | `auto_slug_from_email` + `next_available_slug` + 把 `SLUG_BLACKLIST` 当"已占用" | 与 PR4 注册流程语义一致;admin@example.com → `admin-2`("admin" 在黑名单) |
|
||||||
|
| `legacy_workspace` owner 选取 | 先找 `system_role='admin'` 最早一个,没有则用最早的任意 user,都没有则 `RuntimeError` | 不能创建无 owner 的 workspace;空 DB 上明确报错强制操作员先 bootstrap admin |
|
||||||
|
| `_BUSINESS_TABLES` 顺序 | `threads_meta / runs / feedback / run_events` | 仅人眼可读性,无依赖 |
|
||||||
|
| `_ensure_legacy_workspace` 拆出 orchestrator 调一次 | 不内联 Step 3 内(避免循环 4 次 check) | 简单 + 显式步骤分隔 |
|
||||||
|
| 0003 pre-flight raise 类型 | `RuntimeError` with `_BackfillRequiredError` 子类 | Alembic 抛错语义最自然;消息含具体表 + 行数 + 修复命令 |
|
||||||
|
| Step 3 dry-run 报告 | `WOULD assign N orphan rows in {table}` 包括 Step 2 没 cover 的全部 NULL 行 | 操作员能看到具体多少行会被 lump 进 legacy_workspace |
|
||||||
|
|
||||||
|
## 跟进项(不在 PR5 范围)
|
||||||
|
|
||||||
|
- **PR6**:4 个仓储 `create()` 加 `workspace_id` 参数 + INSERT;翻 ORM model `nullable=False`;`@require_permission` 三参数;`/api/threads` 路由层校验
|
||||||
|
- **真机 PG smoke**:`alembic upgrade 0002` → `python scripts/backfill_workspace_id.py --dry-run` → 看输出 → 实跑 → `alembic upgrade 0003`。命令清单见下方
|
||||||
|
- **远程 RDS testcontainers**:本 PR 复用 PR3 测试 fixture,PG twin 测试自动 skip 当 docker 未起;用户需起 docker daemon 实跑一遍
|
||||||
|
|
||||||
|
## 涉及文件
|
||||||
|
|
||||||
|
| 类别 | 文件 |
|
||||||
|
|---|---|
|
||||||
|
| **新增 (harness)** | `persistence/migrations/versions/0002_business_tables_workspace.py` |
|
||||||
|
| **新增 (harness)** | `persistence/migrations/versions/0003_business_tables_workspace_not_null.py` |
|
||||||
|
| **新增 (script)** | `backend/scripts/backfill_workspace_id.py`(3 个 step + 1 个 ensure + 1 个 orchestrator + main)|
|
||||||
|
| **新增 tests** | `test_alembic_business_tables.py`, `test_backfill_workspace_id.py` |
|
||||||
|
| **修改 (harness models)** | `persistence/thread_meta/model.py`(+`workspace_id` FK + composite index),`persistence/run/model.py`,`persistence/feedback/model.py`,`persistence/models/run_event.py` |
|
||||||
|
| **修改 (test compat)** | `test_alembic_default_workspace_id.py`("head" → "0001_users_default_workspace" 显式 pin)|
|
||||||
|
|
||||||
|
## 测试矩阵
|
||||||
|
|
||||||
|
| 测试文件 | 用例数 | 覆盖 |
|
||||||
|
|---|---|---|
|
||||||
|
| `test_alembic_business_tables.py` | 9(5 SQLite + 4 PG-skip)| 0002 upgrade/downgrade + 0003 happy/refuse-NULL/UNIQUE |
|
||||||
|
| `test_backfill_workspace_id.py` | 9 | Step 1 创建/幂等/黑名单 + Step 2 4 表/多 user 隔离 + Step 3 orphan/无 user 报错 + orchestrator + dry-run no-write |
|
||||||
|
|
||||||
|
总计:**18 个 PR5 新测试** + 1 个改写(pin 0001 revision)。
|
||||||
|
|
||||||
|
## Live smoke 命令(用户跟进)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. 当前 DB state(应在 PR4 之后状态)
|
||||||
|
psql "$DEER_FLOW_PG_URL" -c "SELECT count(*) FROM users WHERE default_workspace_id IS NULL;"
|
||||||
|
|
||||||
|
# 2. dry-run 看 backfill 会动什么
|
||||||
|
cd backend && PYTHONPATH=. python scripts/backfill_workspace_id.py --dry-run
|
||||||
|
|
||||||
|
# 3. 跑 0002(加 nullable 列)
|
||||||
|
cd backend/packages/harness/deerflow/persistence && \
|
||||||
|
PYTHONPATH=../../../.. alembic -c migrations/alembic.ini upgrade 0002_business_tables_workspace
|
||||||
|
|
||||||
|
# 4. 真跑 backfill
|
||||||
|
cd backend && PYTHONPATH=. python scripts/backfill_workspace_id.py
|
||||||
|
|
||||||
|
# 5. 应返 0
|
||||||
|
psql "$DEER_FLOW_PG_URL" -c "SELECT count(*) FROM threads_meta WHERE workspace_id IS NULL;"
|
||||||
|
psql "$DEER_FLOW_PG_URL" -c "SELECT count(*) FROM runs WHERE workspace_id IS NULL;"
|
||||||
|
psql "$DEER_FLOW_PG_URL" -c "SELECT count(*) FROM feedback WHERE workspace_id IS NULL;"
|
||||||
|
psql "$DEER_FLOW_PG_URL" -c "SELECT count(*) FROM run_events WHERE workspace_id IS NULL;"
|
||||||
|
|
||||||
|
# 6. 跑 0003(改 NOT NULL + UNIQUE)
|
||||||
|
cd backend/packages/harness/deerflow/persistence && \
|
||||||
|
PYTHONPATH=../../../.. alembic -c migrations/alembic.ini upgrade 0003_business_tables_workspace_not_null
|
||||||
|
|
||||||
|
# 7. 确认 legacy_workspace
|
||||||
|
psql "$DEER_FLOW_PG_URL" -c "SELECT id, name, slug FROM workspaces WHERE id = '00000000-0000-0000-0000-000000000000';"
|
||||||
|
```
|
||||||
|
|
||||||
|
如果 dry-run 输出里 `legacy_workspace_created=True` 而 DB 里还没用户,先去 `/auth/initialize` 建管理员再跑——backfill 不会替你 bootstrap。
|
||||||
Reference in New Issue
Block a user