carlos / platform Public

hostwrite: the inert selection interface for host-specific writers (claim Plan 6 Task 2)

Co-Authored-By: Codex <noreply@openai.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
codex agent, operated by paul@keymail.dev pushed by claude agent, operated by paul@keymail.dev 5d645223cfd50a4d887a8ee8a79eb60f41d37149 parent bf3b5e8
2 files changed, +95 −0
  • internal/hostwrite/write.go +45 −0
  • internal/hostwrite/write_test.go +50 −0
diff --git a/internal/hostwrite/write.go b/internal/hostwrite/write.go
new file mode 100644
index 000000000..dc9b3c097
--- /dev/null
+++ b/internal/hostwrite/write.go
@@ -0,0 +1,45 @@
+package hostwrite
+
+import (
+ "context"
+ "errors"
+ "strings"
+)
+
+var ErrUnavailable = errors.New("hostwrite: lifecycle provider unavailable")
+
+type Identity struct {
+ Config string
+ Release string
+}
+
+type Work interface {
+ With(context.Context, Identity, func() error) error
+}
+
+type Selector interface {
+ Select(context.Context, string, string) (Work, error)
+}
+
+type Config struct {
+ Enabled bool
+ Selector Selector
+}
+
+func Enabled(getenv func(string) string) bool {
+ return strings.TrimSpace(getenv("CARLOS_HOST_CLAIM")) != ""
+}
+
+func (c Config) Select(ctx context.Context, host, action string) (Work, error) {
+ if !c.Enabled {
+ return legacy{}, nil
+ }
+ if c.Selector == nil {
+ return nil, ErrUnavailable
+ }
+ return c.Selector.Select(ctx, host, action)
+}
+
+type legacy struct{}
+
+func (legacy) With(_ context.Context, _ Identity, fn func() error) error { return fn() }
diff --git a/internal/hostwrite/write_test.go b/internal/hostwrite/write_test.go
new file mode 100644
index 000000000..765c38f87
--- /dev/null
+++ b/internal/hostwrite/write_test.go
@@ -0,0 +1,50 @@
+package hostwrite
+
+import (
+ "context"
+ "errors"
+ "testing"
+)
+
+type rejectSelector struct{ calls *int }
+
+func (s rejectSelector) Select(context.Context, string, string) (Work, error) {
+ *s.calls++
+ return nil, ErrUnavailable
+}
+
+func TestFlagAndDisabledParity(t *testing.T) {
+ for _, tc := range []struct {
+ value string
+ want bool
+ }{{"", false}, {" \n", false}, {"0", true}, {"1", true}} {
+ calls := 0
+ got := Enabled(func(key string) string {
+ calls++
+ if key != "CARLOS_HOST_CLAIM" {
+ t.Fatal(key)
+ }
+ return tc.value
+ })
+ if got != tc.want || calls != 1 {
+ t.Fatal(tc, got, calls)
+ }
+ }
+ calls, writes := 0, 0
+ work, err := (Config{Selector: rejectSelector{&calls}}).Select(context.Background(), "host", "config")
+ if err != nil {
+ t.Fatal(err)
+ }
+ sentinel := errors.New("legacy error")
+ err = work.With(context.Background(), Identity{}, func() error { writes++; return sentinel })
+ if err != sentinel || calls != 0 || writes != 1 {
+ t.Fatal(err, calls, writes)
+ }
+}
+
+func TestEnabledMissingProviderRefuses(t *testing.T) {
+ work, err := (Config{Enabled: true}).Select(context.Background(), "host", "config")
+ if work != nil || !errors.Is(err, ErrUnavailable) {
+ t.Fatal(work, err)
+ }
+}