soloforge 1.4.11 → 1.4.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/claude_code/tools.d.ts +3 -0
- package/dist/adapters/claude_code/tools.d.ts.map +1 -1
- package/dist/adapters/claude_code/tools.js +321 -374
- package/dist/adapters/claude_code/tools.js.map +1 -1
- package/dist/engine/brainstorm_contract.d.ts +2 -0
- package/dist/engine/brainstorm_contract.d.ts.map +1 -1
- package/dist/engine/brainstorm_contract.js +11 -1
- package/dist/engine/brainstorm_contract.js.map +1 -1
- package/dist/engine/expand_pipeline.d.ts +121 -0
- package/dist/engine/expand_pipeline.d.ts.map +1 -0
- package/dist/engine/expand_pipeline.js +141 -0
- package/dist/engine/expand_pipeline.js.map +1 -0
- package/dist/engine/first_principles.d.ts +2 -0
- package/dist/engine/first_principles.d.ts.map +1 -1
- package/dist/engine/first_principles.js +14 -1
- package/dist/engine/first_principles.js.map +1 -1
- package/dist/engine/intent_expander.d.ts +3 -0
- package/dist/engine/intent_expander.d.ts.map +1 -1
- package/dist/engine/intent_expander.js +252 -306
- package/dist/engine/intent_expander.js.map +1 -1
- package/dist/engine/intent_route_scorer.d.ts +1 -1
- package/dist/engine/intent_route_scorer.d.ts.map +1 -1
- package/dist/engine/intent_route_scorer.js +18 -3
- package/dist/engine/intent_route_scorer.js.map +1 -1
- package/dist/engine/intent_router.d.ts +2 -2
- package/dist/engine/intent_router.d.ts.map +1 -1
- package/dist/engine/intent_router.js +28 -6
- package/dist/engine/intent_router.js.map +1 -1
- package/dist/engine/intent_signal_extractor.d.ts +1 -1
- package/dist/engine/intent_signal_extractor.d.ts.map +1 -1
- package/dist/engine/intent_signal_extractor.js +12 -0
- package/dist/engine/intent_signal_extractor.js.map +1 -1
- package/dist/engine/release_readiness_gate.d.ts.map +1 -1
- package/dist/engine/release_readiness_gate.js +38 -37
- package/dist/engine/release_readiness_gate.js.map +1 -1
- package/dist/engine/source_code_matcher.d.ts +53 -0
- package/dist/engine/source_code_matcher.d.ts.map +1 -0
- package/dist/engine/source_code_matcher.js +165 -0
- package/dist/engine/source_code_matcher.js.map +1 -0
- package/dist/engine/task_context.d.ts +5 -0
- package/dist/engine/task_context.d.ts.map +1 -1
- package/dist/engine/task_context.js +37 -5
- package/dist/engine/task_context.js.map +1 -1
- package/dist/engine/technology_decision.d.ts +2 -0
- package/dist/engine/technology_decision.d.ts.map +1 -1
- package/dist/engine/technology_decision.js +18 -1
- package/dist/engine/technology_decision.js.map +1 -1
- package/dist/engine/traceability.d.ts.map +1 -1
- package/dist/engine/traceability.js +4 -0
- package/dist/engine/traceability.js.map +1 -1
- package/dist/engine/workflow_contract_registry.d.ts.map +1 -1
- package/dist/engine/workflow_contract_registry.js +24 -0
- package/dist/engine/workflow_contract_registry.js.map +1 -1
- package/dist/knowledge/index_manager.d.ts +1 -0
- package/dist/knowledge/index_manager.d.ts.map +1 -1
- package/dist/knowledge/index_manager.js +15 -0
- package/dist/knowledge/index_manager.js.map +1 -1
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -125,21 +125,38 @@ function decodeIndexedJsonString(value) {
|
|
|
125
125
|
function parseContractObject(value, fieldName) {
|
|
126
126
|
if (value === undefined || value === null)
|
|
127
127
|
return {};
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
128
|
+
// 字符串 → 尝试 JSON 解析(可能被 MCP 客户端序列化为 JSON 字符串)
|
|
129
|
+
let parsed;
|
|
130
|
+
if (typeof value === "string") {
|
|
131
|
+
try {
|
|
132
|
+
parsed = JSON.parse(value);
|
|
133
|
+
}
|
|
134
|
+
catch (parseErr) {
|
|
135
|
+
return { error: `${fieldName} 的 JSON 解析失败: ${parseErr.message}。请检查 JSON 格式是否正确` };
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
parsed = value;
|
|
140
|
+
}
|
|
141
|
+
const candidate = decodeIndexedJsonString(parsed);
|
|
138
142
|
if (!candidate || typeof candidate !== "object" || Array.isArray(candidate)) {
|
|
139
|
-
return { error: `${fieldName}
|
|
143
|
+
return { error: `${fieldName} 必须是 JSON 对象(当前类型: ${Array.isArray(candidate) ? "array" : typeof candidate})。期望格式请参考 expected_schema` };
|
|
140
144
|
}
|
|
141
145
|
return { value: candidate };
|
|
142
146
|
}
|
|
147
|
+
/** 需要通过 parseContractObject 校验的契约字段列表 */
|
|
148
|
+
const CONTRACT_OBJECT_FIELDS = [
|
|
149
|
+
"architecture_decision_workshop", "decision_workshop",
|
|
150
|
+
"technology_decision_contract", "technology_decision_record",
|
|
151
|
+
"detail_discipline_contract", "provided_details",
|
|
152
|
+
"first_principles_frame", "brainstorm_session",
|
|
153
|
+
"design_artifact_pack", "ood_solid_summary",
|
|
154
|
+
"backend_implementation_work_package", "code_observability_work_package",
|
|
155
|
+
];
|
|
156
|
+
/** 需要规范化的字段 */
|
|
157
|
+
const NORMALIZED_FIELDS = {
|
|
158
|
+
decision_workshop: "decision_workshop",
|
|
159
|
+
};
|
|
143
160
|
// ── Zod Schema 定义 ──
|
|
144
161
|
const ClassifySchema = {
|
|
145
162
|
intent: z.string().describe("开发者意图描述"),
|
|
@@ -149,18 +166,18 @@ const ExpandSchema = {
|
|
|
149
166
|
task_id: z.string().describe("sf_classify 返回的任务 ID"),
|
|
150
167
|
clarification_answers: z.array(z.string()).optional().describe("对澄清问题的回答"),
|
|
151
168
|
input_material_confirmations: z.array(z.string()).optional().describe("已确认安全的输入材料路径列表"),
|
|
152
|
-
architecture_decision_workshop: z.unknown().optional().describe("
|
|
153
|
-
decision_workshop: z.unknown().optional().describe("
|
|
154
|
-
technology_decision_contract: z.unknown().optional().describe("
|
|
155
|
-
technology_decision_record: z.unknown().optional().describe("
|
|
156
|
-
detail_discipline_contract: z.unknown().optional().describe("
|
|
157
|
-
provided_details: z.unknown().optional().describe("
|
|
158
|
-
first_principles_frame: z.unknown().optional().describe("
|
|
159
|
-
brainstorm_session: z.unknown().optional().describe("
|
|
160
|
-
design_artifact_pack: z.unknown().optional().describe("
|
|
161
|
-
ood_solid_summary: z.unknown().optional().describe("
|
|
162
|
-
backend_implementation_work_package: z.unknown().optional().describe("
|
|
163
|
-
code_observability_work_package: z.unknown().optional().describe("
|
|
169
|
+
architecture_decision_workshop: z.record(z.unknown()).optional().describe("架构设计前六域决策记录。示例 {domains:[{domain:'backend',options:[{id:'opt-a',title:'...'}],recommended_option_id:'opt-a',user_confirmation_ref:'confirm:...'}],status:'confirmed'}"),
|
|
170
|
+
decision_workshop: z.record(z.unknown()).optional().describe("通用决策研讨合同。示例 {activated_packs:['delivery_validation'],domains:[{domain:'delivery_validation',options:[{id:'opt-a',title:'...'}],recommended_option_id:'opt-a',user_confirmation_ref:'confirm:...'}],status:'confirmed'}"),
|
|
171
|
+
technology_decision_contract: z.record(z.unknown()).optional().describe("技术决策主权契约。示例 {decision_id:'TECH-001',decision_scope:'architecture',options:[{option_id:'opt-a',title:'A',description:'...'}],recommended_option:'opt-a',rejected_options:[{option:'opt-b',reason:'...'}],evidence:[{claim:'...',supporting_facts:['...']}],falsification:['...'],failure_conditions:['...'],validation_plan:['...'],rollback_or_exit_plan:['...'],human_gate_required:true,human_gate_evidence:'confirm:...',executable_without_human:false}"),
|
|
172
|
+
technology_decision_record: z.record(z.unknown()).optional().describe("技术决策执行记录。示例 {decision_id:'TECH-001',selected_option:'opt-a',status:'approved',approved_by:'user',evidence_refs:['confirm:...']}"),
|
|
173
|
+
detail_discipline_contract: z.record(z.unknown()).optional().describe("细节纪律契约。示例 {contract_id:'detail-001',required_dimensions:['boundary','failure_path','rollback'],blocking_dimensions:['boundary'],status:'ready'}"),
|
|
174
|
+
provided_details: z.record(z.unknown()).optional().describe("细节纪律已提供证据。示例 {boundary:['只检查 readiness,不改代码'],failure_path:['unknown 时标记证据不足'],rollback:['无写入,无需回滚']}"),
|
|
175
|
+
first_principles_frame: z.record(z.unknown()).optional().describe("高影响任务第一性原理框架。示例 {fundamental_need:'...',known_facts:['...'],assumptions:['...'],candidate_solutions:[{solution_id:'s1',description:'...',complexity:'low'}],simplest_viable_solution:'s1',chosen_solution:'s1',falsification_questions:['...'],why_not_legacy_path:'...'}"),
|
|
176
|
+
brainstorm_session: z.record(z.unknown()).optional().describe("不确定项/方案选择会话。示例 {trigger:'uncertainty',options:[{option_id:'opt-a',title:'...',description:'...',pros:['...'],cons:['...']}],recommended_option_id:'opt-a',rejected_option_ids:['opt-b'],failure_conditions:['...'],brainstorm_status:'options_ready'}"),
|
|
177
|
+
design_artifact_pack: z.record(z.unknown()).optional().describe("设计产物包路径映射或复验状态。示例 {status:'implementation_ready',paths:{architecture_document:'docs/architecture/01-架构设计文档.md'},verification_refs:['verification:...']}"),
|
|
178
|
+
ood_solid_summary: z.record(z.unknown()).optional().describe("复杂编码任务 OOD/SOLID 摘要。示例 {status:'confirmed',responsibilities:['...'],interfaces:['...'],risks:['...'],evidence_refs:['...']}"),
|
|
179
|
+
backend_implementation_work_package: z.record(z.unknown()).optional().describe("后端接口实现工程工作包。示例 {status:'confirmed',api_boundary:['...'],transaction_boundary:['...'],error_contract:['...'],evidence_refs:['...']}"),
|
|
180
|
+
code_observability_work_package: z.record(z.unknown()).optional().describe("代码可维护性与可观测性工作包。示例 {status:'confirmed',logging_plan:['...'],comment_plan:['...'],sensitive_logging_policy:'redact',evidence_refs:['...']}"),
|
|
164
181
|
};
|
|
165
182
|
const VerifySchema = {
|
|
166
183
|
task_id: z.string().describe("任务 ID"),
|
|
@@ -178,7 +195,7 @@ const LearnSchema = {
|
|
|
178
195
|
};
|
|
179
196
|
const StatusSchema = {
|
|
180
197
|
task_id: z.string().optional().describe("任务 ID(不传则查当前任务)"),
|
|
181
|
-
action: z.enum(["current", "recent", "resume", "cancel", "archive_stale"]).optional().describe("操作类型"),
|
|
198
|
+
action: z.enum(["current", "recent", "resume", "cancel", "archive_stale", "retry_expand"]).optional().describe("操作类型"),
|
|
182
199
|
confirm: z.boolean().optional().describe("确认执行会写入状态的操作,例如 cancel/archive_stale/job_cancel"),
|
|
183
200
|
authorized: z.boolean().optional().describe("confirm 的兼容别名"),
|
|
184
201
|
job_id: z.string().optional().describe("Job ID(用于 job_status / job_resume / job_cancel)"),
|
|
@@ -511,7 +528,7 @@ export async function checkLocalAcceptanceGate(params) {
|
|
|
511
528
|
const HIGH_IMPACT_KEYWORDS = /架构|技术选型|数据库|框架|多端|跨模块|高影响|高风险|migration|技术路线|选型|rework/i;
|
|
512
529
|
const UNCERTAINTY_KEYWORDS = /不确定|方案选择|多选项|权衡|脑暴|brainstorm|多方案|选型|比较/i;
|
|
513
530
|
const DETAIL_INTENSIVE_KEYWORDS = /多步骤|复杂|跨模块|高风险|多端|分布式|微服务/i;
|
|
514
|
-
const LOW_RISK_ROUTES = new Set(["skip_chat", "direct_answer", "operation", "planning", "review", "source_extraction"]);
|
|
531
|
+
const LOW_RISK_ROUTES = new Set(["skip_chat", "direct_answer", "operation", "planning", "review", "source_extraction", "acceptance"]);
|
|
515
532
|
/** 技术选型/架构/数据库/框架/高影响决策 → 需 decision sovereignty gate */
|
|
516
533
|
export function requiresDecisionSovereignty(ctx, route) {
|
|
517
534
|
if (!ctx)
|
|
@@ -607,7 +624,7 @@ export async function checkDecisionSovereigntyGate(params) {
|
|
|
607
624
|
const decisionModule = await lazyTechnologyDecision();
|
|
608
625
|
const validation = decisionModule.validateTechnologyDecision(decisionContract);
|
|
609
626
|
if (!validation.valid) {
|
|
610
|
-
return { allowed: false, reason_zh: `技术选型决策校验失败: ${validation.violations.join("; ")}
|
|
627
|
+
return { allowed: false, reason_zh: `技术选型决策校验失败: ${validation.violations.join("; ")}`, expected_schema: validation.expected_schema };
|
|
611
628
|
}
|
|
612
629
|
// 有决策记录时评估执行状态
|
|
613
630
|
const decisionRecord = params.ctx.technology_decision_record;
|
|
@@ -672,7 +689,7 @@ export async function checkFirstPrinciplesGate(params) {
|
|
|
672
689
|
const fpModule = await lazyFirstPrinciples();
|
|
673
690
|
const validation = fpModule.validateFirstPrinciplesForHighImpact(fpFrame);
|
|
674
691
|
if (!validation.passed) {
|
|
675
|
-
return { allowed: false, reason_zh: `高影响任务缺第一性原理推理: ${validation.violations.join("; ")}`, failures: validation.violations };
|
|
692
|
+
return { allowed: false, reason_zh: `高影响任务缺第一性原理推理: ${validation.violations.join("; ")}`, failures: validation.violations, expected_schema: validation.expected_schema };
|
|
676
693
|
}
|
|
677
694
|
return { allowed: true };
|
|
678
695
|
}
|
|
@@ -695,7 +712,7 @@ export async function checkBrainstormGate(params) {
|
|
|
695
712
|
const brainstormModule = await lazyBrainstormContract();
|
|
696
713
|
const validation = brainstormModule.validateBrainstormSession(brainstormSession);
|
|
697
714
|
if (!validation.valid) {
|
|
698
|
-
return { allowed: false, reason_zh: `方案探索不完整: ${validation.violations.join("; ")}`, violations: validation.violations };
|
|
715
|
+
return { allowed: false, reason_zh: `方案探索不完整: ${validation.violations.join("; ")}`, violations: validation.violations, expected_schema: validation.expected_schema };
|
|
699
716
|
}
|
|
700
717
|
// 脑暴推荐永远不能自动执行 — 需 Decision Sovereignty human gate
|
|
701
718
|
const canAutoExec = brainstormModule.canBrainstormAutoExecute(brainstormSession);
|
|
@@ -1329,7 +1346,7 @@ export async function registerTools(server, deps) {
|
|
|
1329
1346
|
}
|
|
1330
1347
|
catch (err) {
|
|
1331
1348
|
const errorNextTools = name === "sf_expand"
|
|
1332
|
-
? ["sf_status", "sf_debug", "sf_governance_report"]
|
|
1349
|
+
? ["sf_expand", "sf_status", "sf_debug", "sf_governance_report"]
|
|
1333
1350
|
: contract.default_next_tools;
|
|
1334
1351
|
const errorForbiddenTools = name === "sf_expand"
|
|
1335
1352
|
? ["sf_verify", "sf_review", "sf_deliver", "sf_scaffold"]
|
|
@@ -1484,161 +1501,27 @@ export async function registerTools(server, deps) {
|
|
|
1484
1501
|
if (!ctx || !ctx.classification) {
|
|
1485
1502
|
return { result: { error: "任务不存在或尚未分类,请先调用 sf_classify" } };
|
|
1486
1503
|
}
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
const existingTechnologyDecisionContract = parseContractObject(ctx.technology_decision_contract, "technology_decision_contract");
|
|
1496
|
-
if (existingTechnologyDecisionContract.error) {
|
|
1497
|
-
return { result: { error: existingTechnologyDecisionContract.error, status: "invalid_input" } };
|
|
1498
|
-
}
|
|
1499
|
-
const existingTechnologyDecisionRecord = parseContractObject(ctx.technology_decision_record, "technology_decision_record");
|
|
1500
|
-
if (existingTechnologyDecisionRecord.error) {
|
|
1501
|
-
return { result: { error: existingTechnologyDecisionRecord.error, status: "invalid_input" } };
|
|
1502
|
-
}
|
|
1503
|
-
const existingDetailContract = parseContractObject(ctx.detail_discipline_contract, "detail_discipline_contract");
|
|
1504
|
-
if (existingDetailContract.error) {
|
|
1505
|
-
return { result: { error: existingDetailContract.error, status: "invalid_input" } };
|
|
1506
|
-
}
|
|
1507
|
-
const existingProvidedDetails = parseContractObject(ctx.provided_details, "provided_details");
|
|
1508
|
-
if (existingProvidedDetails.error) {
|
|
1509
|
-
return { result: { error: existingProvidedDetails.error, status: "invalid_input" } };
|
|
1510
|
-
}
|
|
1511
|
-
const existingFirstPrinciplesFrame = parseContractObject(ctx.first_principles_frame, "first_principles_frame");
|
|
1512
|
-
if (existingFirstPrinciplesFrame.error) {
|
|
1513
|
-
return { result: { error: existingFirstPrinciplesFrame.error, status: "invalid_input" } };
|
|
1514
|
-
}
|
|
1515
|
-
const existingBrainstormSession = parseContractObject(ctx.brainstorm_session, "brainstorm_session");
|
|
1516
|
-
if (existingBrainstormSession.error) {
|
|
1517
|
-
return { result: { error: existingBrainstormSession.error, status: "invalid_input" } };
|
|
1518
|
-
}
|
|
1519
|
-
const existingDesignArtifactPack = parseContractObject(ctx.design_artifact_pack, "design_artifact_pack");
|
|
1520
|
-
if (existingDesignArtifactPack.error) {
|
|
1521
|
-
return { result: { error: existingDesignArtifactPack.error, status: "invalid_input" } };
|
|
1522
|
-
}
|
|
1523
|
-
const existingOodSolidSummary = parseContractObject(ctx.ood_solid_summary, "ood_solid_summary");
|
|
1524
|
-
if (existingOodSolidSummary.error) {
|
|
1525
|
-
return { result: { error: existingOodSolidSummary.error, status: "invalid_input" } };
|
|
1526
|
-
}
|
|
1527
|
-
const existingBackendImplementationWorkPackage = parseContractObject(ctx.backend_implementation_work_package, "backend_implementation_work_package");
|
|
1528
|
-
if (existingBackendImplementationWorkPackage.error) {
|
|
1529
|
-
return { result: { error: existingBackendImplementationWorkPackage.error, status: "invalid_input" } };
|
|
1530
|
-
}
|
|
1531
|
-
const existingCodeObservabilityWorkPackage = parseContractObject(ctx.code_observability_work_package, "code_observability_work_package");
|
|
1532
|
-
if (existingCodeObservabilityWorkPackage.error) {
|
|
1533
|
-
return { result: { error: existingCodeObservabilityWorkPackage.error, status: "invalid_input" } };
|
|
1534
|
-
}
|
|
1535
|
-
if (existingArchitectureWorkshop.value)
|
|
1536
|
-
ctx.architecture_decision_workshop = existingArchitectureWorkshop.value;
|
|
1537
|
-
if (existingDecisionWorkshop.value) {
|
|
1538
|
-
ctx.decision_workshop = (await lazyDecisionWorkshop()).normalizeDecisionWorkshopContract(existingDecisionWorkshop.value, args.task_id) ?? existingDecisionWorkshop.value;
|
|
1539
|
-
}
|
|
1540
|
-
if (existingTechnologyDecisionContract.value)
|
|
1541
|
-
ctx.technology_decision_contract = existingTechnologyDecisionContract.value;
|
|
1542
|
-
if (existingTechnologyDecisionRecord.value)
|
|
1543
|
-
ctx.technology_decision_record = existingTechnologyDecisionRecord.value;
|
|
1544
|
-
if (existingDetailContract.value)
|
|
1545
|
-
ctx.detail_discipline_contract = existingDetailContract.value;
|
|
1546
|
-
if (existingProvidedDetails.value)
|
|
1547
|
-
ctx.provided_details = existingProvidedDetails.value;
|
|
1548
|
-
if (existingFirstPrinciplesFrame.value)
|
|
1549
|
-
ctx.first_principles_frame = existingFirstPrinciplesFrame.value;
|
|
1550
|
-
if (existingBrainstormSession.value)
|
|
1551
|
-
ctx.brainstorm_session = existingBrainstormSession.value;
|
|
1552
|
-
if (existingDesignArtifactPack.value)
|
|
1553
|
-
ctx.design_artifact_pack = existingDesignArtifactPack.value;
|
|
1554
|
-
if (existingOodSolidSummary.value)
|
|
1555
|
-
ctx.ood_solid_summary = existingOodSolidSummary.value;
|
|
1556
|
-
if (existingBackendImplementationWorkPackage.value)
|
|
1557
|
-
ctx.backend_implementation_work_package = existingBackendImplementationWorkPackage.value;
|
|
1558
|
-
if (existingCodeObservabilityWorkPackage.value)
|
|
1559
|
-
ctx.code_observability_work_package = existingCodeObservabilityWorkPackage.value;
|
|
1560
|
-
if (args.architecture_decision_workshop) {
|
|
1561
|
-
const parsed = parseContractObject(args.architecture_decision_workshop, "architecture_decision_workshop");
|
|
1562
|
-
if (parsed.error)
|
|
1563
|
-
return { result: { error: parsed.error, status: "invalid_input" } };
|
|
1564
|
-
ctx.architecture_decision_workshop = parsed.value;
|
|
1565
|
-
}
|
|
1566
|
-
if (args.decision_workshop) {
|
|
1567
|
-
const parsed = parseContractObject(args.decision_workshop, "decision_workshop");
|
|
1568
|
-
if (parsed.error)
|
|
1569
|
-
return { result: { error: parsed.error, status: "invalid_input" } };
|
|
1570
|
-
ctx.decision_workshop = (await lazyDecisionWorkshop()).normalizeDecisionWorkshopContract(parsed.value, args.task_id) ?? parsed.value;
|
|
1571
|
-
}
|
|
1572
|
-
if (args.technology_decision_contract) {
|
|
1573
|
-
const parsed = parseContractObject(args.technology_decision_contract, "technology_decision_contract");
|
|
1574
|
-
if (parsed.error)
|
|
1575
|
-
return { result: { error: parsed.error, status: "invalid_input" } };
|
|
1576
|
-
ctx.technology_decision_contract = parsed.value;
|
|
1577
|
-
}
|
|
1578
|
-
if (args.technology_decision_record) {
|
|
1579
|
-
const parsed = parseContractObject(args.technology_decision_record, "technology_decision_record");
|
|
1580
|
-
if (parsed.error)
|
|
1581
|
-
return { result: { error: parsed.error, status: "invalid_input" } };
|
|
1582
|
-
ctx.technology_decision_record = parsed.value;
|
|
1583
|
-
}
|
|
1584
|
-
if (args.detail_discipline_contract) {
|
|
1585
|
-
const parsed = parseContractObject(args.detail_discipline_contract, "detail_discipline_contract");
|
|
1586
|
-
if (parsed.error)
|
|
1587
|
-
return { result: { error: parsed.error, status: "invalid_input" } };
|
|
1588
|
-
ctx.detail_discipline_contract = parsed.value;
|
|
1589
|
-
}
|
|
1590
|
-
if (args.provided_details) {
|
|
1591
|
-
const parsed = parseContractObject(args.provided_details, "provided_details");
|
|
1592
|
-
if (parsed.error)
|
|
1593
|
-
return { result: { error: parsed.error, status: "invalid_input" } };
|
|
1594
|
-
ctx.provided_details = parsed.value;
|
|
1595
|
-
}
|
|
1596
|
-
if (args.first_principles_frame) {
|
|
1597
|
-
const parsed = parseContractObject(args.first_principles_frame, "first_principles_frame");
|
|
1598
|
-
if (parsed.error)
|
|
1599
|
-
return { result: { error: parsed.error, status: "invalid_input" } };
|
|
1600
|
-
ctx.first_principles_frame = parsed.value;
|
|
1601
|
-
}
|
|
1602
|
-
if (args.brainstorm_session) {
|
|
1603
|
-
const parsed = parseContractObject(args.brainstorm_session, "brainstorm_session");
|
|
1604
|
-
if (parsed.error)
|
|
1605
|
-
return { result: { error: parsed.error, status: "invalid_input" } };
|
|
1606
|
-
ctx.brainstorm_session = parsed.value;
|
|
1607
|
-
}
|
|
1608
|
-
if (args.design_artifact_pack) {
|
|
1609
|
-
const parsed = parseContractObject(args.design_artifact_pack, "design_artifact_pack");
|
|
1610
|
-
if (parsed.error)
|
|
1611
|
-
return { result: { error: parsed.error, status: "invalid_input" } };
|
|
1612
|
-
ctx.design_artifact_pack = parsed.value;
|
|
1613
|
-
}
|
|
1614
|
-
if (args.ood_solid_summary) {
|
|
1615
|
-
const parsed = parseContractObject(args.ood_solid_summary, "ood_solid_summary");
|
|
1616
|
-
if (parsed.error)
|
|
1617
|
-
return { result: { error: parsed.error, status: "invalid_input" } };
|
|
1618
|
-
ctx.ood_solid_summary = parsed.value;
|
|
1619
|
-
}
|
|
1620
|
-
if (args.backend_implementation_work_package) {
|
|
1621
|
-
const parsed = parseContractObject(args.backend_implementation_work_package, "backend_implementation_work_package");
|
|
1622
|
-
if (parsed.error)
|
|
1623
|
-
return { result: { error: parsed.error, status: "invalid_input" } };
|
|
1624
|
-
ctx.backend_implementation_work_package = parsed.value;
|
|
1625
|
-
}
|
|
1626
|
-
if (args.code_observability_work_package) {
|
|
1627
|
-
const parsed = parseContractObject(args.code_observability_work_package, "code_observability_work_package");
|
|
1504
|
+
// 合并校验 + 应用:ctx 已存储值 → args 覆盖值,统一解析和规范化
|
|
1505
|
+
let needsSave = false;
|
|
1506
|
+
for (const field of CONTRACT_OBJECT_FIELDS) {
|
|
1507
|
+
// args 优先,其次 ctx 已存储值
|
|
1508
|
+
const raw = args[field] ?? ctx[field];
|
|
1509
|
+
if (!raw)
|
|
1510
|
+
continue;
|
|
1511
|
+
const parsed = parseContractObject(raw, field);
|
|
1628
1512
|
if (parsed.error)
|
|
1629
1513
|
return { result: { error: parsed.error, status: "invalid_input" } };
|
|
1630
|
-
|
|
1514
|
+
if (parsed.value) {
|
|
1515
|
+
ctx[field] = field === "decision_workshop"
|
|
1516
|
+
? ((await lazyDecisionWorkshop()).normalizeDecisionWorkshopContract(parsed.value, args.task_id) ?? parsed.value)
|
|
1517
|
+
: parsed.value;
|
|
1518
|
+
needsSave = true;
|
|
1519
|
+
}
|
|
1631
1520
|
}
|
|
1632
|
-
if (
|
|
1633
|
-
|| args.technology_decision_contract || args.technology_decision_record
|
|
1634
|
-
|| args.detail_discipline_contract || args.provided_details
|
|
1635
|
-
|| args.first_principles_frame || args.brainstorm_session
|
|
1636
|
-
|| args.design_artifact_pack
|
|
1637
|
-
|| args.ood_solid_summary || args.backend_implementation_work_package || args.code_observability_work_package) {
|
|
1521
|
+
if (needsSave)
|
|
1638
1522
|
await taskContext.save(ctx);
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
if (ctx.status === "classifying" || ctx.status === "clarifying") {
|
|
1523
|
+
// 状态守卫:classifying/expanding/clarifying/failed → expanding
|
|
1524
|
+
if (ctx.status === "classifying" || ctx.status === "clarifying" || ctx.status === "failed") {
|
|
1642
1525
|
await taskContext.updateStatus(args.task_id, "expanding");
|
|
1643
1526
|
ctx.status = "expanding";
|
|
1644
1527
|
}
|
|
@@ -1699,6 +1582,7 @@ export async function registerTools(server, deps) {
|
|
|
1699
1582
|
plan_context: planContext,
|
|
1700
1583
|
input_material_confirmations: args.input_material_confirmations,
|
|
1701
1584
|
task_id: args.task_id,
|
|
1585
|
+
brainstorm_session: ctx.brainstorm_session ?? undefined,
|
|
1702
1586
|
});
|
|
1703
1587
|
}
|
|
1704
1588
|
catch (expandErr) {
|
|
@@ -1951,223 +1835,231 @@ export async function registerTools(server, deps) {
|
|
|
1951
1835
|
}
|
|
1952
1836
|
}
|
|
1953
1837
|
}
|
|
1954
|
-
// ── S4 gates:
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
return {
|
|
1973
|
-
result: {
|
|
1974
|
-
error: detailGate.reason_zh,
|
|
1975
|
-
missing_details: detailGate.missing_details,
|
|
1976
|
-
status: "blocked",
|
|
1977
|
-
recovery: "多步骤/复杂任务缺 detail_discipline_contract,请提供后重新 sf_expand",
|
|
1978
|
-
},
|
|
1979
|
-
};
|
|
1838
|
+
// ── S4 gates: 轻量路径(acceptance/review/read_only)整体跳过 ──
|
|
1839
|
+
const isLightweightRoute = expansionRoute === "acceptance" || expansionRoute === "review"
|
|
1840
|
+
|| expansionRoute === "direct_answer" || expansionRoute === "analysis";
|
|
1841
|
+
if (!isLightweightRoute) {
|
|
1842
|
+
// ── S4 gates: 独立于 hasInstructionContract,由 requires* 触发判定 ──
|
|
1843
|
+
// 问题十六: 技术选型决策主权 gate — 高影响技术决策缺证据时 blocked
|
|
1844
|
+
{
|
|
1845
|
+
const decisionGate = await checkDecisionSovereigntyGate({ ctx, route: expansionRoute });
|
|
1846
|
+
if (!decisionGate.allowed) {
|
|
1847
|
+
return {
|
|
1848
|
+
result: {
|
|
1849
|
+
error: decisionGate.reason_zh,
|
|
1850
|
+
status: "blocked",
|
|
1851
|
+
expected_schema: decisionGate.expected_schema,
|
|
1852
|
+
recovery: decisionGate.requires_human_gate ? "请提供技术决策确认证据(human_gate_evidence)" : "技术选型决策校验失败,请按照 expected_schema 格式提供 technology_decision_contract",
|
|
1853
|
+
},
|
|
1854
|
+
};
|
|
1855
|
+
}
|
|
1980
1856
|
}
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
}
|
|
1993
|
-
}
|
|
1857
|
+
// 问题二十六: 细节纪律 gate — 复杂任务缺关键细节时 blocked
|
|
1858
|
+
{
|
|
1859
|
+
const detailGate = await checkDetailDisciplineGate({ ctx, route: expansionRoute });
|
|
1860
|
+
if (!detailGate.allowed) {
|
|
1861
|
+
return {
|
|
1862
|
+
result: {
|
|
1863
|
+
error: detailGate.reason_zh,
|
|
1864
|
+
missing_details: detailGate.missing_details,
|
|
1865
|
+
status: "blocked",
|
|
1866
|
+
recovery: "多步骤/复杂任务缺 detail_discipline_contract,请提供后重新 sf_expand",
|
|
1867
|
+
},
|
|
1868
|
+
};
|
|
1869
|
+
}
|
|
1994
1870
|
}
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
1871
|
+
// 问题二十七: 第一性原理 gate — 高影响任务缺推理时 blocked
|
|
1872
|
+
{
|
|
1873
|
+
const fpGate = await checkFirstPrinciplesGate({ ctx, route: expansionRoute });
|
|
1874
|
+
if (!fpGate.allowed) {
|
|
1875
|
+
return {
|
|
1876
|
+
result: {
|
|
1877
|
+
error: fpGate.reason_zh,
|
|
1878
|
+
failures: fpGate.failures,
|
|
1879
|
+
expected_schema: fpGate.expected_schema,
|
|
1880
|
+
status: "blocked",
|
|
1881
|
+
recovery: "高影响任务缺 first_principles_frame,请按照 expected_schema 格式提供后重新 sf_expand",
|
|
1882
|
+
},
|
|
1883
|
+
};
|
|
1884
|
+
}
|
|
2008
1885
|
}
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
const traceReport = isImplementationRoute
|
|
2024
|
-
? traceabilityModule.auditDesignImplementationTraceability(projectPath)
|
|
2025
|
-
: {
|
|
2026
|
-
passed: true,
|
|
2027
|
-
matrix_exists: false,
|
|
2028
|
-
findings: [],
|
|
2029
|
-
checked_files: [],
|
|
2030
|
-
matrix_path: traceabilityModule.DEFAULT_DESIGN_IMPLEMENTATION_TRACEABILITY_PATH,
|
|
2031
|
-
};
|
|
2032
|
-
if (isImplementationRoute && !traceReport.passed) {
|
|
2033
|
-
ctx.traceability_binding = {
|
|
2034
|
-
requirement_ids: [],
|
|
2035
|
-
prototype_ids: [],
|
|
2036
|
-
architecture_ids: [],
|
|
2037
|
-
detail_design_ids: [],
|
|
2038
|
-
phase_ids: [],
|
|
2039
|
-
slice_ids: [],
|
|
2040
|
-
acceptance_ids: [],
|
|
2041
|
-
status: "needs_backfill",
|
|
2042
|
-
findings: traceReport.findings.map((finding) => `${finding.code}: ${finding.message_zh}`),
|
|
2043
|
-
};
|
|
2044
|
-
await taskContext.save(ctx);
|
|
2045
|
-
return {
|
|
2046
|
-
result: {
|
|
2047
|
-
error: "需求/原型/设计/切片/验收追踪链路未闭合,不得进入编码实现",
|
|
2048
|
-
status: "blocked",
|
|
2049
|
-
diagnostic_code: TOOL_DIAGNOSTIC_CODES.traceabilityMissing,
|
|
2050
|
-
traceability_findings: traceReport.findings,
|
|
2051
|
-
checked_files: traceReport.checked_files,
|
|
2052
|
-
matrix_path: traceReport.matrix_path,
|
|
2053
|
-
template_path: traceabilityModule.DESIGN_IMPLEMENTATION_TRACEABILITY_TEMPLATE_PATH,
|
|
2054
|
-
recovery_command: traceabilityModule.DESIGN_IMPLEMENTATION_TRACEABILITY_RECOVERY_COMMAND,
|
|
2055
|
-
recovery: `${traceabilityModule.DESIGN_IMPLEMENTATION_TRACEABILITY_RECOVERY_GUIDANCE}。不得取消任务后直接写代码,不得改用 Bash 绕过追踪矩阵。`,
|
|
2056
|
-
},
|
|
2057
|
-
};
|
|
1886
|
+
// 问题四十二(brainstorm): 脑暴契约 gate — 不确定项缺方案探索时 blocked
|
|
1887
|
+
{
|
|
1888
|
+
const brainstormGate = await checkBrainstormGate({ ctx, route: expansionRoute });
|
|
1889
|
+
if (!brainstormGate.allowed) {
|
|
1890
|
+
return {
|
|
1891
|
+
result: {
|
|
1892
|
+
error: brainstormGate.reason_zh,
|
|
1893
|
+
violations: brainstormGate.violations,
|
|
1894
|
+
expected_schema: brainstormGate.expected_schema,
|
|
1895
|
+
status: "blocked",
|
|
1896
|
+
recovery: "不确定项/方案选择缺 brainstorm_session,请按照 expected_schema 格式提供后重新 sf_expand",
|
|
1897
|
+
},
|
|
1898
|
+
};
|
|
1899
|
+
}
|
|
2058
1900
|
}
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
1901
|
+
// 问题六十六/六十七/六十八: 编码前工程约束必须聚合返回。
|
|
1902
|
+
// 不能让 OOD 或后端工程门禁抢先返回,遮蔽用户项目的注释/日志契约。
|
|
1903
|
+
{
|
|
1904
|
+
const preImplementationBlocks = [];
|
|
1905
|
+
const preImplementationPayload = {};
|
|
1906
|
+
const artifactDesignIntent = expansionRoute === "artifact_generation"
|
|
1907
|
+
|| ["architecture_design", "database_design", "api_design", "detailed_design"].includes(String(workflowIntent))
|
|
1908
|
+
|| /(?:架构|数据库|API|接口|OpenAPI).{0,12}(?:设计|规格|文档)|(?:设计|规格|文档).{0,12}(?:API|接口|OpenAPI)/i.test(ctx.intent);
|
|
1909
|
+
const isImplementationRoute = !artifactDesignIntent && (expansionRoute === "code_change"
|
|
1910
|
+
|| String(workflowIntent) === "backend_api_implementation"
|
|
1911
|
+
|| String(workflowIntent) === "frontend_business_logic"
|
|
1912
|
+
|| /编码|实现|开发|controller|service|component/i.test(ctx.intent));
|
|
1913
|
+
const traceabilityModule = await lazyTraceability();
|
|
1914
|
+
const traceReport = isImplementationRoute
|
|
1915
|
+
? traceabilityModule.auditDesignImplementationTraceability(projectPath)
|
|
1916
|
+
: {
|
|
1917
|
+
passed: true,
|
|
1918
|
+
matrix_exists: false,
|
|
1919
|
+
findings: [],
|
|
1920
|
+
checked_files: [],
|
|
1921
|
+
matrix_path: traceabilityModule.DEFAULT_DESIGN_IMPLEMENTATION_TRACEABILITY_PATH,
|
|
1922
|
+
};
|
|
1923
|
+
if (isImplementationRoute && !traceReport.passed) {
|
|
2063
1924
|
ctx.traceability_binding = {
|
|
2064
|
-
requirement_ids:
|
|
2065
|
-
prototype_ids:
|
|
2066
|
-
architecture_ids:
|
|
2067
|
-
detail_design_ids:
|
|
2068
|
-
phase_ids:
|
|
2069
|
-
slice_ids:
|
|
2070
|
-
acceptance_ids:
|
|
2071
|
-
status: "
|
|
2072
|
-
findings:
|
|
1925
|
+
requirement_ids: [],
|
|
1926
|
+
prototype_ids: [],
|
|
1927
|
+
architecture_ids: [],
|
|
1928
|
+
detail_design_ids: [],
|
|
1929
|
+
phase_ids: [],
|
|
1930
|
+
slice_ids: [],
|
|
1931
|
+
acceptance_ids: [],
|
|
1932
|
+
status: "needs_backfill",
|
|
1933
|
+
findings: traceReport.findings.map((finding) => `${finding.code}: ${finding.message_zh}`),
|
|
2073
1934
|
};
|
|
2074
1935
|
await taskContext.save(ctx);
|
|
2075
1936
|
return {
|
|
2076
1937
|
result: {
|
|
2077
|
-
error: "
|
|
2078
|
-
status: "
|
|
2079
|
-
diagnostic_code: TOOL_DIAGNOSTIC_CODES.
|
|
2080
|
-
|
|
1938
|
+
error: "需求/原型/设计/切片/验收追踪链路未闭合,不得进入编码实现",
|
|
1939
|
+
status: "blocked",
|
|
1940
|
+
diagnostic_code: TOOL_DIAGNOSTIC_CODES.traceabilityMissing,
|
|
1941
|
+
traceability_findings: traceReport.findings,
|
|
1942
|
+
checked_files: traceReport.checked_files,
|
|
2081
1943
|
matrix_path: traceReport.matrix_path,
|
|
2082
1944
|
template_path: traceabilityModule.DESIGN_IMPLEMENTATION_TRACEABILITY_TEMPLATE_PATH,
|
|
2083
|
-
recovery_command:
|
|
2084
|
-
recovery:
|
|
1945
|
+
recovery_command: traceabilityModule.DESIGN_IMPLEMENTATION_TRACEABILITY_RECOVERY_COMMAND,
|
|
1946
|
+
recovery: `${traceabilityModule.DESIGN_IMPLEMENTATION_TRACEABILITY_RECOVERY_GUIDANCE}。不得取消任务后直接写代码,不得改用 Bash 绕过追踪矩阵。`,
|
|
2085
1947
|
},
|
|
2086
1948
|
};
|
|
2087
1949
|
}
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
1950
|
+
const traceBindingIds = traceabilityModule.extractTraceabilityBindingIds(ctx);
|
|
1951
|
+
if (traceReport.matrix_exists && isImplementationRoute) {
|
|
1952
|
+
const missingPrefixes = ["PHASE", "SLICE", "DD", "AC"].filter((prefix) => !traceBindingIds.some((id) => id.startsWith(`${prefix}-`)));
|
|
1953
|
+
if (missingPrefixes.length > 0) {
|
|
1954
|
+
ctx.traceability_binding = {
|
|
1955
|
+
requirement_ids: traceBindingIds.filter((id) => id.startsWith("REQ-")),
|
|
1956
|
+
prototype_ids: traceBindingIds.filter((id) => id.startsWith("PROTO-")),
|
|
1957
|
+
architecture_ids: traceBindingIds.filter((id) => id.startsWith("ARCH-")),
|
|
1958
|
+
detail_design_ids: traceBindingIds.filter((id) => id.startsWith("DD-")),
|
|
1959
|
+
phase_ids: traceBindingIds.filter((id) => id.startsWith("PHASE-")),
|
|
1960
|
+
slice_ids: traceBindingIds.filter((id) => id.startsWith("SLICE-")),
|
|
1961
|
+
acceptance_ids: traceBindingIds.filter((id) => id.startsWith("AC-")),
|
|
1962
|
+
status: "missing",
|
|
1963
|
+
findings: missingPrefixes.map((prefix) => `编码前缺少 ${prefix}-* 绑定`),
|
|
1964
|
+
};
|
|
1965
|
+
await taskContext.save(ctx);
|
|
1966
|
+
return {
|
|
1967
|
+
result: {
|
|
1968
|
+
error: "编码任务必须先绑定阶段、切片、详细设计和验收 ID",
|
|
1969
|
+
status: "awaiting_traceability_binding",
|
|
1970
|
+
diagnostic_code: TOOL_DIAGNOSTIC_CODES.traceabilityBindingMissing,
|
|
1971
|
+
missing_prefixes: missingPrefixes,
|
|
1972
|
+
matrix_path: traceReport.matrix_path,
|
|
1973
|
+
template_path: traceabilityModule.DESIGN_IMPLEMENTATION_TRACEABILITY_TEMPLATE_PATH,
|
|
1974
|
+
recovery_command: "sf_expand",
|
|
1975
|
+
recovery: "在任务上下文或指令中明确本次实现对应的 PHASE-*、SLICE-*、DD-*、AC-*,再重新调用 sf_expand;不得脱离已确认设计直接编码,不得取消任务后改用 Bash/Edit/Write 直接实现",
|
|
1976
|
+
},
|
|
1977
|
+
};
|
|
1978
|
+
}
|
|
1979
|
+
ctx.traceability_binding = {
|
|
1980
|
+
requirement_ids: traceBindingIds.filter((id) => id.startsWith("REQ-")),
|
|
1981
|
+
prototype_ids: traceBindingIds.filter((id) => id.startsWith("PROTO-")),
|
|
1982
|
+
architecture_ids: traceBindingIds.filter((id) => id.startsWith("ARCH-")),
|
|
1983
|
+
detail_design_ids: traceBindingIds.filter((id) => id.startsWith("DD-")),
|
|
1984
|
+
phase_ids: traceBindingIds.filter((id) => id.startsWith("PHASE-")),
|
|
1985
|
+
slice_ids: traceBindingIds.filter((id) => id.startsWith("SLICE-")),
|
|
1986
|
+
acceptance_ids: traceBindingIds.filter((id) => id.startsWith("AC-")),
|
|
1987
|
+
status: "bound",
|
|
1988
|
+
findings: [],
|
|
1989
|
+
};
|
|
1990
|
+
preImplementationPayload.traceability_binding = ctx.traceability_binding;
|
|
1991
|
+
}
|
|
1992
|
+
const oodModule = await lazyOodSolid();
|
|
1993
|
+
const oodGate = oodModule.evaluateOodDesignGate({
|
|
1994
|
+
task_id: args.task_id,
|
|
1995
|
+
intent: ctx.intent,
|
|
1996
|
+
route: expansionRoute,
|
|
1997
|
+
summary: ctx.ood_solid_summary,
|
|
2133
1998
|
});
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
1999
|
+
if (oodGate.applicable) {
|
|
2000
|
+
ctx.ood_solid_summary = oodGate.required_summary;
|
|
2001
|
+
preImplementationPayload.ood_solid_summary = oodGate.required_summary;
|
|
2002
|
+
}
|
|
2003
|
+
if (!oodGate.allowed) {
|
|
2004
|
+
preImplementationBlocks.push({
|
|
2005
|
+
code: TOOL_DIAGNOSTIC_CODES.oodMissingSummary,
|
|
2006
|
+
reason_zh: oodGate.reason_zh,
|
|
2007
|
+
});
|
|
2008
|
+
}
|
|
2009
|
+
const backendModule = await lazyBackendImplementation();
|
|
2010
|
+
const backendGate = backendModule.evaluateBackendImplementationGate({
|
|
2138
2011
|
task_id: args.task_id,
|
|
2139
2012
|
intent: ctx.intent,
|
|
2140
2013
|
route: expansionRoute,
|
|
2141
|
-
|
|
2142
|
-
work_package: ctx.code_observability_work_package,
|
|
2014
|
+
work_package: ctx.backend_implementation_work_package,
|
|
2143
2015
|
});
|
|
2144
|
-
if (
|
|
2145
|
-
ctx.
|
|
2146
|
-
preImplementationPayload.
|
|
2016
|
+
if (backendGate.applicable) {
|
|
2017
|
+
ctx.backend_implementation_work_package = backendGate.work_package;
|
|
2018
|
+
preImplementationPayload.backend_implementation_work_package = backendGate.work_package;
|
|
2147
2019
|
}
|
|
2148
|
-
if (!
|
|
2020
|
+
if (!backendGate.allowed) {
|
|
2149
2021
|
preImplementationBlocks.push({
|
|
2150
|
-
code: TOOL_DIAGNOSTIC_CODES.
|
|
2151
|
-
reason_zh:
|
|
2022
|
+
code: TOOL_DIAGNOSTIC_CODES.backendMissingSummary,
|
|
2023
|
+
reason_zh: backendGate.reason_zh,
|
|
2152
2024
|
});
|
|
2153
2025
|
}
|
|
2026
|
+
if (ctx.classification.task_type !== "scaffold") {
|
|
2027
|
+
const obsModule = await lazyCodeObservability();
|
|
2028
|
+
const obsGate = obsModule.evaluateCodeObservabilityGate({
|
|
2029
|
+
task_id: args.task_id,
|
|
2030
|
+
intent: ctx.intent,
|
|
2031
|
+
route: expansionRoute,
|
|
2032
|
+
changed_files: ctx.execution?.changed_files,
|
|
2033
|
+
work_package: ctx.code_observability_work_package,
|
|
2034
|
+
});
|
|
2035
|
+
if (obsGate.applicable) {
|
|
2036
|
+
ctx.code_observability_work_package = obsGate.work_package;
|
|
2037
|
+
preImplementationPayload.code_observability_work_package = obsGate.work_package;
|
|
2038
|
+
}
|
|
2039
|
+
if (!obsGate.allowed) {
|
|
2040
|
+
preImplementationBlocks.push({
|
|
2041
|
+
code: TOOL_DIAGNOSTIC_CODES.codeObservabilityWorkPackage,
|
|
2042
|
+
reason_zh: obsGate.reason_zh,
|
|
2043
|
+
});
|
|
2044
|
+
}
|
|
2045
|
+
}
|
|
2046
|
+
if (Object.keys(preImplementationPayload).length > 0) {
|
|
2047
|
+
await taskContext.save(ctx);
|
|
2048
|
+
}
|
|
2049
|
+
if (preImplementationBlocks.length > 0) {
|
|
2050
|
+
return {
|
|
2051
|
+
result: {
|
|
2052
|
+
error: "编码前工程契约未全部确认,不得开始实现",
|
|
2053
|
+
status: "awaiting_implementation_contracts",
|
|
2054
|
+
diagnostic_codes: preImplementationBlocks.map((b) => b.code),
|
|
2055
|
+
blocking_findings: preImplementationBlocks,
|
|
2056
|
+
...preImplementationPayload,
|
|
2057
|
+
recovery: "请同时确认 OOD/SOLID、后端工程边界、代码注释与日志契约后重新调用 sf_expand",
|
|
2058
|
+
},
|
|
2059
|
+
};
|
|
2060
|
+
}
|
|
2154
2061
|
}
|
|
2155
|
-
|
|
2156
|
-
await taskContext.save(ctx);
|
|
2157
|
-
}
|
|
2158
|
-
if (preImplementationBlocks.length > 0) {
|
|
2159
|
-
return {
|
|
2160
|
-
result: {
|
|
2161
|
-
error: "编码前工程契约未全部确认,不得开始实现",
|
|
2162
|
-
status: "awaiting_implementation_contracts",
|
|
2163
|
-
diagnostic_codes: preImplementationBlocks.map((b) => b.code),
|
|
2164
|
-
blocking_findings: preImplementationBlocks,
|
|
2165
|
-
...preImplementationPayload,
|
|
2166
|
-
recovery: "请同时确认 OOD/SOLID、后端工程边界、代码注释与日志契约后重新调用 sf_expand",
|
|
2167
|
-
},
|
|
2168
|
-
};
|
|
2169
|
-
}
|
|
2170
|
-
}
|
|
2062
|
+
} // end if (!isLightweightRoute) — 轻量路径跳过 S4 gates + 编码前工程约束
|
|
2171
2063
|
// Input Material Contract: 处理 hard-blocking 结果
|
|
2172
2064
|
// Privacy Gate: 隐私/敏感信息策略阻断
|
|
2173
2065
|
const isBlocked = expansion.prompt.startsWith("## 阻塞:输入材料禁止读取")
|
|
@@ -2321,7 +2213,6 @@ export async function registerTools(server, deps) {
|
|
|
2321
2213
|
// 存储膨胀结果
|
|
2322
2214
|
try {
|
|
2323
2215
|
await taskContext.setExpansion(args.task_id, expansion);
|
|
2324
|
-
// 存储产物记录
|
|
2325
2216
|
if (expansion.output_artifact_record) {
|
|
2326
2217
|
await taskContext.setArtifact(args.task_id, expansion.output_artifact_record);
|
|
2327
2218
|
}
|
|
@@ -2356,6 +2247,46 @@ export async function registerTools(server, deps) {
|
|
|
2356
2247
|
}
|
|
2357
2248
|
throw updateErr;
|
|
2358
2249
|
}
|
|
2250
|
+
// 设置 plan_proposal_gate — 必须在 setExpansion/setArtifact/updateStatus 之后
|
|
2251
|
+
// 这些操作各自 load-save 会覆盖之前的写入,所以 plan_proposal_gate 必须最后设置
|
|
2252
|
+
{
|
|
2253
|
+
const { evaluatePlanProposalGate, inferPlanGateTaskType } = await import("../../engine/plan_proposal_gate.js");
|
|
2254
|
+
const routeForGate = expansion.workflow_trace?.route ?? expansionRoute ?? "code_change";
|
|
2255
|
+
const taskTypeForGate = inferPlanGateTaskType(ctx.intent);
|
|
2256
|
+
const isLightweight = routeForGate === "acceptance" || routeForGate === "review"
|
|
2257
|
+
|| routeForGate === "direct_answer" || routeForGate === "analysis";
|
|
2258
|
+
try {
|
|
2259
|
+
if (isLightweight) {
|
|
2260
|
+
await taskContext.setPlanProposalGate(args.task_id, {
|
|
2261
|
+
passed: true,
|
|
2262
|
+
plan_gate_status: "passed",
|
|
2263
|
+
required_level: "brief_plan",
|
|
2264
|
+
reason_zh: "轻量路径自动通过",
|
|
2265
|
+
violations: [],
|
|
2266
|
+
evidence_refs: [],
|
|
2267
|
+
blocked_actions: [],
|
|
2268
|
+
});
|
|
2269
|
+
}
|
|
2270
|
+
else {
|
|
2271
|
+
const gateResult = evaluatePlanProposalGate({
|
|
2272
|
+
task_id: args.task_id,
|
|
2273
|
+
task_type: taskTypeForGate,
|
|
2274
|
+
user_intent: ctx.intent,
|
|
2275
|
+
plan_level: "execution_plan",
|
|
2276
|
+
plan_summary_zh: ctx.intent.slice(0, 200),
|
|
2277
|
+
proposed_steps: [expansionRoute ?? "execute"],
|
|
2278
|
+
verification_plan: (expansion.acceptance?.automated ?? []).map((a) => a.description ?? String(a)),
|
|
2279
|
+
risks: [],
|
|
2280
|
+
evidence_refs: expansion.matched_knowledge ?? [],
|
|
2281
|
+
});
|
|
2282
|
+
await taskContext.setPlanProposalGate(args.task_id, gateResult);
|
|
2283
|
+
}
|
|
2284
|
+
}
|
|
2285
|
+
catch (gateErr) {
|
|
2286
|
+
// gate 设置失败不阻断主流程,后续写工具会重新检查
|
|
2287
|
+
console.error(`[sf_expand] plan_proposal_gate 设置失败: ${gateErr.message}`);
|
|
2288
|
+
}
|
|
2289
|
+
}
|
|
2359
2290
|
const expandPayload = Object.keys(advisories).length > 0
|
|
2360
2291
|
? { ...expansion, advisories }
|
|
2361
2292
|
: expansion;
|
|
@@ -3160,6 +3091,22 @@ export async function registerTools(server, deps) {
|
|
|
3160
3091
|
result: { task_id: args.task_id, cancelled },
|
|
3161
3092
|
};
|
|
3162
3093
|
}
|
|
3094
|
+
case "retry_expand": {
|
|
3095
|
+
if (!args.task_id) {
|
|
3096
|
+
return {
|
|
3097
|
+
result: { error: "重试 expand 需要 task_id" },
|
|
3098
|
+
};
|
|
3099
|
+
}
|
|
3100
|
+
const retried = await taskContext.retryExpand(args.task_id);
|
|
3101
|
+
if (!retried) {
|
|
3102
|
+
return {
|
|
3103
|
+
result: { error: "任务不存在、非 failed 状态或缺少 classification,无法重试 expand", task_id: args.task_id },
|
|
3104
|
+
};
|
|
3105
|
+
}
|
|
3106
|
+
return {
|
|
3107
|
+
result: { task_id: args.task_id, retried: true, hint: "任务已恢复到 expanding 状态,可重新调用 sf_expand" },
|
|
3108
|
+
};
|
|
3109
|
+
}
|
|
3163
3110
|
case "archive_stale": {
|
|
3164
3111
|
const stale = await detectStaleCurrentTask(taskContext.getStateDir());
|
|
3165
3112
|
const taskId = args.task_id ?? stale.task_id;
|