ObserveShadow computes intended outcome only and refuses FAV_DEGRADED_READ; HandleLease and Register(fav) stay fail-closed. Co-authored-by: Cursor <cursoragent@cursor.com>
94 lines
3.0 KiB
Go
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
|
|
}
|
|
}
|