diff --git a/.env.example b/.env.example index aa8ef6a2..3346f0b5 100644 --- a/.env.example +++ b/.env.example @@ -108,6 +108,8 @@ MULTI_INTENT_U9_SEGMENT=false MULTI_INTENT_U10_MUTATE=false MULTI_INTENT_U11_DOCGOV=false MULTI_INTENT_U12_RUN_CONTROL=false +# 工作流作用域:strict 命中他流即拦截;weighted 按关系权重打分,验收通过后再开。 +WORKFLOW_SCOPE_MODE=strict # W2: shadow only. Do not set SEMANTIC_SUMMARY_REPLACE=true; REPLACE is unwired. SEMANTIC_SUMMARY_SHADOW=false SEMANTIC_SUMMARY_REPLACE=false diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 2bad3431..b89d5e98 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -123,6 +123,7 @@ services: MULTI_INTENT_U10_MUTATE: ${MULTI_INTENT_U10_MUTATE:-false} MULTI_INTENT_U11_DOCGOV: ${MULTI_INTENT_U11_DOCGOV:-false} MULTI_INTENT_U12_RUN_CONTROL: ${MULTI_INTENT_U12_RUN_CONTROL:-false} + WORKFLOW_SCOPE_MODE: ${WORKFLOW_SCOPE_MODE:-strict} # W2 PU6: shadow semantic summary only. REPLACE stays unwired and is not declared here. SEMANTIC_SUMMARY_SHADOW: ${SEMANTIC_SUMMARY_SHADOW:-false} PI_CHANGE_REPORT_NARRATIVE: ${PI_CHANGE_REPORT_NARRATIVE:-false} diff --git a/docker-compose.main.yml b/docker-compose.main.yml index a08bd1c3..6ca74df3 100644 --- a/docker-compose.main.yml +++ b/docker-compose.main.yml @@ -124,6 +124,7 @@ services: MULTI_INTENT_U10_MUTATE: ${MULTI_INTENT_U10_MUTATE:-false} MULTI_INTENT_U11_DOCGOV: ${MULTI_INTENT_U11_DOCGOV:-false} MULTI_INTENT_U12_RUN_CONTROL: ${MULTI_INTENT_U12_RUN_CONTROL:-false} + WORKFLOW_SCOPE_MODE: ${WORKFLOW_SCOPE_MODE:-strict} SEMANTIC_SUMMARY_SHADOW: ${SEMANTIC_SUMMARY_SHADOW:-false} PI_CHANGE_REPORT_NARRATIVE: ${PI_CHANGE_REPORT_NARRATIVE:-false} FAV_DEGRADED_READ: "false" diff --git a/docker-compose.test.yml b/docker-compose.test.yml index d8162095..2ed26c65 100644 --- a/docker-compose.test.yml +++ b/docker-compose.test.yml @@ -121,6 +121,7 @@ services: MULTI_INTENT_U10_MUTATE: ${MULTI_INTENT_U10_MUTATE:-false} MULTI_INTENT_U11_DOCGOV: ${MULTI_INTENT_U11_DOCGOV:-false} MULTI_INTENT_U12_RUN_CONTROL: ${MULTI_INTENT_U12_RUN_CONTROL:-false} + WORKFLOW_SCOPE_MODE: ${WORKFLOW_SCOPE_MODE:-strict} SEMANTIC_SUMMARY_SHADOW: ${SEMANTIC_SUMMARY_SHADOW:-false} PI_CHANGE_REPORT_NARRATIVE: ${PI_CHANGE_REPORT_NARRATIVE:-false} FAV_DEGRADED_READ: ${FAV_DEGRADED_READ:-false} diff --git a/internal/app/app.go b/internal/app/app.go index a089a90d..820a3eaa 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -38,6 +38,7 @@ import ( "chat-agent-system/internal/taskrun" "chat-agent-system/internal/traceimpact" "chat-agent-system/internal/workflowdef" + "chat-agent-system/internal/workflowscope" "chat-agent-system/internal/workspace" "github.com/jackc/pgx/v5/pgxpool" @@ -324,6 +325,7 @@ func sysadminFeatureValues(cfg *config.Config) func() map[string]string { "MULTI_INTENT_U10_MUTATE": strconv.FormatBool(pflags.U10Mutate), "MULTI_INTENT_U11_DOCGOV": strconv.FormatBool(pflags.U11DocGov), "MULTI_INTENT_U12_RUN_CONTROL": strconv.FormatBool(pflags.U12RunControl), + "WORKFLOW_SCOPE_MODE": string(workflowscope.CurrentMode()), "SEMANTIC_SUMMARY_SHADOW": strconv.FormatBool(sflags.Shadow), "SEMANTIC_SUMMARY_REPLACE": strconv.FormatBool(sflags.Replace), "PI_CHANGE_REPORT_NARRATIVE": strconv.FormatBool(docrevision.PIChangeReportNarrativeEnabled()), diff --git a/internal/config/compose_flag_declaration_test.go b/internal/config/compose_flag_declaration_test.go index 038a237d..1c6f958d 100644 --- a/internal/config/compose_flag_declaration_test.go +++ b/internal/config/compose_flag_declaration_test.go @@ -77,6 +77,15 @@ func TestComposeAppDeclaresSemanticSummaryAndNarrativeShadowFlags(t *testing.T) } } +func TestComposeAppDeclaresWorkflowScopeMode(t *testing.T) { + for _, name := range composeFilesForFlagCheck { + block := serviceBlockText(t, readComposeFile(t, name), "app") + if !strings.Contains(block, "WORKFLOW_SCOPE_MODE: ${WORKFLOW_SCOPE_MODE:-strict}") { + t.Errorf("%s: app service must declare WORKFLOW_SCOPE_MODE with strict default", name) + } + } +} + // E-6: main must explicitly disable demo-user seeding at the compose layer // (defense in depth; internal/auth/bootstrap.go already gates on cfg.Env=="main"). func TestComposeMainSeedDemoUsersExplicitFalse(t *testing.T) { diff --git a/internal/sysadmin/features.go b/internal/sysadmin/features.go index bdac3489..21444826 100644 --- a/internal/sysadmin/features.go +++ b/internal/sysadmin/features.go @@ -113,6 +113,7 @@ var featureCatalog = []featureSpec{ {key: "MULTI_INTENT_U10_MUTATE", label: "U10 变更适配器", group: "intentflow", fallback: "false", description: "低/中风险 mutate 动作适配器"}, {key: "MULTI_INTENT_U11_DOCGOV", label: "U11 文档治理适配器", group: "intentflow", fallback: "false", description: "docgov 动作适配器"}, {key: "MULTI_INTENT_U12_RUN_CONTROL", label: "U12 运行控制适配器", group: "intentflow", fallback: "false", description: "workflow/run-control 动作适配器"}, + {key: "WORKFLOW_SCOPE_MODE", label: "工作流作用域裁决", group: "intent", fallback: "strict", description: "文档命中裁决:strict 命中他流即拦截;weighted 按工作流关系权重打分"}, {key: "SEMANTIC_SUMMARY_SHADOW", label: "语义摘要影子", group: "ctxmgr", fallback: "false", description: "上下文语义摘要影子生成"}, {key: "SEMANTIC_SUMMARY_REPLACE", label: "语义摘要替换", group: "ctxmgr", fallback: "false", description: "语义摘要替换确定性叙事(保留位,未接线)"}, {key: "PI_CHANGE_REPORT_NARRATIVE", label: "PI 变更报告叙事", group: "docrevision", fallback: "false", description: "变更报告 PI 叙事增强;默认仅确定性回退"},