diff --git a/internal/archtest/arch_test.go b/internal/archtest/arch_test.go index 8bce1e52..35b04e39 100644 --- a/internal/archtest/arch_test.go +++ b/internal/archtest/arch_test.go @@ -1,7 +1,12 @@ package archtest import ( + "go/ast" + "go/parser" + "go/token" "os" + "path/filepath" + "strings" "testing" "time" ) @@ -48,3 +53,56 @@ func TestArchitectureBoundaries(t *testing.T) { } } } + +var forbiddenBizdevLocators = []string{ + "Pool", "DocGov", "DocRevision", "ContextManager", "CAS", "ProjectWorkflow", "FreeformDoc", "KB", +} + +func TestBizdevNoLocatorGetters(t *testing.T) { + root, err := ModuleRoot() + if err != nil { + t.Fatal(err) + } + dir := filepath.Join(root, "internal", "bizdev") + fset := token.NewFileSet() + pkgs, err := parser.ParseDir(fset, dir, func(info os.FileInfo) bool { + name := info.Name() + return strings.HasSuffix(name, ".go") && !strings.HasSuffix(name, "_test.go") + }, parser.SkipObjectResolution) + if err != nil { + t.Fatal(err) + } + forbidden := make(map[string]struct{}, len(forbiddenBizdevLocators)) + for _, name := range forbiddenBizdevLocators { + forbidden[name] = struct{}{} + } + for _, pkg := range pkgs { + for path, file := range pkg.Files { + for _, decl := range file.Decls { + fn, ok := decl.(*ast.FuncDecl) + if !ok || fn.Recv == nil || fn.Name == nil { + continue + } + if _, hit := forbidden[fn.Name.Name]; !hit { + continue + } + if !recvIsBizdevService(fn.Recv) { + continue + } + t.Errorf("locator getter %s must not exist on *bizdev.Service (%s)", fn.Name.Name, relFile(root, path)) + } + } + } +} + +func recvIsBizdevService(recv *ast.FieldList) bool { + if recv == nil || len(recv.List) == 0 { + return false + } + expr := recv.List[0].Type + if star, ok := expr.(*ast.StarExpr); ok { + expr = star.X + } + ident, ok := expr.(*ast.Ident) + return ok && ident.Name == "Service" +} diff --git a/internal/bizdev/service.go b/internal/bizdev/service.go index 07757016..78869523 100644 --- a/internal/bizdev/service.go +++ b/internal/bizdev/service.go @@ -238,25 +238,6 @@ func (s *Service) DatabaseReady() bool { return s != nil && s.pool != nil } -func (s *Service) ContextManager() *ctxmgr.ContextManager { - return s.ctxMgr -} - -func (s *Service) CAS() *storage.CAS { - return s.cas -} - -func (s *Service) DocGov() *docgov.GovernanceEngine { - return s.docGov -} - -func (s *Service) DocRevision() *docrevision.Service { - if s == nil { - return nil - } - return s.docRev -} - // AgentOutputMaterializer is the production materialize path. Runtime injects // *docflow.Commands so candidate-ready/commit envelopes share the unique UoW. type AgentOutputMaterializer interface { @@ -279,22 +260,6 @@ func (s *Service) materializeAgentOutput(ctx context.Context, req docrevision.Ag return s.docRev.MaterializeAgentOutput(ctx, req) } -func (s *Service) ProjectWorkflow() *projectworkflow.Service { - return s.projectWorkflow -} - -func (s *Service) Pool() *pgxpool.Pool { - return s.pool -} - -func (s *Service) FreeformDoc() *freedoc.Service { - return s.freeDoc -} - -func (s *Service) KB() *kb.Client { - return s.kb -} - var SupportedTasks = squad.SupportedTasks var SupportedStages = []StageMeta{ diff --git a/internal/bizdev/types.go b/internal/bizdev/types.go index c8cb2ca4..e8f65d14 100644 --- a/internal/bizdev/types.go +++ b/internal/bizdev/types.go @@ -6,10 +6,8 @@ import ( "chat-agent-system/internal/ctxmgr" "chat-agent-system/internal/docgov" - "chat-agent-system/internal/kb" "chat-agent-system/internal/maturity" "chat-agent-system/internal/squad" - "chat-agent-system/internal/storage" ) type TaskMeta = squad.TaskMeta @@ -157,8 +155,4 @@ type BizdevService interface { ExecuteStage(ctx context.Context, req StageRunRequest) (*StageRunResult, error) InspectWorkspace(ctx context.Context, workDir string) (*squad.WorkspaceState, error) ResumeOrModifyWorkspace(ctx context.Context, req squad.ModifyWorkflowRequest) (*squad.SquadRunResult, error) - ContextManager() *ctxmgr.ContextManager - CAS() *storage.CAS - DocGov() *docgov.GovernanceEngine - KB() *kb.Client } diff --git a/internal/coordinator/instance_advance_governance_test.go b/internal/coordinator/instance_advance_governance_test.go index 89c58892..f451d557 100644 --- a/internal/coordinator/instance_advance_governance_test.go +++ b/internal/coordinator/instance_advance_governance_test.go @@ -5,14 +5,10 @@ import ( "testing" "chat-agent-system/internal/app/stageflow" - "chat-agent-system/internal/ctxmgr" - "chat-agent-system/internal/docgov" "chat-agent-system/internal/intent" - "chat-agent-system/internal/kb" "chat-agent-system/internal/maturity" "chat-agent-system/internal/projectinstance" "chat-agent-system/internal/squad" - "chat-agent-system/internal/storage" ) type countingBizdev struct { @@ -34,10 +30,6 @@ func (c *countingBizdev) InspectWorkspace(_ context.Context, _ string) (*squad.W func (c *countingBizdev) ResumeOrModifyWorkspace(_ context.Context, _ squad.ModifyWorkflowRequest) (*squad.SquadRunResult, error) { return nil, nil } -func (c *countingBizdev) ContextManager() *ctxmgr.ContextManager { return nil } -func (c *countingBizdev) CAS() *storage.CAS { return nil } -func (c *countingBizdev) DocGov() *docgov.GovernanceEngine { return nil } -func (c *countingBizdev) KB() *kb.Client { return nil } func TestDialogueAdvanceStage_LiveInstanceDoesNotUseBizdev(t *testing.T) { store := projectinstance.NewMemoryStore() diff --git a/internal/coordinator/pending_confirmation_reply_test.go b/internal/coordinator/pending_confirmation_reply_test.go index 628e5d62..86e004a3 100644 --- a/internal/coordinator/pending_confirmation_reply_test.go +++ b/internal/coordinator/pending_confirmation_reply_test.go @@ -7,13 +7,10 @@ import ( "time" "chat-agent-system/internal/app/stageflow" - "chat-agent-system/internal/ctxmgr" "chat-agent-system/internal/docgov" "chat-agent-system/internal/intent" - "chat-agent-system/internal/kb" "chat-agent-system/internal/maturity" "chat-agent-system/internal/squad" - "chat-agent-system/internal/storage" ) type pendingReplyBizdev struct { @@ -34,10 +31,6 @@ func (p *pendingReplyBizdev) InspectWorkspace(_ context.Context, _ string) (*squ func (p *pendingReplyBizdev) ResumeOrModifyWorkspace(_ context.Context, _ squad.ModifyWorkflowRequest) (*squad.SquadRunResult, error) { return nil, nil } -func (p *pendingReplyBizdev) ContextManager() *ctxmgr.ContextManager { return nil } -func (p *pendingReplyBizdev) CAS() *storage.CAS { return nil } -func (p *pendingReplyBizdev) DocGov() *docgov.GovernanceEngine { return nil } -func (p *pendingReplyBizdev) KB() *kb.Client { return nil } func TestSyncTriggerTask_PendingConfirmation_NotSavedCompleted(t *testing.T) { pending := &stageflow.TaskRunResult{ diff --git a/internal/server/task_finalize.go b/internal/server/task_finalize.go index bb1573bf..ec346813 100644 --- a/internal/server/task_finalize.go +++ b/internal/server/task_finalize.go @@ -30,9 +30,7 @@ func (s *Server) finalizeTaskRun(ctx context.Context, record *taskrun.RunRecord, cmds := s.taskCommands() if s != nil { cmds.PW = s.pwSvc - if s.svc != nil { - cmds.Ctx = s.ctxMgr - } + cmds.Ctx = s.ctxMgr } return cmds.Finalize(ctx, record, result) } @@ -49,7 +47,7 @@ func (s *Server) taskCommands() *taskrun.Commands { cmds.Env = s.cfg.Env cmds.StorageIdentity = "env:" + s.cfg.Env } - if s != nil && s.svc != nil { + if s != nil { cmds.Ctx = s.ctxMgr } return cmds diff --git a/internal/server/task_finalize_test.go b/internal/server/task_finalize_test.go index 75a8bb61..ae9a07c9 100644 --- a/internal/server/task_finalize_test.go +++ b/internal/server/task_finalize_test.go @@ -185,7 +185,7 @@ func TestAsyncTaskRun_FinalizesAfterWorkerCompletes(t *testing.T) { workDir := t.TempDir() _ = os.WriteFile(filepath.Join(workDir, "output.md"), []byte("# async finalize\n"), 0o644) - srv := &Server{cfg: cfg, svc: svc, pwSvc: pwSvc, taskRuns: taskrun.NewManager(pwSvc, ctxMgr)} + srv := &Server{cfg: cfg, svc: svc, pwSvc: pwSvc, ctxMgr: ctxMgr, taskRuns: taskrun.NewManager(pwSvc, ctxMgr)} srv.taskRuns.SetExecutor( func(_ context.Context, _ string, _ bizdev.TaskRunRequest, _ *stream.Streamer) (*bizdev.TaskRunResult, error) { return &bizdev.TaskRunResult{ @@ -242,7 +242,7 @@ func TestAsyncTaskRun_PendingConfirmation_FinalizesAsPending(t *testing.T) { workDir := t.TempDir() _ = os.WriteFile(filepath.Join(workDir, "output.md"), []byte("# head\n"), 0o644) - srv := &Server{cfg: cfg, svc: svc, pwSvc: pwSvc, taskRuns: taskrun.NewManager(pwSvc, ctxMgr)} + srv := &Server{cfg: cfg, svc: svc, pwSvc: pwSvc, ctxMgr: ctxMgr, taskRuns: taskrun.NewManager(pwSvc, ctxMgr)} srv.taskRuns.SetExecutor( func(_ context.Context, _ string, _ bizdev.TaskRunRequest, _ *stream.Streamer) (*bizdev.TaskRunResult, error) { return &bizdev.TaskRunResult{