Files
Agent-System/internal/app/fav/subscriber.go
T
weichenandCursor 2c1d040ccf feat(events): add FAV subscriber shadow without live writes
ObserveShadow computes intended outcome only and refuses FAV_DEGRADED_READ; HandleLease and Register(fav) stay fail-closed.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-17 04:25:17 +00:00

94 lines
3.0 KiB
Go

// Package fav holds the durable Final Artifact Vault event subscriber stub.
//
// U7 incremental rule: the fav consumer stays mode=off and unregistered until
// a separate approval. Registry.Register("fav") and SetMode(fav, live) already
// fail closed in internal/events; this package must not bypass those gates.
package fav
import (
"context"
"fmt"
"os"
"strings"
"chat-agent-system/internal/artifactvault"
"chat-agent-system/internal/events"
"github.com/jackc/pgx/v5"
)
// EventConsumer is the durable FAV publisher subscriber.
// Enabled defaults to false (zero value). Do not register into cmd/events
// until separate cutover approval.
type EventConsumer struct {
Publisher *artifactvault.Publisher
Env string
// Enabled must stay false by default. Even when true, events.Registry
// still rejects Register(ConsumerFAV) until the hard gate is lifted.
Enabled bool
// AllowDegradedRead must stay false. Shadow refuses if this is true.
AllowDegradedRead bool
}
// HandleLease refuses work while the subscriber is disabled (default).
// Even when Enabled is flipped locally, the stub still refuses until the
// events registry hard-gate and a separate cutover approval are both lifted.
func (c *EventConsumer) HandleLease(ctx context.Context, lease events.Lease, tx pgx.Tx) error {
_ = ctx
_ = lease
_ = tx
if c == nil || !c.Enabled {
return fmt.Errorf("%w: fav", events.ErrDisabledConsumer)
}
_ = c.Publisher
return fmt.Errorf("%w: fav publisher cutover not approved", events.ErrDisabledConsumer)
}
// DefaultEnabled reports the compile-time / zero-value default for the subscriber.
func DefaultEnabled() bool {
return false
}
// IntendedOutcome is a read-only shadow calculation. It must never be treated
// as a write receipt; WouldStage/WouldPublish stay false in this slice.
type IntendedOutcome struct {
EventName string
AggregateType string
AggregateID string
LogicalResource string
WouldStage bool
WouldPublish bool
DegradedReadUsed bool
}
// ObserveShadow computes the intended FAV outcome without business side effects.
// FAV_DEGRADED_READ must stay false; live staging/artifact/publication is not opened.
func (c *EventConsumer) ObserveShadow(lease events.Lease) (IntendedOutcome, error) {
out := IntendedOutcome{
EventName: lease.EventName,
AggregateType: lease.AggregateType,
AggregateID: lease.AggregateID,
LogicalResource: strings.TrimSpace(lease.AggregateType) + ":" + strings.TrimSpace(lease.AggregateID),
}
if c == nil {
return out, fmt.Errorf("%w: fav", events.ErrDisabledConsumer)
}
if c.AllowDegradedRead || envTruthy("FAV_DEGRADED_READ") {
return out, fmt.Errorf("fav shadow refuses FAV_DEGRADED_READ")
}
out.WouldStage = false
out.WouldPublish = false
out.DegradedReadUsed = false
_ = c.Publisher // never Drain / Stage / Publish here
return out, nil
}
func envTruthy(key string) bool {
switch strings.ToLower(strings.TrimSpace(os.Getenv(key))) {
case "1", "true", "yes", "on":
return true
default:
return false
}
}