U4 第五步:AssetProvider 端口注入,移除 squad→template 依赖 #8
@@ -50,6 +50,7 @@ imports:
|
|||||||
- squad
|
- squad
|
||||||
- storage
|
- storage
|
||||||
- stream
|
- stream
|
||||||
|
- template
|
||||||
- textextract
|
- textextract
|
||||||
- workflowdef
|
- workflowdef
|
||||||
- workspace
|
- workspace
|
||||||
@@ -288,7 +289,6 @@ imports:
|
|||||||
- agent
|
- agent
|
||||||
- config
|
- config
|
||||||
- stream
|
- stream
|
||||||
- template
|
|
||||||
- textextract
|
- textextract
|
||||||
- workflowdef
|
- workflowdef
|
||||||
storage:
|
storage:
|
||||||
|
|||||||
@@ -211,6 +211,7 @@ func NewService(cfg *config.Config) (*Service, error) {
|
|||||||
return agent.NewBackend(name, cfg)
|
return agent.NewBackend(name, cfg)
|
||||||
},
|
},
|
||||||
TaskPreparer: svc.squadTaskPreparer,
|
TaskPreparer: svc.squadTaskPreparer,
|
||||||
|
Assets: templateAssetProvider{},
|
||||||
})
|
})
|
||||||
svc.parts = Parts{
|
svc.parts = Parts{
|
||||||
Pool: pool,
|
Pool: pool,
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package bizdev
|
||||||
|
|
||||||
|
import (
|
||||||
|
"chat-agent-system/internal/squad"
|
||||||
|
"chat-agent-system/internal/template"
|
||||||
|
)
|
||||||
|
|
||||||
|
// templateAssetProvider 把内容资产端口映射到产品模板包(KTD1 装配侧):
|
||||||
|
// 模板与指令的内容、版本与磁盘/内嵌来源由产品侧全权决定。
|
||||||
|
type templateAssetProvider struct{}
|
||||||
|
|
||||||
|
func (templateAssetProvider) DocTemplate(taskKey string) (string, error) {
|
||||||
|
return template.GetDocTemplate(taskKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (templateAssetProvider) RenderDoc(tmpl string, vars map[string]string) string {
|
||||||
|
return template.RenderDoc(tmpl, vars)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (templateAssetProvider) AgentInstruction(role string) (string, error) {
|
||||||
|
return template.GetAgentInstruction(role)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (templateAssetProvider) AgentInstructionFile(file string) (string, error) {
|
||||||
|
return template.GetAgentInstructionFile(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
// compile-time port conformance
|
||||||
|
var _ squad.AssetProvider = templateAssetProvider{}
|
||||||
@@ -12,7 +12,7 @@ import (
|
|||||||
func TestContextManager_InjectAndFormat(t *testing.T) {
|
func TestContextManager_InjectAndFormat(t *testing.T) {
|
||||||
tmpDir := t.TempDir()
|
tmpDir := t.TempDir()
|
||||||
|
|
||||||
engine := squad.NewWorkspaceEngine()
|
engine := squad.NewWorkspaceEngine(nil)
|
||||||
ws, err := engine.PrepareWorkspace(squad.WorkspacePrepareRequest{
|
ws, err := engine.PrepareWorkspace(squad.WorkspacePrepareRequest{
|
||||||
WorkDir: tmpDir,
|
WorkDir: tmpDir,
|
||||||
TaskKey: "research",
|
TaskKey: "research",
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import (
|
|||||||
|
|
||||||
func TestPrepareWorkspaceWritesDialogueContext(t *testing.T) {
|
func TestPrepareWorkspaceWritesDialogueContext(t *testing.T) {
|
||||||
dir := t.TempDir()
|
dir := t.TempDir()
|
||||||
engine := squad.NewWorkspaceEngine()
|
engine := squad.NewWorkspaceEngine(nil)
|
||||||
ws, err := engine.PrepareWorkspace(squad.WorkspacePrepareRequest{
|
ws, err := engine.PrepareWorkspace(squad.WorkspacePrepareRequest{
|
||||||
WorkDir: dir,
|
WorkDir: dir,
|
||||||
TaskKey: "system_reqs",
|
TaskKey: "system_reqs",
|
||||||
@@ -104,7 +104,7 @@ func prepareDialogueWorkspace(t *testing.T, req squad.WorkspacePrepareRequest) s
|
|||||||
ID: "s1", Title: "整理", Role: "organizer", Mode: "write",
|
ID: "s1", Title: "整理", Role: "organizer", Mode: "write",
|
||||||
SubWorkspace: "tasks/01_organizer", Status: "pending",
|
SubWorkspace: "tasks/01_organizer", Status: "pending",
|
||||||
}}
|
}}
|
||||||
if _, err := squad.NewWorkspaceEngine().PrepareWorkspace(req); err != nil {
|
if _, err := squad.NewWorkspaceEngine(nil).PrepareWorkspace(req); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
return req.WorkDir
|
return req.WorkDir
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ func TestWorkflowDriver_DynamicAdjustmentIntegration(t *testing.T) {
|
|||||||
}
|
}
|
||||||
defer os.RemoveAll(tmpDir)
|
defer os.RemoveAll(tmpDir)
|
||||||
|
|
||||||
engine := NewWorkspaceEngine()
|
engine := NewWorkspaceEngine(nil)
|
||||||
driver := NewWorkflowDriver(engine)
|
driver := NewWorkflowDriver(engine)
|
||||||
evaluator := NewStepEvaluator()
|
evaluator := NewStepEvaluator()
|
||||||
driver.SetEvaluator(evaluator)
|
driver.SetEvaluator(evaluator)
|
||||||
|
|||||||
@@ -207,7 +207,7 @@ func TestDetectRepetition_NoFalsePositive(t *testing.T) {
|
|||||||
func prepareGovWorkspace(t *testing.T, steps []squad.StepSpec) string {
|
func prepareGovWorkspace(t *testing.T, steps []squad.StepSpec) string {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
dir := t.TempDir()
|
dir := t.TempDir()
|
||||||
engine := squad.NewWorkspaceEngine()
|
engine := squad.NewWorkspaceEngine(nil)
|
||||||
_, err := engine.PrepareWorkspace(squad.WorkspacePrepareRequest{
|
_, err := engine.PrepareWorkspace(squad.WorkspacePrepareRequest{
|
||||||
WorkDir: dir,
|
WorkDir: dir,
|
||||||
TaskKey: "intake",
|
TaskKey: "intake",
|
||||||
@@ -272,7 +272,7 @@ func TestWorkflowDriver_GovernanceOnExecute(t *testing.T) {
|
|||||||
{ID: "step_02", Role: "writer", Title: "成文", SubWorkspace: "tasks/02_draft"},
|
{ID: "step_02", Role: "writer", Title: "成文", SubWorkspace: "tasks/02_draft"},
|
||||||
}
|
}
|
||||||
dir := prepareGovWorkspace(t, steps)
|
dir := prepareGovWorkspace(t, steps)
|
||||||
driver := squad.NewWorkflowDriver(squad.NewWorkspaceEngine())
|
driver := squad.NewWorkflowDriver(squad.NewWorkspaceEngine(nil))
|
||||||
|
|
||||||
res, err := driver.Execute(context.Background(), dir, &agent.StubBackend{}, squad.ExecuteOptions{Timeout: 10 * time.Second})
|
res, err := driver.Execute(context.Background(), dir, &agent.StubBackend{}, squad.ExecuteOptions{Timeout: 10 * time.Second})
|
||||||
if err != nil || !res.Success {
|
if err != nil || !res.Success {
|
||||||
@@ -324,7 +324,7 @@ func TestWorkflowDriver_BudgetHardStopKeepsPartialArchive(t *testing.T) {
|
|||||||
{ID: "step_03", Role: "leader", Title: "终审", SubWorkspace: "tasks/03_review"},
|
{ID: "step_03", Role: "leader", Title: "终审", SubWorkspace: "tasks/03_review"},
|
||||||
}
|
}
|
||||||
dir := prepareGovWorkspace(t, steps)
|
dir := prepareGovWorkspace(t, steps)
|
||||||
driver := squad.NewWorkflowDriver(squad.NewWorkspaceEngine())
|
driver := squad.NewWorkflowDriver(squad.NewWorkspaceEngine(nil))
|
||||||
|
|
||||||
// 预算收紧为 2 步 → 第 3 步触发硬停
|
// 预算收紧为 2 步 → 第 3 步触发硬停
|
||||||
tight := squad.BudgetPolicy{MaxSteps: 2, MaxRepairRounds: 1, MaxAgentCalls: 10}
|
tight := squad.BudgetPolicy{MaxSteps: 2, MaxRepairRounds: 1, MaxAgentCalls: 10}
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ func (b *failFirstBackend) Execute(ctx context.Context, req agent.Request) (*age
|
|||||||
func prepareDegradeWS(t *testing.T) (string, *squad.WorkflowDriver) {
|
func prepareDegradeWS(t *testing.T) (string, *squad.WorkflowDriver) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
tmpDir := t.TempDir()
|
tmpDir := t.TempDir()
|
||||||
engine := squad.NewWorkspaceEngine()
|
engine := squad.NewWorkspaceEngine(nil)
|
||||||
ws, err := engine.PrepareWorkspace(squad.WorkspacePrepareRequest{
|
ws, err := engine.PrepareWorkspace(squad.WorkspacePrepareRequest{
|
||||||
WorkDir: tmpDir,
|
WorkDir: tmpDir,
|
||||||
TaskKey: "degrade_test",
|
TaskKey: "degrade_test",
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"chat-agent-system/internal/template"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// PlannerRequest 任务规划请求
|
// PlannerRequest 任务规划请求
|
||||||
@@ -36,14 +35,15 @@ type PlannerResult struct {
|
|||||||
// AdaptivePlanner 自适应任务规划器
|
// AdaptivePlanner 自适应任务规划器
|
||||||
type AdaptivePlanner struct {
|
type AdaptivePlanner struct {
|
||||||
registry *RoleRegistry
|
registry *RoleRegistry
|
||||||
|
assets AssetProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewAdaptivePlanner 创建自适应任务规划器
|
// NewAdaptivePlanner 创建自适应任务规划器
|
||||||
func NewAdaptivePlanner(reg *RoleRegistry) *AdaptivePlanner {
|
func NewAdaptivePlanner(reg *RoleRegistry, assets AssetProvider) *AdaptivePlanner {
|
||||||
if reg == nil {
|
if reg == nil {
|
||||||
reg = NewRoleRegistry()
|
reg = NewRoleRegistry()
|
||||||
}
|
}
|
||||||
return &AdaptivePlanner{registry: reg}
|
return &AdaptivePlanner{registry: reg, assets: assets}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Plan 执行动态规划:区分固定工作流自适应微调 vs 非标文档自主规划
|
// Plan 执行动态规划:区分固定工作流自适应微调 vs 非标文档自主规划
|
||||||
@@ -157,8 +157,15 @@ func (p *AdaptivePlanner) planFixedAdaptive(req PlannerRequest) (*PlannerResult,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tmplContent, err := template.GetDocTemplate(req.TaskKey)
|
var tmplContent string
|
||||||
if err != nil {
|
if p.assets != nil {
|
||||||
|
var err error
|
||||||
|
tmplContent, err = p.assets.DocTemplate(req.TaskKey)
|
||||||
|
if err != nil {
|
||||||
|
tmplContent = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if strings.TrimSpace(tmplContent) == "" {
|
||||||
tmplContent = fmt.Sprintf("# %s\n\n## 1. 结论\n\n待确认\n\n## 2. 正文\n\n待确认\n", title)
|
tmplContent = fmt.Sprintf("# %s\n\n## 1. 结论\n\n待确认\n\n## 2. 正文\n\n待确认\n", title)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import (
|
|||||||
|
|
||||||
func TestPlanFixedAdaptive_Default(t *testing.T) {
|
func TestPlanFixedAdaptive_Default(t *testing.T) {
|
||||||
reg := NewRoleRegistry()
|
reg := NewRoleRegistry()
|
||||||
planner := NewAdaptivePlanner(reg)
|
planner := NewAdaptivePlanner(reg, nil)
|
||||||
|
|
||||||
req := PlannerRequest{
|
req := PlannerRequest{
|
||||||
TaskKey: "intake",
|
TaskKey: "intake",
|
||||||
@@ -36,7 +36,7 @@ func TestPlanFixedAdaptive_Default(t *testing.T) {
|
|||||||
|
|
||||||
func TestPlanFixedAdaptive_MaterialSnifferInsertStep(t *testing.T) {
|
func TestPlanFixedAdaptive_MaterialSnifferInsertStep(t *testing.T) {
|
||||||
reg := NewRoleRegistry()
|
reg := NewRoleRegistry()
|
||||||
planner := NewAdaptivePlanner(reg)
|
planner := NewAdaptivePlanner(reg, nil)
|
||||||
|
|
||||||
req := PlannerRequest{
|
req := PlannerRequest{
|
||||||
TaskKey: "research",
|
TaskKey: "research",
|
||||||
@@ -87,7 +87,7 @@ func TestPlanFixedAdaptive_MaterialSnifferInsertStep(t *testing.T) {
|
|||||||
|
|
||||||
func TestPlanAutonomousAdhoc_CleanroomDesign(t *testing.T) {
|
func TestPlanAutonomousAdhoc_CleanroomDesign(t *testing.T) {
|
||||||
reg := NewRoleRegistry()
|
reg := NewRoleRegistry()
|
||||||
planner := NewAdaptivePlanner(reg)
|
planner := NewAdaptivePlanner(reg, nil)
|
||||||
|
|
||||||
req := PlannerRequest{
|
req := PlannerRequest{
|
||||||
TaskKey: "cleanroom_spec",
|
TaskKey: "cleanroom_spec",
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import (
|
|||||||
func TestQualityGate_Validate(t *testing.T) {
|
func TestQualityGate_Validate(t *testing.T) {
|
||||||
tmpDir := t.TempDir()
|
tmpDir := t.TempDir()
|
||||||
|
|
||||||
engine := squad.NewWorkspaceEngine()
|
engine := squad.NewWorkspaceEngine(nil)
|
||||||
ws, err := engine.PrepareWorkspace(squad.WorkspacePrepareRequest{
|
ws, err := engine.PrepareWorkspace(squad.WorkspacePrepareRequest{
|
||||||
WorkDir: tmpDir,
|
WorkDir: tmpDir,
|
||||||
TaskKey: "intake",
|
TaskKey: "intake",
|
||||||
@@ -88,7 +88,7 @@ func TestQualityGate_Validate(t *testing.T) {
|
|||||||
func TestQualityGate_AutoRepair(t *testing.T) {
|
func TestQualityGate_AutoRepair(t *testing.T) {
|
||||||
tmpDir := t.TempDir()
|
tmpDir := t.TempDir()
|
||||||
|
|
||||||
engine := squad.NewWorkspaceEngine()
|
engine := squad.NewWorkspaceEngine(nil)
|
||||||
ws, err := engine.PrepareWorkspace(squad.WorkspacePrepareRequest{
|
ws, err := engine.PrepareWorkspace(squad.WorkspacePrepareRequest{
|
||||||
WorkDir: tmpDir,
|
WorkDir: tmpDir,
|
||||||
TaskKey: "intake",
|
TaskKey: "intake",
|
||||||
@@ -135,7 +135,7 @@ func TestQualityGate_AutoRepair(t *testing.T) {
|
|||||||
|
|
||||||
func TestQualityGate_SystemReqsAndTestManager(t *testing.T) {
|
func TestQualityGate_SystemReqsAndTestManager(t *testing.T) {
|
||||||
tmpDir := t.TempDir()
|
tmpDir := t.TempDir()
|
||||||
engine := squad.NewWorkspaceEngine()
|
engine := squad.NewWorkspaceEngine(nil)
|
||||||
gate := squad.NewQualityGate()
|
gate := squad.NewQualityGate()
|
||||||
|
|
||||||
// 1. 测试 system_reqs
|
// 1. 测试 system_reqs
|
||||||
@@ -206,7 +206,7 @@ func TestQualityGate_SystemReqsAndTestManager(t *testing.T) {
|
|||||||
// 现场证据 output.md:204 — 终稿自检表否定性引用禁词,不得误报为敷衍违规。
|
// 现场证据 output.md:204 — 终稿自检表否定性引用禁词,不得误报为敷衍违规。
|
||||||
func TestQualityGate_NegatedSelfCheckCitation_NoFalsePositive(t *testing.T) {
|
func TestQualityGate_NegatedSelfCheckCitation_NoFalsePositive(t *testing.T) {
|
||||||
tmpDir := t.TempDir()
|
tmpDir := t.TempDir()
|
||||||
engine := squad.NewWorkspaceEngine()
|
engine := squad.NewWorkspaceEngine(nil)
|
||||||
ws, err := engine.PrepareWorkspace(squad.WorkspacePrepareRequest{
|
ws, err := engine.PrepareWorkspace(squad.WorkspacePrepareRequest{
|
||||||
WorkDir: tmpDir,
|
WorkDir: tmpDir,
|
||||||
TaskKey: "intake",
|
TaskKey: "intake",
|
||||||
@@ -291,7 +291,7 @@ func TestQualityGate_NegatedSelfCheckCitation_NoFalsePositive(t *testing.T) {
|
|||||||
|
|
||||||
func intakeTemplate(t *testing.T) (workDir string, gate *squad.QualityGate) {
|
func intakeTemplate(t *testing.T) (workDir string, gate *squad.QualityGate) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
engine := squad.NewWorkspaceEngine()
|
engine := squad.NewWorkspaceEngine(nil)
|
||||||
ws, err := engine.PrepareWorkspace(squad.WorkspacePrepareRequest{
|
ws, err := engine.PrepareWorkspace(squad.WorkspacePrepareRequest{
|
||||||
WorkDir: t.TempDir(),
|
WorkDir: t.TempDir(),
|
||||||
TaskKey: "intake",
|
TaskKey: "intake",
|
||||||
@@ -463,7 +463,7 @@ func TestQualityGate_ActionPlanStatus_DoesNotTriggerRepair(t *testing.T) {
|
|||||||
if err := os.WriteFile(filepath.Join(workDir, "output.md"), []byte(content), 0o644); err != nil {
|
if err := os.WriteFile(filepath.Join(workDir, "output.md"), []byte(content), 0o644); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
driver := squad.NewWorkflowDriver(squad.NewWorkspaceEngine())
|
driver := squad.NewWorkflowDriver(squad.NewWorkspaceEngine(nil))
|
||||||
report, err := gate.EnsureQualityOrRepair(context.Background(), workDir, "intake", driver, &agent.StubBackend{}, 1, squad.ExecuteOptions{
|
report, err := gate.EnsureQualityOrRepair(context.Background(), workDir, "intake", driver, &agent.StubBackend{}, 1, squad.ExecuteOptions{
|
||||||
Timeout: 5 * time.Second,
|
Timeout: 5 * time.Second,
|
||||||
})
|
})
|
||||||
@@ -506,7 +506,7 @@ func TestCleanHeadingTitle_MultiDigitChineseOrdinal(t *testing.T) {
|
|||||||
// 现场 nightly TestManager:LLM 写「## 十一、签署 / ## 十二、说明」,旧清洗只剥到「十、」会误报缺章。
|
// 现场 nightly TestManager:LLM 写「## 十一、签署 / ## 十二、说明」,旧清洗只剥到「十、」会误报缺章。
|
||||||
func TestQualityGate_Validate_ChineseOrdinalElevenTwelve(t *testing.T) {
|
func TestQualityGate_Validate_ChineseOrdinalElevenTwelve(t *testing.T) {
|
||||||
tmpDir := t.TempDir()
|
tmpDir := t.TempDir()
|
||||||
engine := squad.NewWorkspaceEngine()
|
engine := squad.NewWorkspaceEngine(nil)
|
||||||
ws, err := engine.PrepareWorkspace(squad.WorkspacePrepareRequest{
|
ws, err := engine.PrepareWorkspace(squad.WorkspacePrepareRequest{
|
||||||
WorkDir: filepath.Join(tmpDir, "ws_tm"),
|
WorkDir: filepath.Join(tmpDir, "ws_tm"),
|
||||||
TaskKey: "test_manager",
|
TaskKey: "test_manager",
|
||||||
@@ -552,7 +552,7 @@ func TestNormalizeHeadingTitle_FoldsSpaceWidthAndCase(t *testing.T) {
|
|||||||
|
|
||||||
func TestQualityGate_Validate_HeadingLevelAndSpaceDrift(t *testing.T) {
|
func TestQualityGate_Validate_HeadingLevelAndSpaceDrift(t *testing.T) {
|
||||||
tmpDir := t.TempDir()
|
tmpDir := t.TempDir()
|
||||||
engine := squad.NewWorkspaceEngine()
|
engine := squad.NewWorkspaceEngine(nil)
|
||||||
ws, err := engine.PrepareWorkspace(squad.WorkspacePrepareRequest{
|
ws, err := engine.PrepareWorkspace(squad.WorkspacePrepareRequest{
|
||||||
WorkDir: filepath.Join(tmpDir, "ws_research"),
|
WorkDir: filepath.Join(tmpDir, "ws_research"),
|
||||||
TaskKey: "research",
|
TaskKey: "research",
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ const repairGoodDraft = `# 用户初始需求收集表
|
|||||||
|
|
||||||
func prepareRepairWS(t *testing.T) string {
|
func prepareRepairWS(t *testing.T) string {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
engine := squad.NewWorkspaceEngine()
|
engine := squad.NewWorkspaceEngine(nil)
|
||||||
ws, err := engine.PrepareWorkspace(squad.WorkspacePrepareRequest{
|
ws, err := engine.PrepareWorkspace(squad.WorkspacePrepareRequest{
|
||||||
WorkDir: t.TempDir(),
|
WorkDir: t.TempDir(),
|
||||||
TaskKey: "intake",
|
TaskKey: "intake",
|
||||||
@@ -102,7 +102,7 @@ func (b *localThenErrorBackend) Execute(_ context.Context, req agent.Request) (*
|
|||||||
// Local repair is promoted only when backend succeeds, parents stay untouched, and the local target is trusted.
|
// Local repair is promoted only when backend succeeds, parents stay untouched, and the local target is trusted.
|
||||||
func TestEnsureQualityOrRepair_LocalTargetPromotesAndRevalidates(t *testing.T) {
|
func TestEnsureQualityOrRepair_LocalTargetPromotesAndRevalidates(t *testing.T) {
|
||||||
workDir := prepareRepairWS(t)
|
workDir := prepareRepairWS(t)
|
||||||
driver := squad.NewWorkflowDriver(squad.NewWorkspaceEngine())
|
driver := squad.NewWorkflowDriver(squad.NewWorkspaceEngine(nil))
|
||||||
gate := squad.NewQualityGate()
|
gate := squad.NewQualityGate()
|
||||||
report, err := gate.EnsureQualityOrRepair(context.Background(), workDir, "intake", driver, &localRepairBackend{body: repairGoodDraft}, 1, squad.ExecuteOptions{
|
report, err := gate.EnsureQualityOrRepair(context.Background(), workDir, "intake", driver, &localRepairBackend{body: repairGoodDraft}, 1, squad.ExecuteOptions{
|
||||||
Timeout: 5 * time.Second,
|
Timeout: 5 * time.Second,
|
||||||
@@ -136,7 +136,7 @@ func TestEnsureQualityOrRepair_LocalTargetPromotesAndRevalidates(t *testing.T) {
|
|||||||
func TestEnsureQualityOrRepair_ParentEscapeRestoresAndFailsClosed(t *testing.T) {
|
func TestEnsureQualityOrRepair_ParentEscapeRestoresAndFailsClosed(t *testing.T) {
|
||||||
workDir := prepareRepairWS(t)
|
workDir := prepareRepairWS(t)
|
||||||
orig, _ := os.ReadFile(filepath.Join(workDir, "output.md"))
|
orig, _ := os.ReadFile(filepath.Join(workDir, "output.md"))
|
||||||
driver := squad.NewWorkflowDriver(squad.NewWorkspaceEngine())
|
driver := squad.NewWorkflowDriver(squad.NewWorkspaceEngine(nil))
|
||||||
gate := squad.NewQualityGate()
|
gate := squad.NewQualityGate()
|
||||||
report, err := gate.EnsureQualityOrRepair(context.Background(), workDir, "intake", driver, &parentEscapeBackend{body: repairGoodDraft}, 1, squad.ExecuteOptions{
|
report, err := gate.EnsureQualityOrRepair(context.Background(), workDir, "intake", driver, &parentEscapeBackend{body: repairGoodDraft}, 1, squad.ExecuteOptions{
|
||||||
Timeout: 5 * time.Second,
|
Timeout: 5 * time.Second,
|
||||||
@@ -166,7 +166,7 @@ func TestEnsureQualityOrRepair_ParentEscapeRestoresAndFailsClosed(t *testing.T)
|
|||||||
func TestEnsureQualityOrRepair_BothChangedFailsClosedNoPromote(t *testing.T) {
|
func TestEnsureQualityOrRepair_BothChangedFailsClosedNoPromote(t *testing.T) {
|
||||||
workDir := prepareRepairWS(t)
|
workDir := prepareRepairWS(t)
|
||||||
orig, _ := os.ReadFile(filepath.Join(workDir, "output.md"))
|
orig, _ := os.ReadFile(filepath.Join(workDir, "output.md"))
|
||||||
driver := squad.NewWorkflowDriver(squad.NewWorkspaceEngine())
|
driver := squad.NewWorkflowDriver(squad.NewWorkspaceEngine(nil))
|
||||||
gate := squad.NewQualityGate()
|
gate := squad.NewQualityGate()
|
||||||
parentDecoy := repairGoodDraft + "\n\n## 越界父级不应被采用\n"
|
parentDecoy := repairGoodDraft + "\n\n## 越界父级不应被采用\n"
|
||||||
report, err := gate.EnsureQualityOrRepair(context.Background(), workDir, "intake", driver, &bothWriteBackend{
|
report, err := gate.EnsureQualityOrRepair(context.Background(), workDir, "intake", driver, &bothWriteBackend{
|
||||||
@@ -195,7 +195,7 @@ func TestEnsureQualityOrRepair_BothChangedFailsClosedNoPromote(t *testing.T) {
|
|||||||
func TestEnsureQualityOrRepair_RunErrorDoesNotPromoteLocalTarget(t *testing.T) {
|
func TestEnsureQualityOrRepair_RunErrorDoesNotPromoteLocalTarget(t *testing.T) {
|
||||||
workDir := prepareRepairWS(t)
|
workDir := prepareRepairWS(t)
|
||||||
orig, _ := os.ReadFile(filepath.Join(workDir, "output.md"))
|
orig, _ := os.ReadFile(filepath.Join(workDir, "output.md"))
|
||||||
driver := squad.NewWorkflowDriver(squad.NewWorkspaceEngine())
|
driver := squad.NewWorkflowDriver(squad.NewWorkspaceEngine(nil))
|
||||||
gate := squad.NewQualityGate()
|
gate := squad.NewQualityGate()
|
||||||
report, err := gate.EnsureQualityOrRepair(context.Background(), workDir, "intake", driver, &localThenErrorBackend{body: repairGoodDraft}, 1, squad.ExecuteOptions{
|
report, err := gate.EnsureQualityOrRepair(context.Background(), workDir, "intake", driver, &localThenErrorBackend{body: repairGoodDraft}, 1, squad.ExecuteOptions{
|
||||||
Timeout: 5 * time.Second,
|
Timeout: 5 * time.Second,
|
||||||
@@ -221,7 +221,7 @@ func TestEnsureQualityOrRepair_CreatedParentInputFailsClosed(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
orig, _ := os.ReadFile(filepath.Join(workDir, "output.md"))
|
orig, _ := os.ReadFile(filepath.Join(workDir, "output.md"))
|
||||||
driver := squad.NewWorkflowDriver(squad.NewWorkspaceEngine())
|
driver := squad.NewWorkflowDriver(squad.NewWorkspaceEngine(nil))
|
||||||
gate := squad.NewQualityGate()
|
gate := squad.NewQualityGate()
|
||||||
report, err := gate.EnsureQualityOrRepair(context.Background(), workDir, "intake", driver, &createParentInputBackend{body: repairGoodDraft}, 1, squad.ExecuteOptions{
|
report, err := gate.EnsureQualityOrRepair(context.Background(), workDir, "intake", driver, &createParentInputBackend{body: repairGoodDraft}, 1, squad.ExecuteOptions{
|
||||||
Timeout: 5 * time.Second,
|
Timeout: 5 * time.Second,
|
||||||
@@ -251,7 +251,7 @@ func TestEnsureQualityOrRepair_RestoreFailureStopsWithoutPromote(t *testing.T) {
|
|||||||
return fmt.Errorf("restore output.md: injected")
|
return fmt.Errorf("restore output.md: injected")
|
||||||
})
|
})
|
||||||
defer reset()
|
defer reset()
|
||||||
driver := squad.NewWorkflowDriver(squad.NewWorkspaceEngine())
|
driver := squad.NewWorkflowDriver(squad.NewWorkspaceEngine(nil))
|
||||||
gate := squad.NewQualityGate()
|
gate := squad.NewQualityGate()
|
||||||
report, err := gate.EnsureQualityOrRepair(context.Background(), workDir, "intake", driver, &bothWriteBackend{
|
report, err := gate.EnsureQualityOrRepair(context.Background(), workDir, "intake", driver, &bothWriteBackend{
|
||||||
local: repairGoodDraft,
|
local: repairGoodDraft,
|
||||||
@@ -275,7 +275,7 @@ func TestEnsureQualityOrRepair_RestoreFailureStopsWithoutPromote(t *testing.T) {
|
|||||||
|
|
||||||
func TestEnsureQualityOrRepair_RepairBudgetNotRegressed(t *testing.T) {
|
func TestEnsureQualityOrRepair_RepairBudgetNotRegressed(t *testing.T) {
|
||||||
workDir := prepareRepairWS(t)
|
workDir := prepareRepairWS(t)
|
||||||
driver := squad.NewWorkflowDriver(squad.NewWorkspaceEngine())
|
driver := squad.NewWorkflowDriver(squad.NewWorkspaceEngine(nil))
|
||||||
gate := squad.NewQualityGate()
|
gate := squad.NewQualityGate()
|
||||||
_, err := gate.EnsureQualityOrRepair(context.Background(), workDir, "intake", driver, &parentEscapeBackend{body: repairGoodDraft}, 1, squad.ExecuteOptions{
|
_, err := gate.EnsureQualityOrRepair(context.Background(), workDir, "intake", driver, &parentEscapeBackend{body: repairGoodDraft}, 1, squad.ExecuteOptions{
|
||||||
Timeout: 5 * time.Second,
|
Timeout: 5 * time.Second,
|
||||||
@@ -290,7 +290,7 @@ func TestEnsureQualityOrRepair_RepairBudgetNotRegressed(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestAddStep_SnapshotLayoutCopiesNeedsLocally(t *testing.T) {
|
func TestAddStep_SnapshotLayoutCopiesNeedsLocally(t *testing.T) {
|
||||||
engine := squad.NewWorkspaceEngine()
|
engine := squad.NewWorkspaceEngine(nil)
|
||||||
ws, err := engine.PrepareWorkspace(squad.WorkspacePrepareRequest{
|
ws, err := engine.PrepareWorkspace(squad.WorkspacePrepareRequest{
|
||||||
WorkDir: t.TempDir(),
|
WorkDir: t.TempDir(),
|
||||||
TaskKey: "intake",
|
TaskKey: "intake",
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ func TestRDevRegulatoryFollowsComplexityTier(t *testing.T) {
|
|||||||
t.Fatal("tasks outside the list stay without regulatory even at high")
|
t.Fatal("tasks outside the list stay without regulatory even at high")
|
||||||
}
|
}
|
||||||
|
|
||||||
planner := NewAdaptivePlanner(nil)
|
planner := NewAdaptivePlanner(nil, nil)
|
||||||
low, err := planner.Plan(PlannerRequest{
|
low, err := planner.Plan(PlannerRequest{
|
||||||
TaskKey: "rd_sw_plan",
|
TaskKey: "rd_sw_plan",
|
||||||
Fields: map[string]string{"complexity_tier": "low"},
|
Fields: map[string]string{"complexity_tier": "low"},
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import (
|
|||||||
|
|
||||||
func TestRDevLeaderPrompt_UsesDesignControlNotBusiness(t *testing.T) {
|
func TestRDevLeaderPrompt_UsesDesignControlNotBusiness(t *testing.T) {
|
||||||
tmpDir := t.TempDir()
|
tmpDir := t.TempDir()
|
||||||
engine := squad.NewWorkspaceEngine()
|
engine := squad.NewWorkspaceEngine(nil)
|
||||||
|
|
||||||
ws, err := engine.PrepareWorkspace(squad.WorkspacePrepareRequest{
|
ws, err := engine.PrepareWorkspace(squad.WorkspacePrepareRequest{
|
||||||
WorkDir: filepath.Join(tmpDir, "rd_design_input_spec"),
|
WorkDir: filepath.Join(tmpDir, "rd_design_input_spec"),
|
||||||
@@ -42,7 +42,7 @@ func TestRDevLeaderPrompt_UsesDesignControlNotBusiness(t *testing.T) {
|
|||||||
|
|
||||||
func TestIntakeLeaderPrompt_StillBusiness(t *testing.T) {
|
func TestIntakeLeaderPrompt_StillBusiness(t *testing.T) {
|
||||||
tmpDir := t.TempDir()
|
tmpDir := t.TempDir()
|
||||||
engine := squad.NewWorkspaceEngine()
|
engine := squad.NewWorkspaceEngine(nil)
|
||||||
|
|
||||||
ws, err := engine.PrepareWorkspace(squad.WorkspacePrepareRequest{
|
ws, err := engine.PrepareWorkspace(squad.WorkspacePrepareRequest{
|
||||||
WorkDir: filepath.Join(tmpDir, "intake"),
|
WorkDir: filepath.Join(tmpDir, "intake"),
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ func TestRDevOverlayFiltersRoleAndComplexity(t *testing.T) {
|
|||||||
|
|
||||||
func TestRDevWorkspaceWritesOverlay(t *testing.T) {
|
func TestRDevWorkspaceWritesOverlay(t *testing.T) {
|
||||||
tmpDir := t.TempDir()
|
tmpDir := t.TempDir()
|
||||||
engine := NewWorkspaceEngine()
|
engine := NewWorkspaceEngine(nil)
|
||||||
ws, err := engine.PrepareWorkspace(WorkspacePrepareRequest{
|
ws, err := engine.PrepareWorkspace(WorkspacePrepareRequest{
|
||||||
WorkDir: filepath.Join(tmpDir, "rd_planning_review"),
|
WorkDir: filepath.Join(tmpDir, "rd_planning_review"),
|
||||||
TaskKey: "rd_planning_review",
|
TaskKey: "rd_planning_review",
|
||||||
|
|||||||
@@ -122,6 +122,23 @@ type Options struct {
|
|||||||
// 资产归 Core;Core 只做计算并返回产物,工作区文件由 squad 统一写入)。
|
// 资产归 Core;Core 只做计算并返回产物,工作区文件由 squad 统一写入)。
|
||||||
// nil 或返回 nil 表示无预处理。
|
// nil 或返回 nil 表示无预处理。
|
||||||
TaskPreparer func(workDir string, req SquadRunRequest) (*TaskPreparation, error)
|
TaskPreparer func(workDir string, req SquadRunRequest) (*TaskPreparation, error)
|
||||||
|
|
||||||
|
// Assets 提供文档模板与成员指令等内容资产(KTD1:与业务绑定的模板、
|
||||||
|
// 提示词及版本选择归 Core)。nil 时相关能力失败关闭,不降级猜测。
|
||||||
|
Assets AssetProvider
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssetProvider 是小队执行所需内容资产的唯一来源;磁盘/内嵌来源、
|
||||||
|
// 版本与业务绑定由产品侧装配决定。
|
||||||
|
type AssetProvider interface {
|
||||||
|
// DocTemplate 返回任务对应的文档模板正文。
|
||||||
|
DocTemplate(taskKey string) (string, error)
|
||||||
|
// RenderDoc 以变量渲染模板。
|
||||||
|
RenderDoc(tmpl string, vars map[string]string) string
|
||||||
|
// AgentInstruction 返回角色指令。
|
||||||
|
AgentInstruction(role string) (string, error)
|
||||||
|
// AgentInstructionFile 返回指定指令文件内容。
|
||||||
|
AgentInstructionFile(file string) (string, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TaskPreparation 是领域准备钩子的产物:附件与工作区内回写内容。
|
// TaskPreparation 是领域准备钩子的产物:附件与工作区内回写内容。
|
||||||
@@ -153,7 +170,7 @@ type Service struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewService(opts Options) *Service {
|
func NewService(opts Options) *Service {
|
||||||
engine := NewWorkspaceEngine()
|
engine := NewWorkspaceEngine(opts.Assets)
|
||||||
return &Service{
|
return &Service{
|
||||||
opts: opts.withDefaults(),
|
opts: opts.withDefaults(),
|
||||||
engine: engine,
|
engine: engine,
|
||||||
@@ -215,7 +232,7 @@ func taskDisplayTitle(req SquadRunRequest) string {
|
|||||||
|
|
||||||
// PlanWorkflow 动态规划生成任务工作流
|
// PlanWorkflow 动态规划生成任务工作流
|
||||||
func (s *Service) PlanWorkflow(ctx context.Context, req SquadRunRequest) (*WorkflowPlan, error) {
|
func (s *Service) PlanWorkflow(ctx context.Context, req SquadRunRequest) (*WorkflowPlan, error) {
|
||||||
planner := NewAdaptivePlanner(nil)
|
planner := NewAdaptivePlanner(nil, s.opts.Assets)
|
||||||
res, err := planner.Plan(PlannerRequest{
|
res, err := planner.Plan(PlannerRequest{
|
||||||
TaskKey: req.TaskKey,
|
TaskKey: req.TaskKey,
|
||||||
Title: taskDisplayTitle(req),
|
Title: taskDisplayTitle(req),
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ func (b *countingBackend) Execute(ctx context.Context, req agent.Request) (*agen
|
|||||||
func prepareSingleWriterWS(t *testing.T) (string, *squad.WorkflowDriver) {
|
func prepareSingleWriterWS(t *testing.T) (string, *squad.WorkflowDriver) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
tmpDir := t.TempDir()
|
tmpDir := t.TempDir()
|
||||||
engine := squad.NewWorkspaceEngine()
|
engine := squad.NewWorkspaceEngine(nil)
|
||||||
ws, err := engine.PrepareWorkspace(squad.WorkspacePrepareRequest{
|
ws, err := engine.PrepareWorkspace(squad.WorkspacePrepareRequest{
|
||||||
WorkDir: tmpDir,
|
WorkDir: tmpDir,
|
||||||
TaskKey: "prelim_design",
|
TaskKey: "prelim_design",
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ type WorkflowDriver struct {
|
|||||||
|
|
||||||
func NewWorkflowDriver(engine *WorkspaceEngine) *WorkflowDriver {
|
func NewWorkflowDriver(engine *WorkspaceEngine) *WorkflowDriver {
|
||||||
if engine == nil {
|
if engine == nil {
|
||||||
engine = NewWorkspaceEngine()
|
engine = NewWorkspaceEngine(nil)
|
||||||
}
|
}
|
||||||
return &WorkflowDriver{engine: engine, evaluator: NewStepEvaluator()}
|
return &WorkflowDriver{engine: engine, evaluator: NewStepEvaluator()}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import (
|
|||||||
func TestWorkflowDriver_DynamicAndExecute(t *testing.T) {
|
func TestWorkflowDriver_DynamicAndExecute(t *testing.T) {
|
||||||
tmpDir := t.TempDir()
|
tmpDir := t.TempDir()
|
||||||
|
|
||||||
engine := squad.NewWorkspaceEngine()
|
engine := squad.NewWorkspaceEngine(nil)
|
||||||
ws, err := engine.PrepareWorkspace(squad.WorkspacePrepareRequest{
|
ws, err := engine.PrepareWorkspace(squad.WorkspacePrepareRequest{
|
||||||
WorkDir: tmpDir,
|
WorkDir: tmpDir,
|
||||||
TaskKey: "intake",
|
TaskKey: "intake",
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"chat-agent-system/internal/template"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// WorkspacePrepareRequest 工作区准备请求
|
// WorkspacePrepareRequest 工作区准备请求
|
||||||
@@ -60,10 +59,12 @@ type Workspace struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// WorkspaceEngine 工作区生命周期与沙箱构建引擎
|
// WorkspaceEngine 工作区生命周期与沙箱构建引擎
|
||||||
type WorkspaceEngine struct{}
|
type WorkspaceEngine struct {
|
||||||
|
assets AssetProvider
|
||||||
|
}
|
||||||
|
|
||||||
func NewWorkspaceEngine() *WorkspaceEngine {
|
func NewWorkspaceEngine(assets AssetProvider) *WorkspaceEngine {
|
||||||
return &WorkspaceEngine{}
|
return &WorkspaceEngine{assets: assets}
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrepareWorkspace 初始化自包含工作区目录
|
// PrepareWorkspace 初始化自包含工作区目录
|
||||||
@@ -136,7 +137,7 @@ func (e *WorkspaceEngine) PrepareWorkspace(req WorkspacePrepareRequest) (*Worksp
|
|||||||
Steps: steps,
|
Steps: steps,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
planner := NewAdaptivePlanner(nil)
|
planner := NewAdaptivePlanner(nil, e.assets)
|
||||||
pRes, pErr := planner.Plan(PlannerRequest{
|
pRes, pErr := planner.Plan(PlannerRequest{
|
||||||
TaskKey: req.TaskKey,
|
TaskKey: req.TaskKey,
|
||||||
Title: req.StageTitle,
|
Title: req.StageTitle,
|
||||||
@@ -187,13 +188,16 @@ func (e *WorkspaceEngine) PrepareWorkspace(req WorkspacePrepareRequest) (*Worksp
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 4. 写入自描述模板 template.md
|
// 4. 写入自描述模板 template.md
|
||||||
if strings.TrimSpace(tmplContent) == "" {
|
if strings.TrimSpace(tmplContent) == "" && e.assets != nil {
|
||||||
var err error
|
var err error
|
||||||
tmplContent, err = template.GetDocTemplate(req.TaskKey)
|
tmplContent, err = e.assets.DocTemplate(req.TaskKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tmplContent = fmt.Sprintf("# %s\n\n## 1. 结论\n\n待确认\n\n## 2. 正文\n\n待确认\n", req.StageTitle)
|
tmplContent = ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if strings.TrimSpace(tmplContent) == "" {
|
||||||
|
tmplContent = fmt.Sprintf("# %s\n\n## 1. 结论\n\n待确认\n\n## 2. 正文\n\n待确认\n", req.StageTitle)
|
||||||
|
}
|
||||||
vars := map[string]string{
|
vars := map[string]string{
|
||||||
"project_name": req.ProjectName,
|
"project_name": req.ProjectName,
|
||||||
"product_name": req.ProjectName,
|
"product_name": req.ProjectName,
|
||||||
@@ -207,7 +211,12 @@ func (e *WorkspaceEngine) PrepareWorkspace(req WorkspacePrepareRequest) (*Worksp
|
|||||||
for k, v := range req.InitialFields {
|
for k, v := range req.InitialFields {
|
||||||
vars[k] = v
|
vars[k] = v
|
||||||
}
|
}
|
||||||
renderedTmpl := template.RenderDoc(tmplContent, vars)
|
var renderedTmpl string
|
||||||
|
if e.assets != nil {
|
||||||
|
renderedTmpl = e.assets.RenderDoc(tmplContent, vars)
|
||||||
|
} else {
|
||||||
|
renderedTmpl = tmplContent
|
||||||
|
}
|
||||||
if err := os.WriteFile(filepath.Join(workDir, "template.md"), []byte(renderedTmpl), 0o644); err != nil {
|
if err := os.WriteFile(filepath.Join(workDir, "template.md"), []byte(renderedTmpl), 0o644); err != nil {
|
||||||
return nil, fmt.Errorf("write template.md: %w", err)
|
return nil, fmt.Errorf("write template.md: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"chat-agent-system/internal/template"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// buildSubWorkspaces 为 workflow 中的各个子任务构建独立沙箱
|
// buildSubWorkspaces 为 workflow 中的各个子任务构建独立沙箱
|
||||||
@@ -27,11 +26,13 @@ func (e *WorkspaceEngine) buildSubWorkspaces(workDir string, wf WorkflowPlan, sq
|
|||||||
spec := agentMap[step.Role]
|
spec := agentMap[step.Role]
|
||||||
var inst string
|
var inst string
|
||||||
var err error
|
var err error
|
||||||
if file := strings.TrimSpace(spec.InstructionsFile); file != "" {
|
if e.assets != nil {
|
||||||
inst, err = template.GetAgentInstructionFile(file)
|
if file := strings.TrimSpace(spec.InstructionsFile); file != "" {
|
||||||
}
|
inst, err = e.assets.AgentInstructionFile(file)
|
||||||
if err != nil || strings.TrimSpace(inst) == "" {
|
}
|
||||||
inst, err = template.GetAgentInstruction(step.Role)
|
if err != nil || strings.TrimSpace(inst) == "" {
|
||||||
|
inst, err = e.assets.AgentInstruction(step.Role)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if err == nil && strings.TrimSpace(inst) != "" {
|
if err == nil && strings.TrimSpace(inst) != "" {
|
||||||
agentsBuilder.WriteString(inst)
|
agentsBuilder.WriteString(inst)
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import (
|
|||||||
func TestWorkspaceEngine_PrepareWorkspace(t *testing.T) {
|
func TestWorkspaceEngine_PrepareWorkspace(t *testing.T) {
|
||||||
tmpDir := t.TempDir()
|
tmpDir := t.TempDir()
|
||||||
|
|
||||||
engine := squad.NewWorkspaceEngine()
|
engine := squad.NewWorkspaceEngine(nil)
|
||||||
|
|
||||||
req := squad.WorkspacePrepareRequest{
|
req := squad.WorkspacePrepareRequest{
|
||||||
WorkDir: filepath.Join(tmpDir, "test_workspace_01"),
|
WorkDir: filepath.Join(tmpDir, "test_workspace_01"),
|
||||||
|
|||||||
Reference in New Issue
Block a user