Files
awoooi/docs/awooop/inventory/INV-7-pr-cutting-plan.md
Your Name 8629ac709b
Some checks failed
run-migration / migrate (push) Failing after 59s
Code Review / ai-code-review (push) Successful in 1m8s
Type Sync Check / check-type-sync (push) Successful in 2m27s
feat(awooop): Phase 1-8 完整實作 — AwoooP Agent Platform 六平面架構
## Phase 1-3: Control Plane + Contract System
- awooop_phase1_control_plane_2026-05-04.sql: 12 張核心表 + RLS
- awooop_phase1_batch1_rls_2026-05-04.sql: 全部 FORCE RLS + GRANT
- packages/awooop-contracts/: 六合約 JSON Schema + golden fixtures
- src/models/awooop_contracts.py: Pydantic v2 contract models(extra=forbid)
- src/repositories/contract_repository.py: contract lifecycle(draft→published→active)
- src/services/contract_service.py: HMAC publish sig + Redis multi-sig activate
- src/services/schema_validator.py: LLM output validator(retry×3, E-SCHEMA-001)

## Phase 2: Tenant Isolation
- awooop_phase2_budget_ledger_2026-05-04.sql: budget_ledger + RLS
- src/services/budget_service.py: Token Budget Hard Kill 三層防線
- src/core/context.py: PROJECT_ID ContextVar(31 background loop 自動繼承)
- src/db/base.py + models.py: project_id 欄位 + RLS set_config 注入
- src/hermes/nl_gateway.py: project_id Redis key 前綴(Phase A 雙寫)
- src/services/anomaly_counter.py: per-project 改造(Phase A fallback)

## Phase 4: Platform Shell in Shadow Mode
- awooop_phase4_run_state_2026-05-04.sql: run_state + step_journal + idempotency
- src/services/run_state_machine.py: 8-state FSM + SKIP LOCKED + stale reaper
- src/services/platform_runtime.py: UUID v7 + W3C trace_id + shadow_execute
- src/services/audit_sink.py: PII/secret redaction 9 patterns
- src/api/v1/platform/runs.py: POST/GET /v1/platform/runs(Router→Service 架構)
- src/workers/platform_worker.py: SKIP LOCKED worker + heartbeat + reaper loop
- src/main.py: platform router + lifespan worker start/stop

## Phase 5: MCP Gateway 五閘門
- awooop_phase5_mcp_gateway_2026-05-04.sql: 4 表 + RLS
- src/plugins/mcp/gateway.py: McpGateway(Gate 1~5, E-MCP-GATE-001~009)
- src/plugins/mcp/redaction_middleware.py: 雙層 redaction + 16K 截斷
- src/plugins/mcp/registry.py: __provider name mangling(ADR-116)
- src/plugins/mcp/credential_resolver.py: k8s secret ref 解析
- tests/test_mcp_credential_isolation.py: 10 個迴歸測試(secret leak 防再現)

## Phase 6-8: EwoooC + Channel Hub + Approval Token
- awooop_phase6_ewoooc_onboarding_2026-05-04.sql: ewoooc tenant + 4 read-only MCP tools
- awooop_phase7_channel_hub_2026-05-04.sql: conversation_event + outbound_message
- src/services/provider_proxy.py: ProviderProxy + PlatformEnvelope(ADR-115)
- src/services/channel_hub.py: Telegram inbound mirror + Progressive Feedback(30s)
- src/services/awooop_approval_token.py: HS256 + jti NX replay 防護 + suggest mode

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-04 19:31:53 +08:00

8.0 KiB
Raw Blame History

INV-7: PR Cutting Plan

版本v1.0 初稿 日期2026-05-03台北 範圍Phase 2 全部重構 PR 的切割方案refactor-specialist 設計) 用途:避免大 PR 難以 review每個 PR 獨立可合併、有 rollback 能力


原則

  1. 每個 PR 必須可獨立合併(不依賴後 PR 才能運行)
  2. 每個 PR 有 rollback 方式revert commit 或 feature flag
  3. PR-01~05 無依賴可並行PR-06+ 有順序依賴

PR 清單

PR-01: telemetry.py hardcoded IP assert 移除

範圍apps/api/src/core/telemetry.py:71(約 1-3 行) 前置:無 解決P0-08 EwoooC 啟動失敗

# 修改前
if "192.168.0.188" not in endpoint:
    raise ValueError(f"Forbidden OTEL endpoint: {endpoint}")

# 修改後
allowed = [e.strip() for e in settings.ALLOWED_TELEMETRY_ENDPOINTS.split(",")]
if not any(a in endpoint for a in allowed):
    raise ValueError(f"Forbidden OTEL endpoint: {endpoint}")
# 同步在 config.py 加入 ALLOWED_TELEMETRY_ENDPOINTS = "192.168.0.188"default

reviewvuln-verifier 風險:低(僅移除一個 IP assert


PR-02: decision_manager.py silence key 常數化

範圍apps/api/src/services/decision_manager.py:240(約 2 行) 前置:無 解決P1-24 兩處定義不一致

# 修改前decision_manager.py:240
silence_key = f"telegram_silence:{target}"

# 修改後
from apps.api.src.services.telegram_gateway import SILENCE_KEY_PREFIX
silence_key = f"{SILENCE_KEY_PREFIX}{target}"

reviewdebugger確認 SILENCE_KEY_PREFIX = "telegram_silence:",功能等效) 風險:低


PR-03: ollama_auto_recovery.py 第二定義移除

範圍apps/api/src/services/ollama_auto_recovery.py:230(約 5 行) 前置:需確認 INV-1 中 ollama:current_primary 的所有寫入點 解決P0-11 GCP 三層拓撲遷移必裂

# 移除 ollama_auto_recovery.py 中第二個 ollama:current_primary 寫入點
# 統一從 config.py 或 ollama_failover_manager.py 讀取,不在 auto_recovery 自行決定 primary

reviewtool-expert確認 GCP 拓撲三層 failover 邏輯) 風險:中(影響 Ollama failover 路徑)


PR-04: registry.py _provider__provider

範圍apps/api/src/plugins/mcp/registry.py:24-71(約 20 行) 前置:無 解決P1-05 _provider public 可繞過 audit

# 修改所有 self._provider → self.__provider
# 加 @property provider 讀取(需要時)
# 加 unit test:
#   with pytest.raises(AttributeError):
#       registry.__provider  # double underscore = name mangling

reviewvuln-verifier 風險Python name mangling只影響直接反射存取


PR-05: mcp_bridge.py namespace 動態化

範圍apps/api/src/plugins/mcp/mcp_bridge.py:592,602,631,647,681(約 30 行) 前置Phase 1 schema 未完成前,先改為 from settings fallback 解決P0-13 EwoooC K8s tool 無法使用

# Phase 1 前的過渡做法PR-05a
DEFAULT_NAMESPACE = os.getenv("K8S_DEFAULT_NAMESPACE", "awoooi-prod")
namespace = parameters.get("namespace", DEFAULT_NAMESPACE)

# Phase 2 後的完整做法PR-05b需 Phase 1 schema 先完成):
namespace = parameters.get("namespace",
    get_project_contract(project_id).allowed_k8s_namespaces[0])

reviewtool-expert 風險:低(有 env fallback功能等效


PR-06: consensus_engine.py CONSENSUS_PREFIX 加 project 前綴

範圍apps/api/src/services/consensus_engine.py(約 15 行) 前置Redis 雙寫 Phase A 已完成(舊 consensus: key 已雙寫新 key 解決P0-12 consensus engine 多租戶跨 tenant 共用

# 修改前
CONSENSUS_PREFIX = "consensus:"

# 修改後consensus engine 接受 project_id 參數)
def get_consensus_prefix(project_id: str) -> str:
    return f"{project_id}:consensus:"

reviewdb-expert確認 consensus 語義正確、debuggerregression 風險:中(需 Redis 雙寫先完成)


PR-07: security hardeningnonce + webhook replay + approval policy

範圍

  • apps/api/src/services/security_interceptor.py:451-490nonce 重設計)
  • apps/api/src/api/v1/webhooks.py:679-728webhook timestamp/nonce
  • apps/api/src/services/decision_manager.pyrequires_approval policy-derived 前置ADR-116 已 Accepted規格凍結 解決P0-04、P0-05、P0-063 個 PoC 確認漏洞)
# security_interceptor.py: nonce 必須 HMAC(server_secret + nonce)
# webhooks.py: 加 X-Timestamp header 驗證 ±5min window + Redis NX nonce
# decision_manager.py: requires_approval 從 policy contract 讀,禁止 LLM output 決定

reviewvuln-verifierPoC 重跑驗證修補有效) 風險:高(安全修補,必須 PoC 驗證)


PR-08: Repository project_id filter 批次 1

範圍

  • IncidentRepositoryincidents 表)
  • PlaybookRepositoryplaybooks 表)
  • KnowledgeRepositoryknowledge_entries 表) 前置Phase 1 schema migration 完成project_id 欄位已存在) 解決P0-03 30+ 業務表無 project_id filter
# 每個 repository 方法加 project_id 參數
# 例:
async def get_incident(self, incident_id: str, project_id: str) -> Incident:
    return await self.session.execute(
        select(Incident)
        .where(Incident.incident_id == incident_id)
        .where(Incident.project_id == project_id)  # 新增
    )

reviewdb-expertquery plan 確認) 風險:中(約 200 行;需 regression test


PR-09: Repository project_id filter 批次 2

範圍

  • McpAuditRepository(加 trace_id + project_id
  • AiDecisionRepository(加 run_id + project_id
  • ApprovalRepository(加 trace_id + project_id 前置PR-08 合併後 解決P0-03 繼續

reviewdb-expert + debuggertrace_id 貫穿驗證) 風險:中


PR-10: Background loop 標記ADR-123

範圍apps/api/src/main.py31 個 loop約 150 行) 前置ADR-123 已 Accepted 解決P0-07 31 個 loop 無 project_id

# 每個 loop 的 context 加上標記
# platform_internal: asyncio.create_task 前設定 contextvars
# legacy_awoooi_default: fallback project_id = 'awoooi'

async def _run_with_project_context(coro, project_id: str):
    token = project_id_var.set(project_id)
    try:
        await coro
    finally:
        project_id_var.reset(token)

reviewcritic確認標記正確 風險:中(影響所有 background loop 的 logging context


PR-11: AnomalyCounter per-project 改造

範圍apps/api/src/services/anomaly_counter.py:790(約 80 行) 前置PR-10 完成loop 有 project_id context 後才能傳入) 解決P1-17 AnomalyCounter 全域單例

# 修改前全域單例6 個 prefix 無 tenant
_anomaly_counter = AnomalyCounter(get_redis())

# 修改後per-project 工廠
def get_anomaly_counter(project_id: str) -> AnomalyCounter:
    return AnomalyCounter(get_redis(), project_prefix=f"{project_id}:anomaly")

reviewdebugger確認 6 個 prefix 全數改造) 風險:中


執行順序圖

並行群組 G-D無依賴可同時開
  PR-01, PR-02, PR-03, PR-04, PR-05

↓ Phase 1 Schema 完成後

並行群組 G-E部分並行
  PR-07安全修補優先
  PR-06需 Redis 雙寫 Phase A
  PR-08需 Phase 1 schema

↓ PR-08 合併後

  PR-09依賴 PR-08

↓ ADR-123 Accepted 後

  PR-10background loop 標記)

↓ PR-10 合併後

  PR-11AnomalyCounter

驗收標準

  • PR-01~05 全部合併無依賴Phase 0 完成後即可)
  • PR-07 通過 vuln-verifier PoC 重跑驗證
  • PR-08~09 通過 db-expert reviewquery plan 確認)
  • PR-10~11 通過 regression test31 個 loop logging context 正確)

最後更新2026-05-03台北