feat(events): add fav subscriber and artifact-vault CLI stubs
Keep durable fav unregistered and fail-closed; CLI only prints status and remaining-port inventory without destructive backfill/recover commands. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
// Command artifact-vault is the FAV operator CLI stub for U7 incremental.
|
||||
//
|
||||
// Full backfill / inventory / recover-apply / destroy live on
|
||||
// feature/final-artifact-vault and were NOT whole-branch merged. This binary
|
||||
// compiles on current dev and stays non-destructive: it only prints status /
|
||||
// remaining-port inventory.
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if len(os.Args) < 2 {
|
||||
usage()
|
||||
os.Exit(2)
|
||||
}
|
||||
switch os.Args[1] {
|
||||
case "status", "version":
|
||||
writeJSON(map[string]any{
|
||||
"binary": "artifact-vault",
|
||||
"slice": "u7-incremental-stub",
|
||||
"publisher_default": false,
|
||||
"degraded_read": false,
|
||||
"consumer": "fav",
|
||||
"consumer_mode": "off",
|
||||
"remaining_to_port": []string{
|
||||
"enumerate.go (EnumerateTerminals / LoadExclusions)",
|
||||
"TerminalRow + Backfill(rows) API (diverged from dataset Candidate path)",
|
||||
"full recover-plan / recover-apply / gc-preflight / destroy",
|
||||
"server ingest hooks (OnFinalArtifact*)",
|
||||
"durable fav event Register after separate approval",
|
||||
},
|
||||
"source_branch": "feature/final-artifact-vault",
|
||||
"note": "do not enable FAV_DEGRADED_READ or fav consumer without approval",
|
||||
})
|
||||
case "help", "-h", "--help":
|
||||
usage()
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "artifact-vault: unknown command %q (full CLI remains on feature/final-artifact-vault)\n", os.Args[1])
|
||||
usage()
|
||||
os.Exit(2)
|
||||
}
|
||||
}
|
||||
|
||||
func usage() {
|
||||
fmt.Fprintln(os.Stderr, "usage: artifact-vault <status|version|help>")
|
||||
fmt.Fprintln(os.Stderr, "U7 stub only — backfill/recover/destroy not ported; fav stays off")
|
||||
}
|
||||
|
||||
func writeJSON(v any) {
|
||||
enc := json.NewEncoder(os.Stdout)
|
||||
enc.SetIndent("", " ")
|
||||
_ = enc.Encode(v)
|
||||
}
|
||||
@@ -83,6 +83,8 @@ func main() {
|
||||
(&docproject.EventConsumer{Docs: gov, Env: cfg.Env}).HandleLease,
|
||||
(&intentflow.ProjectionConsumer{Env: cfg.Env}).HandleLease,
|
||||
))
|
||||
// Intentionally NOT registering events.ConsumerFAV / internal/app/fav.
|
||||
// Registry.Register("fav") fails closed; mode stays off until separate approval.
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("GET /health", func(w http.ResponseWriter, _ *http.Request) {
|
||||
writeJSON(w, http.StatusOK, map[string]any{"status": "ok", "env": cfg.Env, "owner": owner})
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
// 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"
|
||||
|
||||
"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
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package fav_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"chat-agent-system/internal/app/fav"
|
||||
"chat-agent-system/internal/events"
|
||||
)
|
||||
|
||||
func TestFavSubscriberDefaultsDisabled(t *testing.T) {
|
||||
if fav.DefaultEnabled() {
|
||||
t.Fatal("fav subscriber DefaultEnabled must be false")
|
||||
}
|
||||
c := &fav.EventConsumer{}
|
||||
if c.Enabled {
|
||||
t.Fatal("zero-value EventConsumer.Enabled must be false")
|
||||
}
|
||||
err := c.HandleLease(context.Background(), events.Lease{}, nil)
|
||||
if !errors.Is(err, events.ErrDisabledConsumer) {
|
||||
t.Fatalf("disabled HandleLease: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFavSubscriberStillDisabledWhenFlagOnWithoutRegistryApproval(t *testing.T) {
|
||||
c := &fav.EventConsumer{Enabled: true, Env: "dev"}
|
||||
err := c.HandleLease(context.Background(), events.Lease{Environment: "dev"}, nil)
|
||||
if !errors.Is(err, events.ErrDisabledConsumer) {
|
||||
t.Fatalf("enabled stub must still refuse until cutover approved: %v", err)
|
||||
}
|
||||
if err := events.NewRegistry().Register(events.ConsumerFAV); err == nil {
|
||||
t.Fatal("Registry.Register(fav) must remain fail-closed")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user