Rule Engine ↔ SCSAI Synchronization Architecture and Operations Manual
Last Updated: 2026-07-23
Applicable Code: server/core/SCSAI-rule-sync.js, server/core/rule-engine.js, server/core/capability-runtime.js, server/scripts/sync-rules-bidirectional.js, server.js (drift self-check)
1. Authoritative Source and Data Flow
- Authoritative Storage: SCSAI's BossAgent_Rule (2000+ entries, fields with prefix bossagent_*).
- Local Copy: The sciot_rules_v2 table in server/data/rule_engine.db (the database actually read by the runtime engine).
- Direction:
- SCSAI → Local: Read-only pull, using item_number (= local rule.id) for idempotent upsert.
- Local → SCSAI: Writeback (push), via SCSAIRuleSync.syncRuleToSCSAI, writes to BossAgent_Rule (no longer the deprecated BOS_RULE: Method).
⚠️ Historical pitfall (fixed, recorded to prevent regression)
- The old SCSAIRuleSync wrote rules into BOS_RULE: Method (a dead path no one reads). Now rewritten to write to BossAgent_Rule.
- The old version didn't pass SCSAIClient when creating the engine → the saveRule writeback was entirely skipped. Now capability-runtime.js passes this.SCSAIClient when creating the UnifiedRuleEngine.
2. Field Mapping (SCSAI BossAgent_Rule ↔ Local sciot_rules_v2)
| Local Column | SCSAI Attribute | Description |
|---|---|---|
| id | item_number | upsert key, consistent across both ends |
| name | name | |
| description | description | |
| scope | bossagent_scope | validate/repair/optimize/compare/generate/identify |
| item_type_name | bossagent_item_type | Applicable object class, * indicates universal |
| severity | bossagent_severity | info/warn/error |
| priority | bossagent_priority | integer |
| condition_script | bossagent_condition_script | TEXT, nullable |
| action_type | bossagent_action_type | suggest/auto_fix/... |
| action_config | bossagent_action_config | JSON string |
| action_script | bossagent_generated_aml | TEXT, nullable |
| is_active | bossagent_is_active | 0/1 |
| is_builtin | bossagent_is_builtin | 0/1 |
| source | bossagent_source | generated/builtin/inspect/biz/manual/SCSAI |
| version | bossagent_version | integer |
| tags | bossagent_tags | CSV or JSON string |
| category | bossagent_category | |
| SCSAI_id | id (@_id) | GUID of the SCSAI item, written back as proof |
Key normalization: _SCSAIVal()
Empty properties returned by SCSAI sendAML are not null, but objects {is_null:"1"}; reference properties may be {keyed_name:...}. Using value || null as a fallback directly in code doesn't work (objects are truthy), which passes the object into better-sqlite3 → binding fails with 'Too few parameter values' → entire batch is skipped (this was the real culprit behind the long-term 0 records in previous PULL).
_SCSAIVal(v) is responsible for normalization:
- Original null/undefined → null
- String/Number/Boolean → String
- Object: contains is_null → null; contains #text/keyed_name/value → take its value; others → JSON.stringify into TEXT column
3. Synchronization Script
server/scripts/sync-rules-bidirectional.js:
node server/scripts/sync-rules-bidirectional.js # 默认:pull(SCSAI → 本地)
node server/scripts/sync-rules-bidirectional.js --push # 本地 → SCSAI(仅 is_builtin=0)
node server/scripts/sync-rules-bidirectional.js --both # 先 pull 再 push
node server/scripts/sync-rules-bidirectional.js --dry # 只读漂移检查,不写任一端
- PULL:
SCSAIRuleSync.syncRulesFromSCSAI(SCSAIClient, engine),按item_numberupsert。maxRecords5000。 - PUSH:遍历本地
is_builtin = 0行,逐条syncRuleToSCSAI。id为 NULL 的行会被跳过(无合法 id 无法映射,非 bug)。 - 漂移自检:
server.js启动后约 15s 报告本地与 SCSAI 的item_number差异(只读 Warn,不写任一端)。
4. 规则生命周期(CRUD 是否回写)
| 操作位置 | 是否回写 SCSAI | 现状 |
|---|---|---|
| Server-side BossAgent_Rule add/edit/delete | Project only reads via get, never writes | Server changes → require manual pull locally to take effect |
| Local management interface saveRule | Yes, writes to BossAgent_Rule | Takes effect immediately when engine is created with SCSAIClient passed |
| Local management interface deleteRule | Yes (via SCSAIRuleSync) | Same as above |
| sync-rules-SCSAI-to-local.js | Read-only pull | Idempotent overwrite, will overwrite local modifications not uploaded to SCSAI |
Note: Pull uses item_number for idempotent overwrite. If local manual modifications exist but the corresponding item is missing on the SCSAI side, rerunning pull will not delete locally redundant rules, but will overwrite rules with the same name—local exclusive modifications risk being overwritten; it is recommended to push first.
5. Operational Key Points
- First/Periodic Alignment: Run --both for a bidirectional sync to converge.
- Confirm Sync Rate: Check the count of entries in sciot_rules_v2 where SCSAI_id is not null; normally this should be close to the total number of local non-garbage rules.
- Orphaned Rules: Local rules with id IS NULL cannot be pushed; assign them semantic IDs first (e.g., biz-repair-xxx-001).
- Drift Alert: Search for [RuleDrift] in startup logs; a WARN indicates divergence between the two ends, requiring --both to fix.
- Data Quality: action_config/tags must be valid JSON or CSV; action_config for inspect-type rules allows key:value text (the engine has special parsing).
- rule_engine.db is a runtime artifact and is excluded by .gitignore; it can be rebuilt from SCSAI at any time using --both.
6. Known Legacy Issues
- Local source=builtin rules (e.g., BUILTIN-, built-in biz-) are not written back to SCSAI (by design, SCSAI is the authoritative source; built-in rules should align with SCSAI).
- No automatic/scheduled sync; currently relies on startup drift self-check + manual --both. For persistent sync, add cron/scheduled tasks to call this script.
- Local copies of BossAgent_Prompt / BossAgent_Template / BossAgent_BizPrompt are still pulled read-only by template-sync-service.js; modifications via the management interface are not yet written back to SCSAI (a separate to-do).
BossAgents