feat(trust): ADR-088 Trust Score 持久化 — L4 自動放行核心
All checks were successful
CD Pipeline / build-and-deploy (push) Successful in 10m40s

TrustScoreManager 從記憶體升級為 PostgreSQL 持久化,
Pod 重啟後信任分數不再歸零,AI 能真正累積到 L4 自動放行門檻。

變更:
- migrations/adr088_trust_score_persistence.sql: trust_records 表
- db/models.py: TrustRecordDB ORM model
- repositories/interfaces.py: ITrustRepository Protocol
- repositories/trust_repository.py: PG upsert ON CONFLICT DO UPDATE
- services/trust_engine.py: bulk_load() 啟動 warm-up
- services/learning_service.py: _persist_trust() + 2 call sites
- main.py: 啟動時 load_all() → bulk_load()

流程: 批准 5 次 → score=5 寫入 DB → Pod 重啟 → warm-up 讀回
      → evaluate_adjusted_risk MEDIUM→LOW → 自動執行

2026-04-17 ogt + Claude Sonnet 4.6(亞太): ADR-088

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
OG T
2026-04-17 16:14:33 +08:00
parent 148d59a0e4
commit 9d6aa7ea45
7 changed files with 353 additions and 1 deletions

View File

@@ -1175,3 +1175,56 @@ class AiGovernanceEvent(Base):
Index("ix_ai_governance_triggered_at", "triggered_at"),
Index("ix_ai_governance_resolved", "resolved"),
)
# =============================================================================
# TrustRecordDB - ADR-088 TrustScore 持久化
# =============================================================================
class TrustRecordDB(Base):
"""
Trust Score 持久化記錄
ADR-088: TrustScoreManager 從記憶體升級為 PostgreSQL 持久化。
Pod 重啟後分數不歸零AI 能真正累積信任達到 L4 自動放行。
score >= 5: MEDIUM → LOW (自動執行)
score >= 10: HIGH → MEDIUM (降一級)
2026-04-17 ogt + Claude Sonnet 4.6(亞太): Phase 4 信任持久化
"""
__tablename__ = "trust_records"
action_pattern: Mapped[str] = mapped_column(
String(255), primary_key=True,
comment="操作模式,例如 delete:nginx-frontend-*"
)
score: Mapped[int] = mapped_column(
Integer, nullable=False, default=0,
comment="累積信任分數。+1/approvereject 歸零"
)
total_approvals: Mapped[int] = mapped_column(
Integer, nullable=False, default=0,
)
total_rejections: Mapped[int] = mapped_column(
Integer, nullable=False, default=0,
)
last_approval_by: Mapped[str | None] = mapped_column(String(100), nullable=True)
last_approval_at: Mapped[datetime | None] = mapped_column(
DateTime(timezone=True), nullable=True,
)
last_rejection_by: Mapped[str | None] = mapped_column(String(100), nullable=True)
last_rejection_at: Mapped[datetime | None] = mapped_column(
DateTime(timezone=True), nullable=True,
)
created_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), nullable=False, default=taipei_now,
)
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), nullable=False, default=taipei_now, onupdate=taipei_now,
)
__table_args__ = (
Index("ix_trust_records_score", "score"),
Index("ix_trust_records_updated", "updated_at"),
)