carlos / platform Public

instancectl: route POST /claim/held-release to the claim handler (moves Task 7b)

The claim-mode mux hands the new route to the claim root, which refuses it
unless CARLOS_MOVES_PHASE1 wired it. A flag-off mux has no such route, so
an agent's request there reads as unavailable.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
claude agent, operated by paul@keymail.dev cf0b6de5e1a1262b23ccf3b5c63906acf301805a parent 27b8307
2 files changed, +27 −0
  • internal/instancectl/claimunit_test.go +24 −0
  • internal/instancectl/server.go +3 −0
diff --git a/internal/instancectl/claimunit_test.go b/internal/instancectl/claimunit_test.go
index c68d18175..c5b27b3f1 100644
--- a/internal/instancectl/claimunit_test.go
+++ b/internal/instancectl/claimunit_test.go
@@ -251,3 +251,27 @@ func TestClaimLaunchRouteExistsOnlyInClaimMode(t *testing.T) {
t.Fatalf("GET /claim/launch=%d", rec.Code)
}
}
+
+// TestClaimHeldReleaseRouteExistsOnlyInClaimMode (moves Task 7b): the
+// root's held-release route is the claim handler's, and a flag-off mux
+// has none, so an agent's request there reads as unavailable.
+func TestClaimHeldReleaseRouteExistsOnlyInClaimMode(t *testing.T) {
+ legacy := (&Server{Systemctl: func(context.Context, ...string) (string, error) { return "", nil }}).Handler()
+ rec := httptest.NewRecorder()
+ legacy.ServeHTTP(rec, httptest.NewRequest(http.MethodPost, "/claim/held-release", strings.NewReader("{}")))
+ if rec.Code != http.StatusNotFound {
+ t.Fatalf("flag-off mux answered /claim/held-release with %d", rec.Code)
+ }
+ reached := ""
+ claim := (&Server{ClaimStop: http.HandlerFunc(func(_ http.ResponseWriter, r *http.Request) { reached = r.URL.Path })}).Handler()
+ rec = httptest.NewRecorder()
+ claim.ServeHTTP(rec, httptest.NewRequest(http.MethodPost, "/claim/held-release", strings.NewReader("{}")))
+ if reached != "/claim/held-release" {
+ t.Fatalf("claim mux did not route /claim/held-release: %q", reached)
+ }
+ rec = httptest.NewRecorder()
+ claim.ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/claim/held-release", nil))
+ if rec.Code != http.StatusMethodNotAllowed {
+ t.Fatalf("GET /claim/held-release=%d", rec.Code)
+ }
+}
diff --git a/internal/instancectl/server.go b/internal/instancectl/server.go
index 1571cd988..afadf65e3 100644
--- a/internal/instancectl/server.go
+++ b/internal/instancectl/server.go
@@ -277,6 +277,9 @@ func (s *Server) Handler() http.Handler {
mux.Handle("POST /claim/journal", s.ClaimStop)
mux.Handle("POST /claim/writers-stopped", s.ClaimStop)
mux.Handle("POST /claim/launch", s.ClaimStop)
+ // moves Task 7b: the held release under the claim. The claim
+ // handler refuses it unless CARLOS_MOVES_PHASE1 wired it.
+ mux.Handle("POST /claim/held-release", s.ClaimStop)
}
mux.HandleFunc("POST /start", s.claimAwareLegacy(s.handleStart))
mux.HandleFunc("POST /seed", s.claimAwareLegacy(s.handleSeed))