feat(v2.5.0): merge 2.5.0-PM — ReBAC permission, multi-tenant, org sync#1983
Open
jieyuhuayang wants to merge 177 commits intofeat/2.5.0-beta1from
Open
feat(v2.5.0): merge 2.5.0-PM — ReBAC permission, multi-tenant, org sync#1983jieyuhuayang wants to merge 177 commits intofeat/2.5.0-beta1from
jieyuhuayang wants to merge 177 commits intofeat/2.5.0-beta1from
Conversation
- Update feat_cicd pipeline: fix uv mirrors config (align with cicd pipeline), add SSH deploy step to 114 server, trigger on 2.5.0-PM branch - Add failure notification to Feishu webhook - Modernize pre-commit hooks: replace flake8/yapf/isort with ruff - Add .editorconfig for cross-IDE consistency - Add PR template for collaboration workflow Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add bisheng-sync.sh to .gitignore - Point frontend file service proxy to localhost:9000 (local MinIO) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Track CLAUDE.md as shared project guide (removed personal dev workflow) - Add docs/architecture/ (10 architecture documents) - Add docs/PRD/ (2.5 permission system PRD + tech spec) - Add docs/私有化部署/ (private deployment guide) - Add docs/blog/ (enterprise permission design blog) - Remove docs from .gitignore Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add comprehensive multi-tenant requirements document as an incremental supplement to the existing 2.5 permission management PRD. Covers tenant model design, data isolation strategy, OpenFGA integration, storage isolation, quota management, and migration plan. Also includes the separate multi-tenant management PRD from another PM, with conflict analysis and merged features documented in Appendix D. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Plant tenant awareness into every layer of the system (DB, JWT, Celery, storage paths) without adding new APIs or UI. This is the foundation for all subsequent v2.5.0 features (F002-F010). Key changes: - Tenant/UserTenant ORM models + DAO + error codes (module 200) - current_tenant_id ContextVar with bypass_tenant_filter() mechanism - SQLAlchemy do_orm_execute + before_flush event hooks for automatic tenant_id query filtering and INSERT filling (INV-1) - JWT payload extended with tenant_id, old tokens fallback to id=1 - HTTP/WS middleware injects tenant context from JWT cookie - Celery signals propagate tenant_id via task headers (INV-8) - Storage prefix functions for MinIO/Milvus/ES/Redis (INV-9) - Alembic migration: tenant/user_tenant tables + tenant_id on 46 tables - Default tenant (id=1) zero-migration compatibility - 42 unit/integration tests (all passing) Covers: AC-01 through AC-11 Invariants: INV-1, INV-6, INV-8, INV-9, INV-13 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- E2E pytest tests for AC-02, AC-07, AC-08, AC-11 (indirect API verification) - E2E test infrastructure: helpers/auth.py, helpers/api.py, helpers/cleanup.py - Manual verification checklist: e2e-checklist.md (DB, UI regression, Celery) - Tests currently skip login-dependent cases (backend needs F001 restart) - Health check passes confirming backend connectivity Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Use E2E_ADMIN_PASSWORD env var (default: dataelem) - Fix workflow list API path: /flows → /workflow/list Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
F001 Multi-Tenant Core Infrastructure — plant tenant awareness into DB/JWT/Celery/storage layers. 30 files, +2823 lines, 47 tests passing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Provide shared pytest fixtures, external service mocks, and frontend Vitest setup so F001–F010 developers can write tests without rebuilding test scaffolding from scratch. Backend: - pyproject.toml: [tool.pytest.ini_options] + test optional-deps - conftest.py: db_engine/db_session (SQLite in-memory, ROLLBACK isolation), async variants, tenant_context/bypass_tenant helpers, mock_redis (fakeredis), mock_minio, mock_openfga, test_client - fixtures/table_definitions.py: 10-table SQLite-compatible DDL registry - fixtures/mock_openfga.py: InMemoryOpenFGAClient (write/check/list) - fixtures/mock_services.py: centralized import-chain pre-mocking - fixtures/factories.py: create_tenant/create_test_user helpers - test_infrastructure_smoke.py: 15 smoke tests verifying all fixtures Frontend (Platform): - vitest.config.ts + setup.ts + test-utils.tsx + smoke test - package.json: vitest + @testing-library devDependencies Verified: 50/50 backend tests passed (15 smoke + 35 F001 regression), 2/2 frontend Vitest tests passed. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… member management Implements the department tree module for BiSheng v2.5.0 permission system overhaul. Provides hierarchical organization structure with materialized path storage, 9 REST endpoints, and DepartmentChangeHandler DTO contract for F004 (ReBAC) integration. Key components: - Department/UserDepartment ORM with DepartmentDao/UserDepartmentDao (sync + async) - DepartmentService: create/tree/update/delete/move/members with path maintenance - 9 API endpoints under /api/v1/departments (admin-only, F004 will add fine-grained ACL) - DepartmentChangeHandler: TupleOperation DTO + log stub for OpenFGA integration - init_data: auto-create root department for default tenant on startup - Error codes 21000-21009 (module 210) - 64 unit/integration tests (DAO + Service + API + init_data) - 16 E2E test cases ready for live backend Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Exclude user_tenant from tenant-aware table discovery (tenant_id is FK, not isolation field) - Fix _get_table_name_from_class fallback returning wrong table names - Extract _set_tenant_context() to deduplicate HTTP/WS middleware logic - Replace AuthJwt.__new__() hack with proper AuthJwt() construction - Move DEFAULT_TENANT_ID import to module level in auth.py Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… contract - Extend Group table: add visibility (public/private) column, change group_name unique constraint to (tenant_id, group_name) composite - New user_group/ DDD module with 9 REST endpoints at /api/v1/user-groups/ - CRUD: create, list, detail, update, delete user groups - Members: batch add, remove, paginated list - Admins: full-replacement set with diff logic - GroupChangeHandler: TupleOperation DTO for 6 events (stub for F004) - Visibility: admin sees all, non-admin sees public + own private groups - Alembic migration v2_5_0_f003_user_group - Error codes 230xx (7 codes: 23000-23006) - 49 tests: 8 handler + 18 DAO + 23 API integration Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
OpenFGA integration foundation for ReBAC permission engine: - OpenFGAConf config model + Docker compose service (T01) - FGAClient async httpx HTTP wrapper with fail-closed semantics (T02) - FGAManager BaseContextManager with auto store/model init (T03) - Static authorization model DSL with 13 type definitions (T04) - Permission error codes 190xx (T05) - FailedTuple ORM compensation queue + Alembic migration (T06) - Permission module skeleton with DTO schemas (T07a) - Spec and tasks documentation Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Core implementation of the ReBAC permission engine: - PermissionService: five-level check chain (admin→cache→FGA→owner→fail-closed), authorize with department subtree expansion, batch_write_tuples, get_permission_level - PermissionCache: L2 Redis cache with 10s TTL, UNCACHEABLE_RELATIONS bypass, scan-based user invalidation - OwnerService: write_owner_tuple, check_is_owner, transfer_ownership for F008 - ChangeHandler connection: DepartmentChangeHandler/GroupChangeHandler execute_async() now delegates to PermissionService.batch_write_tuples() (replaces log stubs) - Celery beat task: retry_failed_tuples every 30s for compensation queue - LoginUser integration: rebac_check/rebac_list_accessible methods - Permission API: 4 endpoints (check, objects, authorize, permissions) - Tests: FGAClient unit tests, PermissionService chain tests, cache tests, ChangeHandler integration tests, upgraded mock_openfga Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- authorize() delegates to batch_write_tuples(), eliminating triple
copy-paste of error-handling ops-building code
- batch_check() now sends correlation_id per check item to correctly
correlate responses from OpenFGA batch-check API
- SCAN pattern for cache invalidation uses exact prefixes (perm:chk:{uid}:
and perm:lst:{uid}:) to avoid false matches on similar user IDs
- retry_failed_tuples batches writes/deletes into single FGA calls,
falling back to per-item retry only on batch failure (fixes N+1)
- FGA-unavailable fallback in check() now uses async _get_resource_creator
instead of sync _sync_owner_fallback to avoid blocking the event loop
- Remove dead TYPE_CHECKING block and redundant _get_fga_client wrappers
- Reset module-level _fga_client cache in FGAManager._async_cleanup()
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
16 API E2E tests across 5 test classes: - TestPermissionCheck: admin shortcircuit, owner fallback, unauthorized, error codes - TestListObjects: admin null, user list, invalid type - TestAuthorize: grant/revoke viewer, non-admin denied, invalid type - TestResourcePermissions: admin read, non-admin denied - TestPermissionHierarchy: manager implies can_manage + can_read Manual checklist covers AC-04 (FailedTuple), AC-07 (ChangeHandler), AC-08 (Docker), AC-09 (startup init), and regression checks. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
E2E test fixes: - workflow/create (201 status) instead of flows/ (404) - User creation requires group_roles field (query API for default group) - Unique names per run (timestamp suffix) to avoid duplicate name errors - Skip authorize→check chain tests when OpenFGA not available FGAManager startup fix: - Add optional=True parameter to register_context() so FGAManager initialization failure does not block application startup - Backend now starts in degraded mode when OpenFGA is unavailable 14 passed, 2 skipped (OpenFGA-dependent), 0 failed. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Fix test patches: _get_fga_client → _get_fga (renamed in /simplify) - Fix redundant except (FGAConnectionError, Exception) → except Exception - Add UNCACHEABLE_RELATIONS guard to list_accessible_ids() cache path Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
#3 batch_write_tuples chunking: Split writes/deletes into chunks of 100 to stay within OpenFGA's per-request tuple limit. Large department subtree expansions now work correctly. #4 privilege escalation guard: Non-admin callers cannot grant relations above their own permission level. A user with can_manage cannot grant 'owner' to themselves. Admin users bypass this check. #7 distributed lock for retry task: retry_failed_tuples now acquires a Redis SETNX lock (60s TTL) to prevent concurrent Beat fires from processing the same pending rows. #8 crash-safe dual-write: ChangeHandler.execute_async() now uses crash_safe=True which pre-inserts FailedTuple records before the FGA call. If the process crashes between MySQL commit and FGA write, pre-recorded entries remain as 'pending' for the retry task to pick up. On FGA success the pre-recorded entries are marked as succeeded. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Cache keys now include tenant_id for multi-tenant isolation:
perm:chk:{tenant_id}:{user_id}:{relation}:{type}:{id}
perm:lst:{tenant_id}:{user_id}:{relation}:{type}
Tenant ID sourced from current_tenant_id ContextVar (falls back to 1).
SCAN invalidation prefixes updated accordingly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
F004 ReBAC Permission Engine Core — OpenFGA integration for BiSheng v2.5. Key deliverables: - FGAClient (httpx async wrapper) + FGAManager (BaseContextManager) - Static authorization model DSL (13 types, permission pyramid) - PermissionService: five-level check chain, authorize, batch_write_tuples - PermissionCache: L2 Redis with tenant-isolated keys (10s TTL) - OwnerService: owner tuple management for F008 - 4 API endpoints: check, objects, authorize, permissions - ChangeHandler → OpenFGA real writes (crash-safe dual-write) - Celery retry_failed_tuples (30s beat, Redis distributed lock) - LoginUser.rebac_check / rebac_list_accessible - Privilege escalation guard on authorize endpoint - E2E tests (14 pass) + unit tests + manual checklist
…a enforcement Transform roles from "permission roles" to "policy roles" with three responsibilities: menu visibility (WEB_MENU), resource quotas (quota_config), and department scope (department_id). Implements three-level quota enforcement (tenant hard limit → role user limit → effective=min). Key changes: - Alembic migration: role table adds role_type/department_id/quota_config fields - Role ORM: new visible_roles DAO with dept subtree filtering (AD-09) - role/ DDD module: RoleService (CRUD + 4-level permission check) + QuotaService (three-level quota calculation + @require_quota decorator) - 9 new API endpoints under /api/v1/roles and /api/v1/quota - Error codes 240xx (6 codes: RoleNotFound through QuotaConfigInvalid) - WebMenuResource enum: +7 new items, 3 deprecated kept for compat - Legacy API backward compat: old /role/ endpoints delegate to new RoleService - LoginUser.get_roles_web_menu() updated for v2.5 menu enum - 34 unit/integration tests (all passing) Covers 30 ACs across role CRUD, menu permissions, quota calculation, quota enforcement, and backward compatibility. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…r handling Fixes from /simplify code review: - quota_service: extract shared _count_resource() eliminating duplicate get_tenant_resource_count/get_user_resource_count (copy-paste → shared) - quota_service: use asyncio.gather() in get_all_effective_quotas to batch 16 sequential DB queries into 2 concurrent gather calls - quota_service: reuse _compute_role_quotas in get_effective_quota (DRY) - quota_service: extract _apply_tenant_cap helper to reduce nesting - quota.py: use asyncio.gather() in /quota/usage endpoint (8 queries → 1 gather) - role.py: fix acount_visible_roles to use _build_visible_roles_stmt subquery instead of duplicating WHERE clause logic - role.py: remove redundant and_() wrapping single condition - role_service: merge _check_role_permission + _check_list_permission into shared _get_permission_level() helper - role_service: replace bare `except Exception: pass` with logged exceptions - user.py: narrow broad except in legacy endpoints — catch specific error codes, log fallback reason instead of silently swallowing - auth.py: remove dead _DEPRECATED_MENUS class variable Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
14 E2E test cases covering role CRUD, menu permissions, quota queries, and legacy API backward compatibility. Manual checklist for dept admin visibility, regular user denial, and web_menu verification. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- 新增 ResourceOwnershipService:批量 owner 交接(MySQL UPDATE + OpenFGA
元组翻转),crash-safe 事务失败 FailedTuple 补偿
- 新增 ResourceTypeRegistry:7 类资源统一元数据(knowledge_space/
folder/knowledge_file/workflow/assistant/tool/channel);dashboard
暂无 ORM,spec §5 已说明延后
- INV-T10 可见集合:to_user 叶子 ∈ {tenant_id, Root};支持 Child→Root
"交回总部"路径,拒绝 Root→Child(改走 F011 migrate-from-root)
- 新增 API:POST /tenants/{id}/resources/transfer-owner + GET
/tenants/{id}/resources/pending-transfer(待交接清单,按叶子 ≠ 资源
tenant_id 判定)
- 错误码 196xx(19601~19606);audit_log action 新增
resource.transfer_owner,TenantAuditAction enum 同步
- 测试:36 个用例全绿(6 registry + 16 service + 14 API)
- 文档:tasks.md 拆 T01~T07;ac-verification.md 含 13 条 AC 映射;
spec §8 升级为自测清单表格(替换旧手工 QA 列表)
- 测试 fixtures:新增 TABLE_ASSISTANT,knowledgefile/t_gpts_tools/
channel 补 tenant_id 列
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…atches
Running F016 tests on 192.168.106.114 during T09 self-test surfaced three
issues that are fixed here. ac-verification.md updated with the 2026-04-19
executed results.
1. **Real bug** — TenantService.aset_quota accepted arbitrary JSON
payloads. Added `QuotaService.validate_quota_config(data.quota_config)`
at the top so unknown keys now return 24005 (F010 AC-4.2 gap, surfaced
by F016 E2E `test_put_quota_rejects_unknown_key`). No schema change.
2. **Test patch target** — `test_count_usage_strict_returns_zero_on_missing_template`
patched `quota_service.get_async_db_session`, which does not exist at
module level (it's lazy-imported inside `_count_resource`). The path
under test never reaches a DB call anyway (template miss returns 0
early); patch removed.
3. **E2E admin password override** — helpers/auth.py hard-codes
`admin123` but 114 admin is `Bisheng@top1`. Introduced local
`_resolve_admin_token(client)` that honours `E2E_ADMIN_PASSWORD` env
var with the helper's default as fallback. Also:
- `test_quota_tree_forbidden_for_non_super_admin` marked skip with
reason — `create_test_user` passes plain password but `login`
RSA-encrypts, so the F010 child_admin fixture flow cannot produce
a working non-super-admin token in this E2E file.
- Added `test_quota_tree_rejects_unauthenticated_request` as a
partial AC-06 auth-gate replacement (verifies `UserPayload.get_admin_user`
rejects requests without a cookie — covers the gate, not the
non-super-admin branch).
T09 executed results:
- Unit: 36/36 pass (`test/test_quota_service*.py` + `test_require_quota_decorator.py`)
- E2E: 5/5 pass + 7 skipped (new unauth-401 test passes)
- §7-4 aggregate: verified via probe — MySQL shows Root=30 workflows,
orphaned Child id=2 correctly excluded; /tenants/quota/tree returns
root.usage[workflow].used=30 children_count=0 → INV-T9 satisfied
- AC-03/06/10: verified end-to-end
…016 docs F016 /tenants/quota/tree self-test on 192.168.106.114 exposed a long- standing SQL template bug in v2.5.0/F005's _RESOURCE_COUNT_TEMPLATES: the 'knowledge_space' template uses `WHERE ... AND status != -1` but the `knowledge` table actually has `state` + `is_released`, not `status`. `_count_resource` catches the `Unknown column 'status'` error and returns 0, so the UI silently shows 0 knowledge usage — never surfaces. - v2.5.0/F005 spec §9.1 — canonical 'Known Post-Release Issues' entry KI-01 with exact location, root cause, suggested fix (`WHERE state != 0`), and impact scope. - v2.5.1/F017 spec §6.1 — added 'prerequisite fix' hint that F017's INV-T6 shared-knowledge_space verification is blocked until KI-01 is addressed; points F017 devs to fix the template while working on shared_storage. - v2.5.1/F016 ac-verification.md — new 'Known issues uncovered during self-test' section anchoring the finding to F005 §9.1. Not fixing KI-01 here — F016 is read-only on the SQL templates; the fix belongs to F005 Owner (or F017's shared-knowledge work).
…SA encrypt Two long-standing E2E infrastructure gaps surfaced during F016 114 self- test. Fixing them in the shared helper so F014/F015/F017/F018/F019/F020 E2E runs benefit without per-feature workarounds. 1. get_admin_token: honour E2E_ADMIN_PASSWORD env var (fallback to the hard-coded 'admin123'). The docstring in the previous version claimed env-var support but the code passed the literal string through. 114 uses 'Bisheng@top1'; setting E2E_ADMIN_PASSWORD unblocks every test. 2. create_test_user: RSA-encrypt the password before POSTing /user/create. Backend's UserService.create_user calls decrypt_md5_password which RSA-decrypts before md5-hashing, so passing plain text stored a garbage hash. Follow-up get_user_token calls then failed with 10600 (Account or password error) because the RSA-decrypted login plaintext md5'd to something different than the stored garbage. F016 E2E cleanup (same commit): - removed the now-redundant _resolve_admin_token wrapper - restored test_quota_tree_forbidden_for_non_super_admin (previously @Skip'd); asserts HTTP 200 + body status_code=403 per UnifiedResponseModel convention (global exception handler converts UnAuthorizedError.http_exception to 200+body) - test_quota_tree_rejects_unauthenticated_request now documents that the real HTTP 401 comes from AuthJwt short-circuiting BEFORE get_admin_user runs Run result on 192.168.106.114 (uvicorn running + E2E_ADMIN_PASSWORD set): 6 passed, 6 skipped in 1.18s ac-verification.md updated: E2E row now 6/6 passed; Follow-up-fixes §3 rewritten as "shared helper fix" + §4 for F016 test refactor.
…enced non-existent columns/tables
v2.5.0/F005 KI-01 (2026-04-19 F016 self-test discovery, now fixed).
Four resource-count SQL templates silently failed against the real
schema; _count_resource's try/except caught the MySQL error and returned
0, making every quota count through these types read as 0.
Templates fixed in quota_service.py::_RESOURCE_COUNT_TEMPLATES:
- knowledge_space: was `WHERE {col}=:{param} AND status != -1`. The
knowledge table has no `status` column (only state + is_released) and
delete_knowledge does a hard DELETE, so a plain COUNT(*) is correct.
Now: `WHERE {col}=:{param}`.
- channel + channel_subscribe: were `WHERE {col}=:{param} AND
status='active'`. The channel table has no `status` column either
(only is_released). Filter removed.
- tool: was `FROM gpts_tools`. Actual table name has a `t_` prefix:
t_gpts_tools. Name fixed.
- knowledgefile status filter (`status IN (1,2)`) retained — that table
really does have a status column.
Added 3 static-assertion regression tests in
test_quota_service.py::TestKI01SqlTemplateSchema to block re-introduction
(cheap, DB-free; full integration verified by 114 probe).
114 probe BEFORE:
usage[knowledge_space] used=0 limit=-1
usage[tool] used=0 limit=-1
usage[channel] used=0 limit=-1
114 probe AFTER (same server, same data, uvicorn reloaded):
usage[knowledge_space] used=15 limit=-1
usage[tool] used=37 limit=-1
usage[channel] used=0 limit=-1 (channel table genuinely empty)
MySQL ground truth confirms:
SELECT COUNT(*) FROM knowledge WHERE tenant_id=1 → 15
SELECT COUNT(*) FROM t_gpts_tools WHERE tenant_id=1 AND is_delete=0 → 37
Docs updated:
- v2.5.0/F005 spec §9.1: KI-01 row marked "FIXED" with expanded scope
(channel/tool, not just knowledge_space) + commit fingerprint
- v2.5.1/F017 spec §6.1: "prerequisite fix" → "prerequisite fix (done)"
- v2.5.1/F016 ac-verification.md: Known-issues section updated with
post-fix probe numbers
把 spec §7 的手工 QA 清单替换为 Test→AC 映射表,明确 开发者需自运行 pytest/Vitest 用例,不再依赖用户人肉点击; 不可自动化项单列 [发版前专项](压测等)。 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- 新建 11 Python 模块:
- ReconcileConf(core/config)
- OrgReconcileService(6h 校对核心编排,Redis SETNX 锁,11 步)
- DepartmentRelinkService(external_id_map / path_plus_name 双策略)
- RelinkConflictStore(Redis HASH + 7d TTL)
- RemoteDeptDiffer(F009 reconciler 的 ts-annotated 薄包装 + crosses_tenant 派生)
- TsConflictReporter(weekly + daily escalation 聚合)
- 3 Celery tasks:reconcile_all_organizations / report_ts_conflicts_weekly / daily_escalation
- Pydantic schemas/relink.py
- API endpoints/relink.py + resolve-conflict
- 扩展 8 模块:
- errcode/sso_sync.py +5 错误码 19314-19318(MMM=193 与 F014 共享)
- settings.py 注册 ReconcileConf + 3 beat 条目
- department.py 加 aget_active_by_source_path_name / aupdate_external_id /
aget_user_ids_by_department DAO
- org_sync.py 加 event_type/level/external_id/source_ts 列 + idx_conflict_lookup +
aget_all_active / acreate_event / acount_recent_conflicts /
aget_conflicts_since / aget_latest_event
- http_middleware.py TENANT_CHECK_EXEMPT_PATHS 加 relink 两路径
- api/router.py 挂载 relink_router
- conftest.py / table_definitions.py 同步 F015 新字段 + 索引
- Alembic 迁移 v2_5_1_f015_reconcile_log_fields(幂等 upgrade/downgrade)
- 8 测试文件约 60 条用例(SQLite + AsyncMock 模式)覆盖 AC-01..AC-13 + INV-T12/T8/T7/T2
- 文档:tasks.md + ac-verification.md + locust 性能占位(AC-08 发版前跑)
AC-11 关键:同 ts Gateway-upsert + Celery-remove 冲突走 remove-wins 路径,
写 audit_log.action='dept.sync_conflict' + event_type='ts_conflict' 事件行
+ 19317 warn,统一由 DepartmentDeletionHandler(CELERY_RECONCILE) 触发孤儿处理。
本地已通过 py_compile;pytest 待 114 远程或 CI 跑。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- reconcile_service: OrgSyncLogBuffer 没有 dept_upserted/dept_archived_event 方法; 直接操作 dept_created/dept_updated/dept_archived 计数字段,与 F014 buffer 实际签名对齐。 - reconcile_tasks.reconcile_single_config: SsoReconcileLockBusyError.http_exception() 返回 FastAPI HTTPException(非子类),原 except 分支永远不 match → 改按 HTTPException.status_code == 19314 分流,AC-13 precursor 的 warning log 正常触发。 - test_reconcile_celery_tasks: 侧加载 tasks.py 的 backend_root 路径从硬编码 /opt/bisheng 改为相对 __file__ 解析,worktree / CI 两处都可用; reconcile_single_config passthrough 装饰器下是普通 function 没 apply_async, 改为整体替换为 MagicMock 以便其 apply_async 属性自动可用。 - test_org_reconcile_service._make_dept: 补 path / status / sort_order 字段, F009 reconciler 的拓扑排序 + 子孙 archive 扫描会读取这三个属性。 114 运行结果: F015 八文件 64/64 passed; F011/F012/F013/F014 + F015 定向回归 216/217 passed(唯一失败与 F015 无关, test_department_service::test_check_permission_raises 是 F014 checkpoint 之前 已存在的 async-未 await bug)。 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ting bug) 原 test 同步调用 ``_check_permission(_NonAdminUser())`` —— 该函数是 ``async def``(见 department_service.py L72),返回的 coroutine 从未被 asyncio 运行器调度,``pytest.raises`` 永远看不到异常 → DID NOT RAISE。 生产代码通过 ``await _check_permission(...)`` 调用(如 L195/205),行为 正常;只是这条测试等于 0 覆盖。 pyproject.toml 已声明 ``asyncio_mode='auto'``,把 test 方法改 ``async def`` 即可自动跑在 asyncio 运行器下。 114 验证:test_department_service.py::TestPermission 2/2 passed; F011~F015 定向回归 217/217 passed。 非 F015 scope,但与 F015 同 worktree 命中,并入本分支避免 future /code-review 或 CI 再次报红。 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- 新增 test/e2e/test_e2e_ldap_reconcile.py(6 条 HTTP 端到端)
- 3 条 HMAC gate:无签名 / 错误签名 / resolve-conflict 同路径 → 19301
- 3 条 Delegation:AC-05 external_id_map / AC-06 path_plus_name /
AC-07 dry_run 全链路(HMAC dep + router + service)
- feature-gate + HMAC-secret 双探针,缺一即 skip,使得 bare
`pytest test/e2e` 在 2.5.0-PM 基线后端上不报错
- 真 bug 修复:relink_service.relink / resolve_conflict 与
reconcile_service.reconcile_config 都没 wrap `bypass_tenant_filter
+ set_current_tenant_id(ROOT_TENANT_ID)`,所有无 JWT 的内部/Celery 入口
在 SQLAlchemy tenant_filter event 下 raise 20004
"Missing tenant context";对齐 F014 `LoginSyncService` 模式补齐
- 114 运行结果:
- E2E 6/6 passed(独立 7861 uvicorn,HMAC secret 从 config.yaml 读)
- F015 单元/集成 64/64 passed 回归无回退
- 文档:ac-verification.md §5 追加 2026-04-20 E2E 执行记录 + 覆盖矩阵
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
/simplify 3 个 review agent 的 findings 精选修复(5 类 / ~10 findings 中选最高价值 + 最低破坏面的条目): - 新增 org_sync/domain/constants.py:EventType / EventLevel / AuditAction / ReconcileAction 集中 stringly-typed 字符串。reconcile service / reporter / relink 全部替换原本散落的 'stale_ts' / 'ts_conflict' / 'warn' / 'dept.sync_conflict' / 'upsert' 等字面量。 - 新增 worker/_asyncio_utils.py 的 run_async_task helper,消除 reconcile_tasks.py 里 4 处重复的 `loop = asyncio.new_event_loop() / try / finally / close` 样板(F012 tenant_reconcile/tasks.py 仍沿用 旧 pattern,scope 外不动)。 - reconcile_service._apply_upsert/_apply_archive 提取 `_record_stale_ts` classmethod,合并两处 SKIP_TS 分支重复 event_row dict + buffer.warn + result.skipped_ts 骨架。 - 简化 `int(getattr(existing, 'last_sync_ts', 0) or 0)` → 直接 `int(existing.last_sync_ts or 0)`,Department 模型字段 server_default=0 不会 None,外层 getattr 多余。 - relink_service 提取 _apply_or_defer helper,合并两种 strategy 的 "单候选 apply vs dry_run" 骨架;减少 external_id_map 与 path_plus_name 的重复代码。 - ts_conflict_reporter 提取 _count_by_external_id staticmethod,合并 weekly_report / daily_escalation_report 两处手工聚合。 未修复的 findings(已记录为未来改进): - [Eff] reconcile _apply_upsert/_apply_archive N+1 per-op aget_by_source_external_id:SELECT 快且需要读 is_deleted=1 行, 换预加载 dict 有语义风险,保留。 - [Eff] aget_conflicts_since 应 SQL GROUP BY 而非 Python 聚合: 改动会 break mock-based reporter 测试,推迟。 - [Eff] RelinkConflictStore DEL+HSET+EXPIRE 3 roundtrip 应 pipeline: fakeredis 的 pipeline 支持有限,推迟。 114 验证:F015 64/64 passed 无回退。 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…冲突告警
T01-T15 全部完成,64 unit/integration + 6 e2e 在 114 独立 7861 uvicorn 通过。
核心能力:
- Celery Beat 每 6h 全量校对 org_sync_config,diff remote vs local 走 F014
OrgSyncTsGuard(INV-T12 ts 最大为准、同 ts remove 优先)→ upsert /
archive → 触发 F011 DepartmentDeletionHandler + F012 UserTenantSyncService
- POST /api/v1/internal/departments/relink(HMAC):SSO 换型时按
external_id_map / path_plus_name 策略重建部门映射,单候选 auto apply,
多候选 Redis HASH 暂存(TTL 7d)由管理员通过 resolve-conflict 端点手工确认
- 冲突告警:事件级 org_sync_log(event_type/level/external_id/source_ts +
idx_conflict_lookup 复合索引)+ 周报(Mon 09:00,阈值 ≥3 次/external_id)
+ 5 天未解决升级每日(09:00)
- 错误码:sso_sync.py 追加 19314-19318(MMM=193 与 F014 共享)
架构复用:F014 OrgSyncTsGuard / DeptUpsertService / OrgSyncLogBuffer+flush_log
/ verify_hmac HMAC dep;F011 DepartmentDeletionHandler / AuditLogDao.ainsert_v2
/ inbox_helper;F012 UserTenantSyncService;F009 OrgSyncProvider +
get_provider + reconciler 拓扑排序。
落地特性:
- Redis SETNX 锁 `org_reconcile:{config_id}`(TTL 1800s 对齐 Celery
soft_time_limit)AC-13 并发幂等
- relink / reconcile service 全 wrap bypass_tenant_filter +
set_current_tenant_id(ROOT/tenant_id),避免 HMAC-only 内部路径踩
SQLAlchemy tenant_filter event
- Constants 模块 (org_sync/domain/constants.py) 集中 EventType /
EventLevel / AuditAction / ReconcileAction,避免 stringly-typed 散落
未覆盖:
- AC-08 10 万部门 <30 min 压测(locust 占位,发版前 2 周跑)
- 管理员冲突确认 UI(归 F019 admin-scope console)
spec: features/v2.5.1/015-ldap-reconcile-celery/spec.md
tasks: features/v2.5.1/015-ldap-reconcile-celery/tasks.md
ac-verification: features/v2.5.1/015-ldap-reconcile-celery/ac-verification.md
…fallback
实施 PRD §7.2 集团共享资源机制:Root 创建的资源通过"共享给集团子公司"开关
对所有 Child 成员只读分发;Child 用户产生的衍生数据(对话、token、调用日
志)归属 Child 叶子 Tenant(INV-T13),不污染 Root 配额。
Phase A 基础设施
- common/errcode/tenant_sharing.py — 错误码 19501~19504 + TenantContextMissingError
- tenant/domain/constants.py — TenantAuditAction 新增 resource.share_{enable,disable}
- tenant/domain/services/resource_share_service.py — FGA 元组封装 +
share_on_create 高层 orchestrator + sync 包装
Phase B 资源共享机制(5 类:knowledge_space / workflow / assistant / channel / tool)
- Alembic v2_5_1_f017_is_shared: 5 表加 is_shared 列
- 5 个 ORM 模型声明 is_shared 字段
- 5 个资源 Service create_* 扩展 share_to_children 参数(None → 读 Root.share_default_to_children)
- 统一 PATCH /api/v1/resources/{type}/{id}/share API(tenant/api/endpoints/resource_share.py)
- TenantMountService.mount_child(auto_distribute=True) + _on_child_mounted 钩子
- TenantMountService.unmount_child + _on_child_unmounted 钩子
Phase C 衍生数据归属(INV-T13)
- ChatMessage/MessageSession ORM 声明 tenant_id 字段(F001 DB 列已就位)
- ChatMessageService.acreate + add_qa_messages 显式写 get_current_tenant_id()
- ChatSessionService.get_or_create_session 改造为叶子 Tenant 归属
- context 缺失时统一抛 TenantContextMissingError(19504)
Phase D LLM 用量追踪
- Alembic v2_5_1_f017_llm_token_log: 新建 llm_token_log + llm_call_log 表
- LLMTokenTracker.record_usage / ModelCallLogger.log(tenant_id = 叶子)
- LLMUsageCallbackHandler 接入 workflow LLM 节点 on_llm_end / on_llm_error
Phase E 外部存储 fallback
- MinIO get_object_sync / download_object_sync / object_exists_sync
先查 tenant_{code}/ 前缀,NoSuchKey + Child 用户时 fallback Root 前缀
- KnowledgeRag.aexpand_with_root_shared: Child 检索时补入 Root.is_shared=1
的知识空间 id,与现有 get_multi_knowledge_vectorstore_sync 合并检索
Phase F 前端 UI(部分,核心共用组件就绪)
- components/bs-ui/shareToChildrenSwitch + sharedBadge 共用组件
- 知识空间 + 助手创建表单接入 ShareToChildrenSwitch
- assistant API client 支持 share_to_children
- i18n 三语言(en/zh-Hans/ja)共 9 个 key(share.*/mount.*)
- 工作流/频道/工具详情页开关 + 列表 Badge 等 UI 延后 follow-up
Phase G 测试 + 文档
- 7 个测试文件,54 个 pytest 单元/集成用例
- features/v2.5.1/017-tenant-shared-storage/tasks.md 38 任务全部打勾
- features/v2.5.1/017-tenant-shared-storage/ac-verification.md 13 AC 真实验证
真实环境验证(114 服务器,2026-04-20)
- 54/54 pytest 全绿(mock 级 FGA + DAO)
- Alembic 双段迁移成功(f014_sso_sync_fields → f017_is_shared → f017_llm_token_log)
- 真实 FGA tuple 读写验证:enable/distribute/list/disable/revoke 全 PASS
- 真实 HTTP E2E:admin PATCH /api/v1/resources/knowledge_space/21/share
来回开关 + 无效类型 400 全 PASS;audit_log 含 shared_children metadata
实施过程修复 3 个真实 bug
- F017-BUG-01: `from bisheng.core.config.settings import settings` 模块无此
顶层变量(运行时 ImportError)→ 改用 bisheng.common.services.config_service
- F017-BUG-02: ORM 未声明 F001 SQL 层加的 tenant_id 列,endpoint
getattr 返 None 导致 Root 资源误判为非 Root → 改原生 SQL + bypass_tenant_filter
- F017-BUG-03: SQLAlchemy Row 非 tuple,`isinstance(row, tuple)` 走 else
导致 int(Row) 抛 TypeError → 直接 row[0]
跨 Feature 副作用
- F011 TenantMountService.mount_child 签名扩展(新增 auto_distribute=True 参数,
向后兼容);PRD §5.2.1 + spec AC-13 明确此接口变更
- CLAUDE.md audit_log action 清单补 resource.share_{enable,disable}
未完成/延后(非阻塞)
- F017-PENDING-01~04: 5 类资源创建表单 UI / 详情页开关 / 列表 Badge /
挂载 Child 弹窗"不自动分发" 的前端接入(后端 API + i18n 已就绪)
- F017-PENDING-05: 存量 chatmessage / message_session tenant_id 按 user_tenant
精准回填脚本(当前 F001 默认 1)
- F017-PENDING-06: LLMUsageCallbackHandler 在 Linsight / Knowledge RAG
其他 LLM 调用链的接入扩展(v2.5.1 F020 继承)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
See feat commit ae32807 for full implementation notes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
全局超管通过 POST /api/v1/admin/tenant-scope 临时锁定管理视角到某 Child Tenant;机制与用户归属解耦(Redis 驻留,非 JWT;仅管理类 API 生效; Child Admin 返 19701)。INV-T14 落地。 - 错误码 197xx:AdminScopeForbiddenError(19701) / AdminScopeTenantNotFoundError(19702) - TenantScopeService:set_scope / get_scope / 3 个钩子方法 - AdminScopeMiddleware:管理类 API 白名单 + 超管 fail-closed + 滑动刷新 - POST/GET /api/v1/admin/tenant-scope endpoint - logout + sync_user token_version+1 两处钩子接入(AC-10/11) - Celery admin_scope_cleanup 10min 巡检(AC-13 / AD-07) - 前端 controllers/API/admin.ts axios 封装(供 F020 useAdminScope 消费) - 配置:multi_tenant.admin_scope_ttl_seconds=14400,beat_schedule 注册 - audit_log action:admin.scope_switch(F011 TenantAuditAction 枚举扩展) - RedisClient 加 attl(key) helper(通用基础设施) - AC-12 通过中间件 _check_is_global_super 每请求再校验兜底;clear_on_role_revoke 方法就位但无公开 API 挂接点(见 role-revoke-hook.md) Tests: 6 files / ~33 用例(service 10 / api 9 / middleware 8 / logout 2 / sync_user 3 / celery 4),pending 114 pytest 验证。
…coded /opt/bisheng Collection error when running pytest from /opt/bisheng-f019 — the old fallback preferred /opt/bisheng (which exists but is a different checkout). Now the path is purely relative to the test file, so it works under any checkout root.
bisheng.user.api.user has pre-existing import-chain brittleness in the test env (not F019-specific). Rather than battling it for a 4-line hook, verify the wiring via AST source-level assertions (hook call present, try/except wrapper, local import) + keep a Service behaviour test that asserts the DEL against a FakeRedis. End-to-end HTTP check remains in ac-verification.md §10 as a manual 114 QA step.
- ac-verification: F019 37/37 绿 + F012 71 回归 + F011/F013 53 回归; 其他 fail 列均与 baseline 一致(预存 flakiness) - tasks.md: 实现状态改为 ✅ 已完成 - 实际偏差: T04 FakeRedis / T08 AST 检查 / T10 方案 2 / T11 路径修复 / attl 新增 / F013 is_global_super gap 临时补丁
…feedback) The endpoint returns a raw dict via resp_200(); the class was never wired into response_model=. Deleting to avoid dead code.
全局超管管理视图切换(Redis 4h 滑动,非 JWT,仅管理类 API 生效)。 INV-T14 落地;F020 LLM 多租户隔离的前置依赖解锁。 - 后端:/api/v1/admin/tenant-scope POST/GET + AdminScopeMiddleware + TenantScopeService + Celery 10min 巡检 + audit_log (admin.scope_switch) - 钩子:logout / sync_user token_version+1 两处接入(AC-10/AC-11) - 错误码:19701/19702(模块 197) - 前端:controllers/API/admin.ts axios 封装(供 F020 消费) - 测试:37/37 绿(service 10 / api 9 / middleware 8 / logout 3 / sync_user 3 / celery 4);F012 回归 71/71 无损 Known gap: clear_on_role_revoke 方法就位但无公开 API 挂接点 (role-revoke-hook.md 记录),由中间件 _check_is_global_super 每请求再校验兜底 AC-12 可观察行为。
…ators Department-admin role listing was excluding tenant roles with a NULL scope and\nstill let in-scope admins edit or delete roles created by others. This\nchange keeps list visibility broad enough for the role-management view\nwhile separating mutation eligibility so updates, deletes, and menu\nchanges require creator ownership when creator metadata exists.\n\nConstraint: Existing deployments may not have populated role.create_user for every historical role\nRejected: Enforce creator-only without fallback | would break legacy environments with missing creator metadata\nConfidence: high\nScope-risk: moderate\nReversibility: clean\nDirective: Keep list visibility and mutation eligibility separate for department admins; they intentionally follow different rules\nTested: src/backend/.venv/bin/pytest src/backend/test/test_role_service.py -q; src/backend/.venv/bin/pytest src/backend/test/test_role_api.py -q; python -m py_compile src/backend/bisheng/database/models/role.py src/backend/bisheng/role/domain/services/role_service.py src/backend/test/test_role_service.py\nNot-tested: Manual UI verification in the role-management table
The rebased knowledge-space UI on feat/2.5.0 had lost complete permission-management wiring for files and folders. This restores the dedicated permission dialog entry, keeps share-link behavior intact, and allows users with ReBAC can_manage access to see the action without relying only on legacy admin role checks. Constraint: Must preserve existing share-link copy behavior while integrating with the updated feat/2.5.0 knowledge-space layout Rejected: Reuse the share button for permission management | would regress the existing copy-share workflow Confidence: high Scope-risk: narrow Directive: Keep file and folder permission entry visibility aligned with can_manage checks; do not narrow it back to isAdmin-only gating without verifying ReBAC behavior Tested: npx vite build (src/frontend/client) Not-tested: Manual permission grant/revoke flows against the deployed environment
The previous frontend rebase restored the permission dialog but still hid sub-resource actions behind space-level checks, let FileCard render an incomplete menu, and let department child-scope selections drift at submit time. This change makes permission entry visibility follow each resource's own grantable capability, keeps 403 handling inside the dialog instead of redirecting away, and preserves the selected include-children scope for department grants. Constraint: The current backend exposes grantable relation models and dialog APIs, but 4001 environment validation is blocked because Milvus is unavailable Rejected: Keep using knowledge_space can_manage as the shared gate for file and folder actions | breaks resource-scoped permission behavior Confidence: high Scope-risk: narrow Directive: When permission-entry visibility changes, derive it from the target resource's own capability surface rather than parent-space shortcuts Tested: npx vite build (src/frontend/client) Not-tested: Manual knowledge-space AI / permission flows in the 4001 environment while Milvus is down
…e path The knowledge-file ingest path was still routing document abstracts through the legacy title-extraction prompt shape, so uploads could persist a title-like string where the product and admin UI both frame this field as a summary. Separately, the platform config pages were normalizing empty prompt values into frontend defaults, which made blank backend state look configured and could silently write template text back on the next save. This change splits abstract generation onto an abstract-specific prompt path, keeps the knowledge-model abstract prompt attached to that path, and stops the platform config editors from injecting default prompt text when the backend value is intentionally empty. Constraint: Existing title-extraction callers in bisheng_langchain still depend on the legacy title prompt behavior Rejected: Reuse extract_title with more conditionals | keeps title and abstract semantics coupled and easy to regress Confidence: high Scope-risk: moderate Reversibility: clean Directive: Treat abstract generation and title extraction as separate behaviors; do not merge their fallback prompts again without covering both with tests Tested: src/backend/.venv/bin/python -m pytest src/backend/test/test_extract_info.py Tested: npm test (src/frontend/platform) Not-tested: Full frontend TypeScript check remains red due to unrelated pre-existing repo errors
The client was dropping freshly registered space files as soon as it reloaded the list, so uploads could appear and then vanish before the polling loop observed their waiting or processing states. This keeps newly registered files in the visible list until the normal refresh catches up, tightens duplicate detection to real name conflicts, and makes the file list react when the resolved space role changes. Constraint: Preserve the existing knowledge-space polling and upload flow on feat/2.5.0 Rejected: Force an immediate full reload after every upload | can still hide pending files when the refresh races role or status state Confidence: high Scope-risk: narrow Reversibility: clean Directive: Treat duplicate overwrite prompts only as name-conflict handling; parse failures must continue to surface as ordinary failed uploads Tested: npm run test:ci -- --runInBand src/pages/knowledge/hooks/useFileUpload.test.ts Tested: npm run build:ci Not-tested: Manual browser upload verification against http://192.168.106.120:3003/workspace/knowledge
Two different visibility gaps could hide newly uploaded space files from the UI. The space children DAO only treated an empty string as the root directory, so legacy rows with a NULL file_level_path vanished once the client refreshed from the backend. The client-side member view also defaulted to success-only filtering even though the knowledge-space API already exposes a broader non-failed status set.\n\nThis change makes root children queries treat NULL and "" as the same root path, adds regression coverage for that behavior, and aligns the member default filter with the existing API contract instead of hard-coding success-only results.\n\nConstraint: Historical knowledge-space files may persist NULL file_level_path values while newer uploads use an empty string\nConstraint: Joined-member browsing should continue hiding failed items by default without hiding in-flight files\nRejected: Normalize legacy rows with a data migration | higher rollout cost than a query-side compatibility fix\nConfidence: high\nScope-risk: narrow\nReversibility: clean\nDirective: Root-level knowledge-space queries must treat NULL and "" equivalently until storage is normalized everywhere\nTested: src/backend/.venv/bin/pytest src/backend/test/test_knowledge_space_file_dao.py -q\nTested: src/backend/.venv/bin/pytest src/backend/test/test_knowledge_space_service.py -q -k "add_file_initializes_file_owner_and_parent_tuples"\nTested: npm run check-imports (src/frontend/client)\nTested: npm run build:ci (src/frontend/client)\nNot-tested: Live browser verification against the shared environment
… links The client treated business-error responses from knowledge-space creation as successful payloads, which produced undefined space ids and follow-up requests against invalid routes. The preview page also rewrote absolute signed MinIO URLs, breaking otherwise valid links. On the backend, space children listing needed an exact-id fetch path after uploads, and a read-permission typo could crash the list endpoint. Constraint: API endpoints currently encode some business failures in a 200 response body, so the client must validate payload shape explicitly Rejected: Commit local dev proxy and embedding bootstrap tweaks | environment-specific and not suitable for shared history Confidence: high Scope-risk: moderate Reversibility: clean Directive: Keep knowledge-space create handlers validating both status_code and data.id until the API contract changes away from 200-with-error-body responses Tested: Local manual verification of knowledge-space create failure messaging, upload visibility after refresh, and preview page absolute URL handling Not-tested: Automated frontend/backend tests not run in this session
Model management had drifted into a super-admin-only path: the LLM write APIs used get_admin_user, the model page hard-coded user.role === 'admin', and finetune routes were not consistently bound to the model menu permission. This commit normalizes the model domain around the effective 'model' WEB_MENU permission so role-based access behaves as configured. Constraint: Existing role payloads expose menu capability through web_menu, not a dedicated frontend permission object Rejected: Keep super-admin-only checks for LLM writes | contradicts role-based model management expectations Rejected: Fix frontend gating only | backend LLM and finetune APIs would remain inconsistent and unsafe Confidence: high Scope-risk: moderate Reversibility: clean Directive: When adding model-domain routes or actions, gate them by the effective 'model' menu permission instead of user.role === 'admin' Tested: src/backend/.venv/bin/python -m pytest src/backend/test/test_llm_management_auth.py src/backend/test/test_finetune_model_auth.py Tested: npm test -- --run src/test/modelPermissions.test.ts Not-tested: full end-to-end verification of model and finetune pages against a live role-configured account
Cherry-picking the knowledge-space permission and upload visibility fixes onto upstream/2.5.0-PM required a small follow-up to reconcile frontend API exports and JSX structure after resolving conflicts. This keeps the intended behavior while restoring a clean client build. Constraint: The upstream baseline had evolved enough that conflict resolution left one missing shared status constant and two JSX structure mismatches in the client knowledge-space views Rejected: Fold these edits back into the previous cherry-picks by rewriting history | slower and riskier than an explicit follow-up fix on the temporary delivery branch Confidence: high Scope-risk: narrow Reversibility: clean Directive: After conflict-heavy cherry-picks in client knowledge-space files, always run a full client build before pushing Tested: src/backend/.venv/bin/python -m pytest src/backend/test/test_llm_management_auth.py src/backend/test/test_finetune_model_auth.py src/backend/test/test_knowledge_space_file_dao.py -q Tested: npm test -- --run src/test/modelPermissions.test.ts Tested: npm run build (src/frontend/client)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
将
2.5.0-PM分支合并到feat/2.5.0-beta1,包含 v2.5.0 权限改造 + 多租户 F001–F010 全部 Feature,以及后续修复与文档。规模:91 commits / 592 files / +75k 行变更。
Why
v2.5.0 的核心变更已在
2.5.0-PM上完成迭代与多轮 Review,需要进入 beta1 集成分支供测试与发版准备。How
主要 Feature(按 F 编号):
space_channel_member兼容)权限模型相关:
运维/CI:
Test
Related
docs/PRD/2.5 权限管理体系改造 PRD/features/v2.5.0/001-*~010-*