1- package api
1+ package aicrossrule
22
33// Phase-50 / 0036 — A3 Cross-rule conflict detection.
44//
@@ -73,6 +73,7 @@ import (
7373 "github.com/ev-dev-labs/teslasync/internal/ai/stream"
7474 "github.com/ev-dev-labs/teslasync/internal/ai/tools"
7575 "github.com/ev-dev-labs/teslasync/internal/ai/tools/diagnostic"
76+ "github.com/ev-dev-labs/teslasync/internal/api/httpx"
7677 tsauth "github.com/ev-dev-labs/teslasync/internal/auth"
7778 dbalert "github.com/ev-dev-labs/teslasync/internal/database/alert"
7879)
@@ -122,40 +123,40 @@ type aiCrossRuleConflictRequest struct {
122123 Limit * int `json:"limit,omitempty"`
123124}
124125
125- // AICrossRuleConflictHandler is the HTTP handler for
126+ // Handler is the HTTP handler for
126127// POST /api/v1/ai/alerts/rules/conflicts.
127128//
128129// Stateless beyond its constructor inputs; safe for concurrent
129130// use across requests. Construction is in router.go so the
130131// dispatcher's tool registry + provider registry are wired
131132// once at boot.
132- type AICrossRuleConflictHandler struct {
133+ type Handler struct {
133134 registry * provider.Registry
134135 tools * tools.Registry
135136 strategy strategy.Strategy
136137 headerName string
137138 maxIters int
138139}
139140
140- // NewAICrossRuleConflictHandler constructs the handler. All
141+ // NewHandler constructs the handler. All
141142// non-pointer arguments are required; the constructor panics
142143// on a nil so the wiring bug surfaces at boot, not at first
143144// request.
144- func NewAICrossRuleConflictHandler (
145+ func NewHandler (
145146 registry * provider.Registry ,
146147 toolReg * tools.Registry ,
147148 strat strategy.Strategy ,
148149 headerName string ,
149- ) * AICrossRuleConflictHandler {
150+ ) * Handler {
150151 switch {
151152 case registry == nil :
152- panic ("api: NewAICrossRuleConflictHandler : nil provider.Registry" )
153+ panic ("aicrossrule: NewHandler : nil provider.Registry" )
153154 case toolReg == nil :
154- panic ("api: NewAICrossRuleConflictHandler : nil tools.Registry" )
155+ panic ("aicrossrule: NewHandler : nil tools.Registry" )
155156 case strat == nil :
156- panic ("api: NewAICrossRuleConflictHandler : nil strategy.Strategy" )
157+ panic ("aicrossrule: NewHandler : nil strategy.Strategy" )
157158 }
158- return & AICrossRuleConflictHandler {
159+ return & Handler {
159160 registry : registry ,
160161 tools : toolReg ,
161162 strategy : strat ,
@@ -217,7 +218,7 @@ func parseCrossRuleConflictBody(w http.ResponseWriter, r *http.Request) (*aiCros
217218// path either writes a structured frame onto the SSE stream
218219// (when the writer has been opened) or a plain JSON 4xx/5xx
219220// (before it has).
220- func (h * AICrossRuleConflictHandler ) ServeHTTP (w http.ResponseWriter , r * http.Request ) {
221+ func (h * Handler ) ServeHTTP (w http.ResponseWriter , r * http.Request ) {
221222 body , ok := parseCrossRuleConflictBody (w , r )
222223 if ! ok {
223224 return
@@ -303,9 +304,17 @@ func (h *AICrossRuleConflictHandler) ServeHTTP(w http.ResponseWriter, r *http.Re
303304 }
304305}
305306
306- // Compile-time assertion: AICrossRuleConflictHandler satisfies
307+ // Compile-time assertion: Handler satisfies
307308// http.Handler.
308- var _ http.Handler = (* AICrossRuleConflictHandler )(nil )
309+ var _ http.Handler = (* Handler )(nil )
310+
311+ func writeError (w http.ResponseWriter , status int , msg string ) {
312+ httpx .WriteError (w , status , msg )
313+ }
314+
315+ func denyAllConfirm (_ context.Context , _ dispatch.ConfirmRequest ) (dispatch.ConfirmDecision , error ) {
316+ return dispatch .ConfirmDenied , nil
317+ }
309318
310319// ---------------------------------------------------------------------
311320// Production wiring for the CrossRuleConflictSource port declared by
@@ -314,26 +323,26 @@ var _ http.Handler = (*AICrossRuleConflictHandler)(nil)
314323// AIInboxCategorizationSource pattern from slice 0035.
315324// ---------------------------------------------------------------------
316325
317- // AICrossRuleConflictSource is the production
326+ // Source is the production
318327// diagnostic.CrossRuleConflictSource. It composes the canonical
319328// AlertRuleRepo (read-only) so the AI projection is grounded
320329// in the SAME alert_rules rows the deterministic AlertStudio
321330// renders. No write path is invoked.
322331//
323332// The struct holds one narrow read interface; the constructor
324333// panics on a nil so a wiring bug surfaces at boot.
325- type AICrossRuleConflictSource struct {
334+ type Source struct {
326335 rules * dbalert.AlertRuleRepo
327336}
328337
329- // NewAICrossRuleConflictSource constructs the adapter. Panics
338+ // NewSource constructs the adapter. Panics
330339// on a nil repo so a wiring mistake surfaces at boot rather
331340// than as a nil-deref on first AI request.
332- func NewAICrossRuleConflictSource (rules * dbalert.AlertRuleRepo ) * AICrossRuleConflictSource {
341+ func NewSource (rules * dbalert.AlertRuleRepo ) * Source {
333342 if rules == nil {
334- panic ("api: NewAICrossRuleConflictSource : nil *dbalert.AlertRuleRepo" )
343+ panic ("aicrossrule: NewSource : nil *dbalert.AlertRuleRepo" )
335344 }
336- return & AICrossRuleConflictSource {rules : rules }
345+ return & Source {rules : rules }
337346}
338347
339348// LoadRules implements diagnostic.CrossRuleConflictSource. Reads the
@@ -348,7 +357,7 @@ func NewAICrossRuleConflictSource(rules *dbalert.AlertRuleRepo) *AICrossRuleConf
348357//
349358// The Limit field caps the returned slice so a runaway request
350359// cannot blow past the canonical 500-row cap.
351- func (a * AICrossRuleConflictSource ) LoadRules (ctx context.Context , f diagnostic.CrossRuleConflictFilters ) ([]* alertmodel.AlertRule , error ) {
360+ func (a * Source ) LoadRules (ctx context.Context , f diagnostic.CrossRuleConflictFilters ) ([]* alertmodel.AlertRule , error ) {
352361 limit := f .Limit
353362 if limit <= 0 || limit > aiCrossRuleConflictMaxLimit {
354363 limit = aiCrossRuleConflictDefaultLimit
0 commit comments