carlos / platform Public

moves: pin the revert re-read's stall on loss- and fence-bound admissions (amadan #300)

revertProbeWrites replays the probe's admission with hostclaim.AdmitWriter,
which cannot vouch for a loss-bound or fence-bound admission: their verified
flags stay hostclaim's own (exposing them would be a forging surface). The
re-read refuses such an admission with ErrConflict and the step stays at
probing, as before the re-read existed. The refusal now names the kind and
amadan #300; the later fix is a hostclaim-owned function that re-verifies
those admissions.

TestRevertProbedRefusesAnUnreplayableAdmission makes the stall explicit: a
real loss-bound and a real fence-bound admission through the claim's writer
(the recovery fixtures make both on a return) are refused, beside a plain
admission over the same fixture that replays; and the revert step, its
probe's one claim write shaped as a loss-bound admission, stays at probing
with the row held.

revertRun.probing is unchanged; only the internal/host envelope is re-pinned.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
claude agent, operated by paul@keymail.dev f8a0c2de2cd1c5c715413d775cc2397ad8ff4cb4 parent 8e61713
3 files changed, +112 −2
  • internal/host/movesrevert.go +11 −1
  • internal/host/movesrevert_test.go +99 −0
  • internal/provision/testdata/plan14-root-reads-home-part2.tsv +2 −1

CI passed — run details

CI log
=== gate ===
cd launchctx && go vet ./... && go test ./... -count=1
ok  	amadan.net/carlos/platform.git/launchctx	0.011s
?   	amadan.net/carlos/platform.git/launchctx/controlname	[no test files]
?   	amadan.net/carlos/platform.git/launchctx/wireid	[no test files]
go vet ./...
gofmt -l .: clean
go test ./... -count=1 -timeout 30m
ok  	github.com/carlosframework/platform	287.273s
?   	github.com/carlosframework/platform/cmd/changelog	[no test files]
ok  	github.com/carlosframework/platform/cmd/console	2.749s
ok  	github.com/carlosframework/platform/cmd/moveprobe	93.186s
ok  	github.com/carlosframework/platform/cmd/zddprobe	0.699s
ok  	github.com/carlosframework/platform/internal/activator	250.501s
ok  	github.com/carlosframework/platform/internal/agentsock	0.026s
ok  	github.com/carlosframework/platform/internal/alerting	0.003s
ok  	github.com/carlosframework/platform/internal/analytics	0.023s
ok  	github.com/carlosframework/platform/internal/appconfig	2.391s
ok  	github.com/carlosframework/platform/internal/apperr	0.081s
ok  	github.com/carlosframework/platform/internal/applog	0.131s
ok  	github.com/carlosframework/platform/internal/awsinv	0.004s
ok  	github.com/carlosframework/platform/internal/boxchoice	0.008s
ok  	github.com/carlosframework/platform/internal/boxid	0.743s
?   	github.com/carlosframework/platform/internal/boxid/boxidtest	[no test files]
?   	github.com/carlosframework/platform/internal/boxid/internal/signtest	[no test files]
ok  	github.com/carlosframework/platform/internal/boxvolumes	0.009s
ok  	github.com/carlosframework/platform/internal/caps	0.012s
ok  	github.com/carlosframework/platform/internal/certwarm	0.002s
ok  	github.com/carlosframework/platform/internal/cgroup	0.689s
ok  	github.com/carlosframework/platform/internal/changelog	0.305s
ok  	github.com/carlosframework/platform/internal/clamd	0.055s
?   	github.com/carlosframework/platform/internal/cliflag	[no test files]
ok  	github.com/carlosframework/platform/internal/console	389.906s
?   	github.com/carlosframework/platform/internal/controlname	[no test files]
?   	github.com/carlosframework/platform/internal/controlsock	[no test files]
ok  	github.com/carlosframework/platform/internal/dbdoor	6.233s
ok  	github.com/carlosframework/platform/internal/dbdoor/dbdoortest	0.016s
ok  	github.com/carlosframework/platform/internal/dbverify	0.004s
ok  	github.com/carlosframework/platform/internal/dnsrec	0.004s
ok  	github.com/carlosframework/platform/internal/docsite	0.005s
ok  	github.com/carlosframework/platform/internal/domains	2.320s
ok  	github.com/carlosframework/platform/internal/doorkey	0.008s
ok  	github.com/carlosframework/platform/internal/economics	0.007s
ok  	github.com/carlosframework/platform/internal/edge	26.374s
?   	github.com/carlosframework/platform/internal/edgeproof	[no test files]
ok  	github.com/carlosframework/platform/internal/edgesfile	0.008s
ok  	github.com/carlosframework/platform/internal/fleetapi	0.135s
ok  	github.com/carlosframework/platform/internal/gate	1.659s
ok  	github.com/carlosframework/platform/internal/genselect	0.015s
ok  	github.com/carlosframework/platform/internal/geo	0.024s
ok  	github.com/carlosframework/platform/internal/geoip	0.032s
ok  	github.com/carlosframework/platform/internal/host	162.837s
ok  	github.com/carlosframework/platform/internal/hostactors	0.435s
ok  	github.com/carlosframework/platform/internal/hostbox	2.444s
ok  	github.com/carlosframework/platform/internal/hostclaim	0.514s
ok  	github.com/carlosframework/platform/internal/hostevidence	0.007s
ok  	github.com/carlosframework/platform/internal/hostexec	4.739s
ok  	github.com/carlosframework/platform/internal/hostfence	0.027s
ok  	github.com/carlosframework/platform/internal/hostfiles	10.487s
ok  	github.com/carlosframework/platform/internal/hostfloor	0.013s
ok  	github.com/carlosframework/platform/internal/hostguard	1.013s
ok  	github.com/carlosframework/platform/internal/hostops	1.547s
?   	github.com/carlosframework/platform/internal/hostops/fencerelease	[no test files]
ok  	github.com/carlosframework/platform/internal/hostops/internal/fencerelease	0.002s
ok  	github.com/carlosframework/platform/internal/hostquarantine	0.002s
ok  	github.com/carlosframework/platform/internal/hostrelocate	0.420s
ok  	github.com/carlosframework/platform/internal/hostresolve	0.135s
ok  	github.com/carlosframework/platform/internal/hostwrite	0.002s
ok  	github.com/carlosframework/platform/internal/infraview	0.004s
ok  	github.com/carlosframework/platform/internal/instancectl	4.180s
ok  	github.com/carlosframework/platform/internal/instanceexec	2.710s
ok  	github.com/carlosframework/platform/internal/instancefs	0.024s
ok  	github.com/carlosframework/platform/internal/instancekey	0.023s
ok  	github.com/carlosframework/platform/internal/instanceupdates	0.025s
ok  	github.com/carlosframework/platform/internal/layout	1.511s
ok  	github.com/carlosframework/platform/internal/lease	9.805s
ok  	github.com/carlosframework/platform/internal/lease/leasetest	0.024s
ok  	github.com/carlosframework/platform/internal/ledger	0.033s
ok  	github.com/carlosframework/platform/internal/mail	0.174s
ok  	github.com/carlosframework/platform/internal/maintenance	0.076s
ok  	github.com/carlosframework/platform/internal/maintenanceexec	5.510s
ok  	github.com/carlosframework/platform/internal/maintenanceguard	0.083s
ok  	github.com/carlosframework/platform/internal/metrics	0.004s
ok  	github.com/carlosframework/platform/internal/moves	20.898s
ok  	github.com/carlosframework/platform/internal/moves/archive	0.778s
ok  	github.com/carlosframework/platform/internal/objstore	0.170s
ok  	github.com/carlosframework/platform/internal/ops	0.179s
ok  	github.com/carlosframework/platform/internal/park	0.203s
ok  	github.com/carlosframework/platform/internal/privatefile	0.003s
ok  	github.com/carlosframework/platform/internal/provision	14.387s
ok  	github.com/carlosframework/platform/internal/publicrelease	0.007s
?   	github.com/carlosframework/platform/internal/regionname	[no test files]
ok  	github.com/carlosframework/platform/internal/registry	4.885s
ok  	github.com/carlosframework/platform/internal/relay	7.216s
ok  	github.com/carlosframework/platform/internal/release	19.813s
ok  	github.com/carlosframework/platform/internal/rollout	3.623s
ok  	github.com/carlosframework/platform/internal/rolloutwords	0.003s
ok  	github.com/carlosframework/platform/internal/rootaws	0.007s
ok  	github.com/carlosframework/platform/internal/rootparticipation	0.006s
ok  	github.com/carlosframework/platform/internal/rootstate	0.025s
ok  	github.com/carlosframework/platform/internal/runs	0.568s
ok  	github.com/carlosframework/platform/internal/s3compat	0.004s
ok  	github.com/carlosframework/platform/internal/safefile	0.004s
ok  	github.com/carlosframework/platform/internal/schedule	2.453s
ok  	github.com/carlosframework/platform/internal/seeds	0.011s
ok  	github.com/carlosframework/platform/internal/sim/hostlife	8.707s
ok  	github.com/carlosframework/platform/internal/sim/litestreamfacts	3.946s
ok  	github.com/carlosframework/platform/internal/status	0.041s
ok  	github.com/carlosframework/platform/internal/steering	0.006s
ok  	github.com/carlosframework/platform/internal/stopevidence	0.336s
ok  	github.com/carlosframework/platform/internal/svcrole	0.002s
ok  	github.com/carlosframework/platform/internal/telemetry	0.004s
ok  	github.com/carlosframework/platform/internal/termfence	0.004s
ok  	github.com/carlosframework/platform/internal/tunnelclient	0.004s
ok  	github.com/carlosframework/platform/internal/unixdial	0.002s
ok  	github.com/carlosframework/platform/internal/updatewords	0.004s
ok  	github.com/carlosframework/platform/internal/usage	0.004s
ok  	github.com/carlosframework/platform/internal/version	0.001s
ok  	github.com/carlosframework/platform/internal/vet	1.525s
=== cross ===
--- shard 0 of 5 ---
cross-compiling darwin/arm64
--- shard 1 of 5 ---
cross-compiling darwin/amd64
--- shard 2 of 5 ---
cross-compiling linux/arm64
linux/arm64 carlos binary: 47120546 bytes (ceiling 52000000)
--- shard 3 of 5 ---
cross-compiling windows/amd64
--- shard 4 of 5 ---
cross-compiling windows/arm64
=== minio-contract ===
minio contract: arm64, minio RELEASE.2025-09-07T16-13-09Z, mc RELEASE.2025-08-13T08-35-41Z
  minio: cached, digest verified
  mc: cached, digest verified
  minio healthy on 127.0.0.1:20999
Added `local` successfully.
Bucket created successfully `local/contract-bucket`.
Bucket created successfully `local/denied-bucket`.
Created policy `contract-only` successfully.
Added user `scopeduser` successfully.
Attached Policies: [contract-only]
To User: scopeduser
  buckets and scoped user ready
=== RUN   TestDeliveredContractMinio
=== RUN   TestDeliveredContractMinio/put
=== RUN   TestDeliveredContractMinio/get_round-trips_the_body
=== RUN   TestDeliveredContractMinio/list_shows_the_key
=== RUN   TestDeliveredContractMinio/head_and_list_agree_on_the_version
=== RUN   TestDeliveredContractMinio/the_app_cannot_tag_its_own_object
=== RUN   TestDeliveredContractMinio/the_app_cannot_tag_its_own_object/not_by_PutObjectTagging
=== RUN   TestDeliveredContractMinio/the_app_cannot_tag_its_own_object/and_not_by_x-amz-tagging_on_upload
=== RUN   TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route
=== RUN   TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/not_by_CreateMultipartUpload
=== RUN   TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/not_by_CopyObject_with_a_REPLACE_tagging_directive
=== RUN   TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/but_multipart_itself_is_allowed
=== RUN   TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/and_copy_itself_is_allowed
=== RUN   TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/a_POST-policy_forged_tag_with_a_matching_version_is_still_not_a_verdict
=== RUN   TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/and_an_ordinary_tagged-with-something-else_upload_is_allowed
=== RUN   TestDeliveredContractMinio/delete_removes_it
=== RUN   TestDeliveredContractMinio/cross-bucket_denial
--- PASS: TestDeliveredContractMinio (0.02s)
    --- PASS: TestDeliveredContractMinio/put (0.00s)
    --- PASS: TestDeliveredContractMinio/get_round-trips_the_body (0.00s)
    --- PASS: TestDeliveredContractMinio/list_shows_the_key (0.00s)
    --- PASS: TestDeliveredContractMinio/head_and_list_agree_on_the_version (0.00s)
    --- PASS: TestDeliveredContractMinio/the_app_cannot_tag_its_own_object (0.00s)
        --- PASS: TestDeliveredContractMinio/the_app_cannot_tag_its_own_object/not_by_PutObjectTagging (0.00s)
        --- PASS: TestDeliveredContractMinio/the_app_cannot_tag_its_own_object/and_not_by_x-amz-tagging_on_upload (0.00s)
    --- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route (0.01s)
        --- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/not_by_CreateMultipartUpload (0.00s)
        --- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/not_by_CopyObject_with_a_REPLACE_tagging_directive (0.00s)
        --- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/but_multipart_itself_is_allowed (0.00s)
        --- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/and_copy_itself_is_allowed (0.00s)
        --- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/a_POST-policy_forged_tag_with_a_matching_version_is_still_not_a_verdict (0.00s)
        --- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/and_an_ordinary_tagged-with-something-else_upload_is_allowed (0.00s)
    --- PASS: TestDeliveredContractMinio/delete_removes_it (0.00s)
    --- PASS: TestDeliveredContractMinio/cross-bucket_denial (0.00s)
PASS
ok  	github.com/carlosframework/platform/internal/objstore	0.021s
=== RUN   TestT4StorageProbeMinio
2026/10/16 03:06:18 moves: T4: STORE: list: operation error S3: ListObjectsV2, https response error StatusCode: 403, RequestID: 18DEE3A01E8A6739, HostID: 7c420053e0fbcd7f41653fdd92ffa2802c903d05303b1aa15adc2e8bb65d183f, api error SignatureDoesNotMatch: The request signature we calculated does not match the signature you provided. Check your key and signing method.
--- PASS: TestT4StorageProbeMinio (0.01s)
PASS
ok  	github.com/carlosframework/platform/internal/host	0.020s
minio contract: PASS
diff --git a/internal/host/movesrevert.go b/internal/host/movesrevert.go
index 5d36711cf..5a0af451d 100644
--- a/internal/host/movesrevert.go
+++ b/internal/host/movesrevert.go
@@ -692,7 +692,14 @@ func revertProbeWrites(box, dataDir string, earlier, fresh hostclaim.Observed) e
return conflict("no admission")
}
last := f.Holder.Admissions[n-1]
- if last.Box != box || last.Mode != "cold" || last.LossID != "" || last.Generation == "" || last.Generation == e.Holder.Generation {
+ if last.LossID != "" {
+ // A loss-bound admission (as a fence-bound one, which AdmitWriter
+ // refuses below) is verified only inside hostclaim, which does not
+ // expose that; until a hostclaim-owned function re-verifies it
+ // (amadan #300), the step stalls here as it did before the re-read.
+ return conflict("a loss-bound admission, which only hostclaim can verify (amadan #300)")
+ }
+ if last.Box != box || last.Mode != "cold" || last.Generation == "" || last.Generation == e.Holder.Generation {
return conflict("the last admission is not a new generation for this box's writer")
}
in := hostclaim.Admission{
@@ -708,6 +715,9 @@ func revertProbeWrites(box, dataDir string, earlier, fresh hostclaim.Observed) e
return err
}
if err := hostclaim.AdmitWriter(in)(admitted); err != nil {
+ if errors.Is(err, hostclaim.ErrFence) {
+ return conflict("a fence-bound admission, which only hostclaim can verify (amadan #300)")
+ }
return conflict(err.Error())
}
admitted.Gen = e.Gen + 1
diff --git a/internal/host/movesrevert_test.go b/internal/host/movesrevert_test.go
index ee8c7516b..4c195d9d4 100644
--- a/internal/host/movesrevert_test.go
+++ b/internal/host/movesrevert_test.go
@@ -2,10 +2,12 @@ package host
import (
"context"
+ "crypto/ed25519"
"errors"
"fmt"
"os"
"path/filepath"
+ "strconv"
"strings"
"testing"
"time"
@@ -771,6 +773,103 @@ func TestRevertProbedRefusesAForeignWrite(t *testing.T) {
}
}
+// TestRevertProbedRefusesAnUnreplayableAdmission pins today's stall, so it
+// is explicit and not silent (amadan #300). revertProbeWrites replays the
+// probe's admission with hostclaim.AdmitWriter, which cannot vouch for a
+// loss-bound admission (AdmitWriterWithLoss) or a fence-bound one
+// (AdmitWriterWithFencedTargetLoss): their verified flags are hostclaim's
+// own and are not exposed, since exposing them would be a forging surface.
+// So the re-read refuses such an admission with hostclaim.ErrConflict and
+// the step stays at probing, as it did before the re-read existed. The later
+// fix is a hostclaim-owned function that re-verifies those admissions (#300).
+//
+// The recovery fixtures produce both kinds only on a return (a restoring
+// claim); revertProbeWrites reads no op kind, so the real admissions made
+// through the claim's writer are checked against it directly, beside a plain
+// admission over the same fixture that does replay. The revert step itself
+// is then driven with a probe whose one claim write is shaped as a
+// loss-bound admission.
+func TestRevertProbedRefusesAnUnreplayableAdmission(t *testing.T) {
+ admit := func(t *testing.T, f *rcFixture, pub ed25519.PublicKey) (hostclaim.Observed, hostclaim.Observed) {
+ t.Helper()
+ before := f.read()
+ in := f.selectAdmission(before)
+ after, err := f.lossWriter(pub).Admit(t.Context(), before, in.Admission, in.Baseline)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if after.Claim.Gen != before.Claim.Gen+1 {
+ t.Fatalf("the admission wrote gen %d over %d", after.Claim.Gen, before.Claim.Gen)
+ }
+ return before, after
+ }
+ t.Run("a plain admission replays", func(t *testing.T) {
+ f := newRCFixture(t, rcClean)
+ before, after := admit(t, f, nil)
+ if err := revertProbeWrites(f.srcBox, f.dataDir, before, after); err != nil {
+ t.Fatalf("the plain admission was refused: %v", err)
+ }
+ })
+ t.Run("loss-bound", func(t *testing.T) {
+ f := newRCFixture(t, rcClean)
+ pub := f.declare(false, []hostclaim.WriteSource{rcTargetSource("t2", 25), rcTargetSource(poOp, 20), rcCutSource()}, func(c *hostclaim.Claim) {
+ c.Holder.Session = 25
+ c.Holder.Admissions = append(c.Holder.Admissions, hostclaim.AdmissionRecord{Session: 25, Box: "uk-box", Generation: "t2", Predecessor: poOp, Region: "uk", PredecessorRegion: "uk", Mode: "cold"})
+ })
+ before, after := admit(t, f, pub)
+ if a := after.Claim.Holder.Admissions[len(after.Claim.Holder.Admissions)-1]; a.LossID != rcLossID {
+ t.Fatalf("not a loss-bound admission: %+v", a)
+ }
+ if err := revertProbeWrites(f.srcBox, f.dataDir, before, after); !errors.Is(err, hostclaim.ErrConflict) || !strings.Contains(err.Error(), "amadan #300") {
+ t.Fatalf("a loss-bound admission was replayed: %v", err)
+ }
+ })
+ t.Run("fence-bound", func(t *testing.T) {
+ f := newRCFixture(t, rcClean)
+ record := f.fence()
+ before, after := admit(t, f, nil)
+ if after.Claim.Op.Journal["fence_admitted."+record.ID] == "" {
+ t.Fatalf("not a fence-bound admission: %+v", after.Claim.Op.Journal)
+ }
+ if err := revertProbeWrites(f.srcBox, f.dataDir, before, after); !errors.Is(err, hostclaim.ErrConflict) || !strings.Contains(err.Error(), "amadan #300") {
+ t.Fatalf("a fence-bound admission was replayed: %v", err)
+ }
+ })
+ t.Run("the revert step stays at probing", func(t *testing.T) {
+ h := newRevertHarness(t)
+ h.fromStopped(t)
+ h.arm(t)
+ var admitted *hostclaim.Claim
+ h.probe = func(recoveryProbeRequest) (recoveryProbeOutcome, error) {
+ if admitted == nil {
+ rewriteP1Claim(t, h.s, func(c *hostclaim.Claim) {
+ hd := c.Holder
+ session := c.Gen + 1
+ hd.Admissions = append(hd.Admissions, hostclaim.AdmissionRecord{Session: session, Box: h.srcBox, Generation: "lost-gen", Predecessor: hd.Generation, Mode: "cold", Region: "ie", PredecessorRegion: "ie", LossID: rcLossID})
+ hd.Session, hd.Stamp, hd.Generation, hd.WriterBox = session, session, "lost-gen", h.srcBox
+ c.Op.Journal["loss_admitted."+rcLossID] = strconv.FormatUint(session, 10)
+ })
+ admitted = h.claim(t)
+ }
+ return recoveryProbeAnswered, nil
+ }
+ err := h.w().revertSource(context.Background(), h.s, h.read(t, p1Host), nil)
+ if admitted == nil {
+ t.Fatal("the probe never ran")
+ }
+ if !errors.Is(err, hostclaim.ErrConflict) || !strings.Contains(err.Error(), "amadan #300") {
+ t.Fatalf("the revert's probed took a loss-bound admission: %v", err)
+ }
+ c := h.claim(t)
+ if c.Gen != admitted.Gen || c.Op.Phase != hostclaim.PhaseProbing || c.Op.Journal[hostclaim.JournalRevPhase] != hostclaim.PhaseProbing {
+ t.Fatalf("the claim after the refusal: gen %d (admitted %d), %+v", c.Gen, admitted.Gen, c.Op)
+ }
+ if m := h.move(t); m.ReopenedAt != nil || h.route.HoldOp != p1ID {
+ t.Fatalf("the refusal reopened: %+v, row %+v", m, h.route)
+ }
+ })
+}
+
// TestRevertReplays: a crash after any of the revert's side effects, then a
// restarted agent, still reaches the close, with the evidence asked once it
// is journalled, the hold only ever restoring under the move id, and the
diff --git a/internal/provision/testdata/plan14-root-reads-home-part2.tsv b/internal/provision/testdata/plan14-root-reads-home-part2.tsv
index 90bc3312b..06c8758fe 100644
--- a/internal/provision/testdata/plan14-root-reads-home-part2.tsv
+++ b/internal/provision/testdata/plan14-root-reads-home-part2.tsv
@@ -7,6 +7,7 @@
# Task 5: the Phase 1 move door admits a home target under host claims. Server.handleAPIInstanceMoveRequest gains its home refusals before any write (MV-N510 without host claims, MV-N511 for a home-named box, read from the same edges-file listing through movePlacements) and its sentences name the home; Server.startClaimModeMove's sentences name the home. Read sets unchanged (the same file:Exec and file:Read kinds), only offsets moved: both re-frozen at main's disposition (class, task and basis unchanged; kinds and digest refreshed), they add two frozen declarations; the seal moves. internal/console's source envelope is refreshed.
# Merge of main at ee35dee (2026-10-16): this overlay now loads after census-ruling-seed (FS-R6, FS-R10) and moves-overcap-cut-236. No unit of this overlay is in either; every row still matches its scan unchanged. Only the internal/activator and internal/host envelopes are re-pinned to the merged tree, and the seal moves with the merged freeze.
# The ruling's condition (2026-10-16): revertRun.probing takes the re-read claim only when it is the step's own read plus the probe wake's own writes, replayed with the claim's own transitions (hostclaim.AdmitWriter's cold admission for this box's writer, with its origin, then the restore's re-tag through FS-R11's claimRestoreObservation.retag) and compared exactly; anything else is a conflict, as the stale CAS was. A reverting op's probe attempts are journalled on the box, never reserved in the claim. The check reads nothing, so the read set is the same one file:Read: revertRun.probing is re-pinned at the same disposition (only its digest and the read's offset moved), its frozen declaration replaced, the internal/host envelope re-pinned, and the seal moves.
+# amadan #300 (2026-10-16): revertProbeWrites names a loss- or fence-bound admission, which only hostclaim can verify, in its refusal; revertRun.probing is unchanged and only the internal/host envelope is re-pinned.
@legacy 849ce111c61c3dcedd80595348d0a26c67fb47462d497fba63d52d2c94e1d5d0 internal/activator/backend_exec.go ExecBackend.LaunchEnvironment legacy accepted-legacy accepted-legacy;rev3=J1;basis=root-route whole:Route@139 e8853e15b427b80a701b5e10b565f7d4f0b778e0c8185da63266e2408f6b4113
internal/activator/backend_exec.go ExecBackend.LaunchEnvironment legacy accepted-legacy accepted-legacy;rev3=J1;basis=root-route whole:Route@139 e8853e15b427b80a701b5e10b565f7d4f0b778e0c8185da63266e2408f6b4113
@legacy 1680e7269c40013e72c2f0a39abc13ee75cecadf2b3b8aa3d3a0278c060458ce internal/activator/backend_exec.go ExecBackend.LaunchLayers legacy accepted-legacy accepted-legacy;rev3=J1;basis=root-route registry:Socket@751,whole:Route@179,whole:Route@747 66a14ac6d0a4c2c9333adf45a792407d8cc4fa389416feb31fcf58616fc5b611
@@ -37,5 +38,5 @@ internal/console/apiinstancemove.go Server.handleAPIInstanceMoveRequest legacy a
internal/console/apiinstancemove.go Server.startClaimModeMove legacy accepted-legacy accepted-legacy;rev3=J4;basis=root-input file:Exec@3526,file:Exec@565 c4e47ebd1e84c89d72ed8bbb25e65af8e365f4993827682ca55de9fd2d2d90bb
@coverage internal/activator 9511daaa77f912b86ab84d8e30b7e6c6b1b6d0b2989e79ec6bda2c0471daf1d4
@coverage internal/console a565401e44e7797120ad52136146d84c8e0d430ceeb6a6f1c5e1ea0ed7a78c7e
-@coverage internal/host 585d33e2b501102e08b0fc9fd9cc074720ee9800d262270b59dfeb90ee50d0ce
+@coverage internal/host cc00e77188da3a5a20b3099adee45d829e07bd417fa73bdb8ebb0add2af4e1c2
@coverage internal/moves 51ae19338bf9f92d45cc166f9fb71cdd3280c617892f71bb9ce67b2b00a7ae27