From 9b1a94c5bcb3584c66ce1beafed76f4c45297527 Mon Sep 17 00:00:00 2001 From: weichen Date: Tue, 15 Sep 2026 14:23:07 +0000 Subject: [PATCH] feat(app): drop production outbox and approval hook registration Durable notify/advance/projection are live on shared dev, so composition roots must not assign OnOutbox or AddOnApprovedHook. Bind helpers remain for tests and a rollback window. Co-authored-by: Cursor --- internal/app/app.go | 12 ++---- internal/app/unhook_test.go | 48 +++++++++++++++++++++++ internal/server/workflow_approval_hook.go | 7 ++-- 3 files changed, 54 insertions(+), 13 deletions(-) create mode 100644 internal/app/unhook_test.go diff --git a/internal/app/app.go b/internal/app/app.go index 6ee92ef7..27fe690c 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -201,22 +201,16 @@ func (r *Runtime) bindDocumentNotify() { if r == nil || r.notifyBound { return } - if r.Biz != nil && r.Rev != nil { - if docnotify.Bind(r.Rev, r.DocNotify) { - r.subscribers++ - } - } + // Durable notify is live; do not assign OnOutbox in production. r.notifyBound = true } func (r *Runtime) bindWorkflowApprovalHook() { - if r == nil || r.hookBound || r.Gov == nil || r.Inst == nil || !r.Inst.Available() { - r.hookBound = true + if r == nil || r.hookBound { return } - r.Gov.AddOnApprovedHook(r.onWorkflowDocumentApproved) + // Durable workflow_advance is live; do not register AddOnApprovedHook in production. r.hookBound = true - r.subscribers++ } // WriterLive reports whether the durable consumer already owns the visible write. diff --git a/internal/app/unhook_test.go b/internal/app/unhook_test.go new file mode 100644 index 00000000..07479924 --- /dev/null +++ b/internal/app/unhook_test.go @@ -0,0 +1,48 @@ +package app + +import ( + "os" + "path/filepath" + "strings" + "testing" +) + +func TestProductionCompositionHasNoHookRegistration(t *testing.T) { + root := findGoMod(t) + files := []string{ + filepath.Join(root, "internal", "app", "app.go"), + filepath.Join(root, "internal", "server", "workflow_approval_hook.go"), + filepath.Join(root, "internal", "server", "server.go"), + } + for _, f := range files { + b, err := os.ReadFile(f) + if err != nil { + t.Fatal(err) + } + src := string(b) + if strings.Contains(src, "OnOutbox =") { + t.Fatalf("%s still assigns OnOutbox", f) + } + if strings.Contains(src, "AddOnApprovedHook(") { + t.Fatalf("%s still registers AddOnApprovedHook", f) + } + } +} + +func findGoMod(t *testing.T) string { + t.Helper() + dir, err := os.Getwd() + if err != nil { + t.Fatal(err) + } + for { + if _, err := os.Stat(filepath.Join(dir, "go.mod")); err == nil { + return dir + } + parent := filepath.Dir(dir) + if parent == dir { + t.Fatal("go.mod not found") + } + dir = parent + } +} diff --git a/internal/server/workflow_approval_hook.go b/internal/server/workflow_approval_hook.go index abd85414..3a77ca5b 100644 --- a/internal/server/workflow_approval_hook.go +++ b/internal/server/workflow_approval_hook.go @@ -9,10 +9,9 @@ import ( ) func (s *Server) registerWorkflowApprovalHook() { - if s == nil || s.gov == nil || s.instSvc == nil || !s.instSvc.Available() { - return - } - s.gov.AddOnApprovedHook(s.onWorkflowDocumentApproved) + // Durable workflow_advance is the unique writer. Keep the method for one + // rollback window; do not register AddOnApprovedHook in production. + return } func (s *Server) onWorkflowDocumentApproved(ctx context.Context, doc *docgov.DocumentRecord) {