4.6 KiB
BiSheng Architecture Constitution
The single source of truth for BiSheng's architectural laws — invariant across all features, never to be violated.
AGENTS.mdand every feature'sdesign.mdreference this file; they never copy it. Changing an implementation never requires editing this file (they point here).scripts/arch-guard.shis the machine-enforcement arm of this document: each RULE maps to a clause below (see the anchor table).- Violations are reported as BLOCKER during
/sdd-review design.- Change governance: editing this file requires PR review (a law change affects every feature). If a RULE is involved, sync the "→ Cx" note in
arch-guard.sh.- Last revised: 2026-06-05.
Anchor Table (clause ↔ arch-guard RULE)
| Clause | Law | arch-guard RULE | Severity |
|---|---|---|---|
| C1 | DDD layered call chain | RULE-1 / 2 / 3 / 4 / 5 | VIOLATION (RULE-3 is WARNING during migration) |
| C2 | Dual-DB compatibility (MySQL + DM8) | — (review + CI) | — |
| C3 | Multi-tenancy auto-injection | — (review) | — |
| C4 | Permission unified entry point | RULE-8 | VIOLATION |
| C5 | Error-code convention | — (review) | — |
| C6 | No hardcoded secrets | RULE-7 | WARNING |
| C7 | Frontend store must not call HTTP directly | RULE-6 | WARNING |
C1. DDD Layered Call Chain
Call chain — never skip layers: Router → Endpoint → Service → Repository → DB
- Never
import bisheng.database.models.*in endpoints — go through a Domain Service/DAO (RULE-3, WARNING during migration). - Never write ORM queries in Service; never add new DAO entry points for new features.
common/,core/must not importdomain/,api/(RULE-1).database/models/must not importdomain/(RULE-2).domain/models/must not importdomain/services/(RULE-4).- The API layer must not cross-import between modules (RULE-5).
C2. Dual-DB Compatibility (MySQL + DM8) ⚠️
Every new feature must work on both dialects. DM8 is not optional.
| ✅ Use | ❌ Never use |
|---|---|
dialect_helpers.JsonType |
sqlalchemy.JSON, mysql.JSON |
dialect_helpers.LargeText |
LONGTEXT, MEDIUMTEXT |
dialect_helpers.UPDATE_TIME_SERVER_DEFAULT |
ON UPDATE CURRENT_TIMESTAMP |
SQLAlchemy inspect() |
information_schema, DATABASE() |
| Explicit relational columns | JSON_EXTRACT / JSON_CONTAINS / JSON_SEARCH |
macOS: the DM8 driver (dmPython/dmAsync) is not installed (sys_platform != 'darwin').
DM8 is a development hard-requirement — always use dialect_helpers, never MySQL-only syntax. But DM8 compatibility is verified by a central regression run (pre-release / periodic, on Linux), not by per-feature CI gates — day-to-day it's held by this law + review, not by a per-PR DM8 test.
C3. Multi-Tenancy — Auto-Injected, Never Manual
Never write WHERE tenant_id = X manually. SQLAlchemy events handle it automatically for 23+ tables.
multi_tenant.enabled=false behaves identically to single-tenant (default tenant_id=1).
C4. Permissions — Unified Entry Point
from bisheng.permission.domain.services.permission_service import PermissionService
await PermissionService.check(...) # check access
await PermissionService.authorize(...) # write OpenFGA owner tuple on resource creation (required)
- Never query
role_accessdirectly for resource authorization (RULE-8 / historical invariant INV-T19, VIOLATION). - Resource creation must call
PermissionService.authorize(); failures go to thefailed_tuplesretry table. - Five-level short-circuit:
super_admin→ tenant mismatch deny → tenant admin → ReBAC (OpenFGA) → RBAC menu.
C5. Error-Code Convention
- 5-digit
MMMEE(3-digit module + 2-digit error), defined incommon/errcode/. - Module numbers: 100=server, 104=assistant, 105=flow, 106=user, 108=llm, 109=knowledge, 110=linsight, 120=workstation, 130=chat, 140=message, 150=tool, 180=knowledge_space.
C6. No Hardcoded Secrets (RULE-7)
No password / secret_key / api_key / access_token literals in code. Use config + Fernet encryption (passwords in config.yaml are Fernet-encrypted; never write plaintext).
C7. Frontend Store Must Not Call HTTP Directly (RULE-6)
A frontend store must not call HTTP directly — go through controllers/API/ (platform) or api/ (client).
All other frontend conventions (state library, UI library, path aliases, i18n, Toast, etc.) live in .claude/rules/platform-frontend.md and .claude/rules/client-frontend.md (see also AGENTS.md §4).