carlos / platform Public

host: a moves pass's failed listing fails only the offline view's holds; the edges file loop loads the view's snapshot once (Task 1 fix round 1)

- Minor 1: movesPass's listing failure calls failHolds, which publishes a
  failed holds snapshot (next revision, onChange told) and leaves the
  snapshot keep and elsewhere read at the last good listing's, so
  scheduleSkip keeps protecting a ready row whose move names another
  region. Reconcile's own failed listing (fail) is unchanged.
  TestHoldingFailsAloneOnAMovesPassListingFailure pins it, restore
  included.
- Minor 2: edgesFileLoop loads the snapshot once per tick; the digest
  and the writer's source (holdsAt) come from it.
- Minor 4: holdingSource says only ID and SelfServe may be read.
- Census: edgesFileLoop and the internal/host envelope re-pinned in
  place; the seal moves.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
claude agent, operated by paul@keymail.dev 5d7647012e26a75201494b3c996192664f96271b parent 1765833
7 files changed, +177 −36
  • docs/superpowers/notes/census-proposal-self-serve-moves-3.md +7 −1
  • internal/host/edgesfile.go +14 −4
  • internal/host/edgesfile_holding_test.go +120 −14
  • internal/host/movesoffline.go +23 −8
  • internal/host/movespass.go +5 −4
  • internal/provision/plan14_root_reads_test.go +4 −2
  • internal/provision/testdata/plan14-root-reads-self-serve-moves-3.tsv +4 −3

CI failed — run details

CI log
=== gate ===
sigwho: pid 2 ppid 1 pgid 0 sid 0; child pid 3
.amadan/test-gate.sh
gate: 2026-10-24T03:07:33Z on aarch64, 32 cpus, 61350 MB available, go1.26.8 linux/arm64
gate: +0s start launchctx
gate: +0s start vet
gate: +0s start gofmt
gate: +1s start rest
gate: +10s start internal_console@0
gate: +10s start internal_console@1
gate: +10s start internal_console@2
gate: +10s start internal_console@3
gate: +10s start internal_console@4
gate: +10s start internal_console@5
gate: +10s start internal_console@6
gate: +10s start internal_console@7
gate: +10s waiting for internal_console's shards before the next split package
gate: +173s start root@0
gate: +173s start root@1
gate: +173s start root@2
gate: +173s waiting for root's shards before the next split package

amadan: step gate failed: signal: terminated

amadan: job failed at step gate — its output is above, not at the tail of this log
diff --git a/docs/superpowers/notes/census-proposal-self-serve-moves-3.md b/docs/superpowers/notes/census-proposal-self-serve-moves-3.md
index c1c0e74ee..d71b2282a 100644
--- a/docs/superpowers/notes/census-proposal-self-serve-moves-3.md
+++ b/docs/superpowers/notes/census-proposal-self-serve-moves-3.md
@@ -11,7 +11,7 @@ if errs := checkRootReadLegacy(inv, seal); len(errs) != 0 {
if err = loadRootReadInventory(root+"/internal/provision/testdata/plan14-root-reads-self-serve-moves-3.tsv", &inv, true); err != nil {
t.Fatal(err)
}
-seal = "272a2c07e3ed3e8df91d45561b002492f3f358527011339659043c2a137a80fd"
+seal = "3d790560864a2ffead59b55836b00029501e54ec59f9893c64ea10e57977ff5a"
```
The record-write overlay is `plan14SelfServeMoves3WriteCensusInventory` in `internal/provision/self_serve_moves3_census_test.go`, built over plan 2's inventory; `TestPlan9B8RecordWriteCensus` takes it. Later tasks of the plan extend both.
@@ -54,3 +54,9 @@ Envelopes re-pinned:
- `internal/registry`
The record-write census is unchanged by this task. The edges-file writer still makes its one bucket write through `putReplacing` (nonrecord `sel:Put`, unchanged), to `control/edges/<box>.json`, never a record key. `holdingEntries` writes nothing. The registry and reconciler changes are SQLite. `plan14SelfServeMoves3WriteCensusInventory` therefore adds and removes nothing yet.
+
+### Task 1 fix round 1
+
+The review's Minor 1: a moves pass's listing failure now fails only the offline view's holds (`failHolds`: a failed holds snapshot at the next revision, the subscriber told), and the snapshot `keep` and `elsewhere` read stays the last good listing's. So `scheduleSkip` keeps protecting a ready row whose move names another region. Reconcile's own failed listing (`refreshOfflineView`, `fail`) keeps today's behaviour. Minor 2: the edges file loop loads the view's snapshot once per tick, for both the schedule's digest and the writer's source (`holdsAt`).
+
+`edgesFileLoop` is re-pinned in place. Its read site is unchanged; the change is the frozen fingerprint, which moves the seal to `3d790560…`. The `internal/host` envelope is re-pinned in place. No root read, class or task is added, and the record-write census is unchanged.
diff --git a/internal/host/edgesfile.go b/internal/host/edgesfile.go
index a4c7b1db2..7b8d72524 100644
--- a/internal/host/edgesfile.go
+++ b/internal/host/edgesfile.go
@@ -410,8 +410,10 @@ type holdingEntry struct {
}
// holdingSource answers which offline move the box's last record listing
-// carried for host (offlineView.holding, read through holdsOf for one
-// pass): false when it carried none.
+// carried for host (offlineView.holding, read through holdsOf or holdsAt for
+// one pass): false when it carried none. A caller reads only the returned
+// move's ID and SelfServe: only those are copied into the immutable
+// snapshot, and the other pointer fields are shared with the listing.
type holdingSource func(host string) (provision.MoveState, bool)
// edgesFilePass builds this box's edges file from the routes currently in
@@ -1040,13 +1042,21 @@ func edgesFileLoop(reg *registry.Registry, lifetimes bool, offline func() *offli
// EC2 each read can block for the probe's whole timeout.
eip := readPublicIP()
for now := range tick.C {
+ // The view's holds are loaded once per tick: the schedule's digest
+ // and the writer's source are the same snapshot.
var view *offlineView
+ var held *offlineHolds
+ var holds holdingSource
if lifetimes && offline != nil {
view = offline()
}
+ if view != nil {
+ held = view.snapshot()
+ holds = holdsAt(held)
+ }
fp, err := reg.Fingerprint()
if err == nil && view != nil {
- fp += "\x1d" + view.snapshot().digest()
+ fp += "\x1d" + held.digest()
}
if err != nil {
// Not fatal: the timer still publishes. Counted and throttled the
@@ -1068,7 +1078,7 @@ func edgesFileLoop(reg *registry.Registry, lifetimes bool, offline func() *offli
ident.Headroom = headroomFor(headroomCapabilities(ident.Capabilities), dataDir, reg)
ident = withMovesFields(ident, dataDir, optDir)
ident = withSeedsFields(ident, dataDir, optDir)
- err = edgesFileWriteClaims(ctx, store, reg, ident, eip, &last, state.timed(), claims, lifetimes, holdsOf(view))
+ err = edgesFileWriteClaims(ctx, store, reg, ident, eip, &last, state.timed(), claims, lifetimes, holds)
if err != nil {
// Throttled: a bucket that refuses every write (an IAM Deny, an
// outage) is retried on a backoff, and each retry must not be a
diff --git a/internal/host/edgesfile_holding_test.go b/internal/host/edgesfile_holding_test.go
index 05898d010..4154fb0c8 100644
--- a/internal/host/edgesfile_holding_test.go
+++ b/internal/host/edgesfile_holding_test.go
@@ -499,7 +499,7 @@ func TestHoldingFixtureIsTheWritersOutput(t *testing.T) {
// The production wiring: runAgent hands the edges file loop the armed
// worker's offline view, and the loop hands the writer that view's holds,
-// read once per pass (holdsOf).
+// read once per pass (holdsAt over one snapshot).
func TestHoldingSourceIsWiredIntoTheEdgesFileLoop(t *testing.T) {
handed := false
inspectFunc(t, "agent.go", "runAgent", func(n ast.Node) {
@@ -516,22 +516,128 @@ func TestHoldingSourceIsWiredIntoTheEdgesFileLoop(t *testing.T) {
if !handed {
t.Error("runAgent's edgesFileLoop call does not pass armedOfflineView")
}
- passed := false
+ // The loop loads the view's snapshot once per tick (fix round 1, Minor
+ // 2): the writer's source is holdsAt over it and the schedule's digest
+ // is its digest.
+ passed, loads, sourced, digested := false, 0, false, false
inspectFunc(t, "edgesfile.go", "edgesFileLoop", func(n ast.Node) {
- call, ok := n.(*ast.CallExpr)
- if !ok {
- return
- }
- if id, ok := call.Fun.(*ast.Ident); !ok || id.Name != "edgesFileWriteClaims" || len(call.Args) == 0 {
- return
- }
- if h, ok := call.Args[len(call.Args)-1].(*ast.CallExpr); ok {
- if id, ok := h.Fun.(*ast.Ident); ok && id.Name == "holdsOf" {
- passed = true
+ switch n := n.(type) {
+ case *ast.CallExpr:
+ if sel, ok := n.Fun.(*ast.SelectorExpr); ok {
+ switch sel.Sel.Name {
+ case "snapshot":
+ loads++
+ case "digest":
+ if x, ok := sel.X.(*ast.Ident); ok && x.Name == "held" {
+ digested = true
+ }
+ }
+ }
+ if id, ok := n.Fun.(*ast.Ident); ok && id.Name == "holdsAt" && len(n.Args) == 1 {
+ if a, ok := n.Args[0].(*ast.Ident); ok && a.Name == "held" {
+ sourced = true
+ }
+ }
+ if id, ok := n.Fun.(*ast.Ident); ok && id.Name == "edgesFileWriteClaims" && len(n.Args) > 0 {
+ if a, ok := n.Args[len(n.Args)-1].(*ast.Ident); ok && a.Name == "holds" {
+ passed = true
+ }
}
}
})
- if !passed {
- t.Error("edgesFileLoop does not hand the writer holdsOf(view)")
+ if !passed || loads != 1 || !sourced || !digested {
+ t.Errorf("edgesFileLoop: writer gets holds %v, snapshot loads %d, holds = holdsAt(held) %v, held.digest() %v; want true, 1, true, true",
+ passed, loads, sourced, digested)
}
}
+
+// A moves pass whose listing fails fails the holds only (fix round 1 of
+// Task 1, Minor 1): the snapshot keep and elsewhere read stays the last good
+// listing's, so scheduleSkip keeps protecting a ready row whose move names
+// another region (the target's row in a revert after open), while holding
+// answers nothing, the subscriber sees a failed snapshot and the edges file
+// republishes without offline_moves. The next good listing restores both.
+func TestHoldingFailsAloneOnAMovesPassListingFailure(t *testing.T) {
+ ctx := context.Background()
+ const held, other = "held.example.com", "other.example.com"
+ wd := newOffWorld(t)
+ wd.create(held)
+ wd.create(other)
+ // offHost reverts after open: its record names the home, and the target
+ // (uk-box) keeps its row ready; held is landing on uk-box, held there.
+ wd.move(func(rec *provision.Record) error {
+ rec.Move = &provision.MoveState{Kind: provision.MoveKindOffline, ID: offID, From: "", To: "uk", State: provision.OfflineReverting}
+ return nil
+ })
+ wd.moveHost(held, func(rec *provision.Record) error {
+ rec.Region = "uk"
+ rec.Move = &provision.MoveState{Kind: provision.MoveKindOffline, ID: holdMoveA, From: "", To: "uk", State: provision.OfflineLanded}
+ return nil
+ })
+ b := wd.box("uk-box", "uk", nil)
+ heldInstance(t, b.reg, held, "landing", holdMoveA, 1, 1)
+ v := b.w.cfg.Offline
+ var got []edge.HoldSnapshot
+ v.Subscribe(func(s edge.HoldSnapshot) { got = append(got, s) })
+ publish := func() edgesFile {
+ _, f := publishHoldingFrom(t, b.reg, holdsOf(v))
+ return f
+ }
+ answers := func(when string, wantHeld bool) {
+ t.Helper()
+ if !v.elsewhere(offHost) || !v.keep(offHost) || v.keep(other) {
+ t.Fatalf("%s: elsewhere(%s) %v, keep(%s) %v, keep(%s) %v; want the good listing's true, true, false",
+ when, offHost, v.elsewhere(offHost), offHost, v.keep(offHost), other, v.keep(other))
+ }
+ if _, ok := v.holding(held); ok != wantHeld {
+ t.Fatalf("%s: holding(%s) = %v, want %v", when, held, ok, wantHeld)
+ }
+ if f := publish(); (len(f.OfflineMoves) == 1) != wantHeld {
+ t.Fatalf("%s: holding entries %+v, want one: %v", when, f.OfflineMoves, wantHeld)
+ }
+ }
+
+ // The good listing, as reconcile reads it.
+ refreshOfflineView(ctx, wd.s, v)
+ answers("after a good listing", true)
+
+ failing := failingListStore{Store: wd.s, prefix: "apps/", err: errors.New("listing refused")}
+ if err := movesPass(ctx, failing, b.w); err == nil {
+ t.Fatal("the pass's listing did not fail")
+ }
+ answers("after the moves pass's listing failed", false)
+ if n := len(got); n < 2 || !got[n-1].Failed || got[n-1].Holds != nil || got[n-1].Rev != got[n-2].Rev+1 {
+ t.Fatalf("the subscriber saw %+v, want a failed snapshot last at the next revision", got)
+ }
+ // scheduleSkip reads the armed worker's view, as the agent's scheduler does.
+ prev := armedMovesWorker.Load()
+ t.Cleanup(func() { armedMovesWorker.Store(prev) })
+ b.w.cfg.OfflineCapable = true
+ armedMovesWorker.Store(b.w)
+ if !scheduleSkip(registry.Route{Host: offHost, Status: "ready"}) {
+ t.Fatal("scheduleSkip stopped protecting the reverting host's ready row")
+ }
+
+ // The next good listing restores both.
+ b.pass()
+ answers("after the next good moves pass", true)
+ if s := got[len(got)-1]; s.Failed || s.Holds[held].MoveID != holdMoveA {
+ t.Fatalf("the subscriber's last snapshot %+v, want the restored holds", s)
+ }
+
+ // Reconcile's own failed listing keeps today's behaviour: keep fails
+ // closed and the holds fail with it.
+ refreshOfflineView(ctx, failing, v)
+ if !v.keep(other) || v.elsewhere(offHost) {
+ t.Fatalf("reconcile's failed listing: keep(%s) %v, elsewhere(%s) %v; want true, false", other, v.keep(other), offHost, v.elsewhere(offHost))
+ }
+ if _, ok := v.holding(held); ok {
+ t.Fatal("reconcile's failed listing left the holds")
+ }
+}
+
+// publishHoldingFrom is publishHolding with lifetimes on and region HA off.
+func publishHoldingFrom(t *testing.T, reg *registry.Registry, holds holdingSource) ([]byte, edgesFile) {
+ t.Helper()
+ return publishHolding(t, reg, nil, true, holds)
+}
diff --git a/internal/host/movesoffline.go b/internal/host/movesoffline.go
index a9290f16a..1065f9e76 100644
--- a/internal/host/movesoffline.go
+++ b/internal/host/movesoffline.go
@@ -292,13 +292,16 @@ func (h *offlineHolds) adapt() edge.HoldSnapshot {
return s
}
-// publish stores keep's snapshot and the next revision of the holds, and
-// hands the holds to onChange, all under pubMu, so the two views of one
-// listing are stored together.
+// publish stores keep's snapshot (unless keep is nil: failHolds leaves the
+// last one) and the next revision of the holds, and hands the holds to
+// onChange, all under pubMu, so the two views of one listing are stored
+// together.
func (v *offlineView) publish(keep *offlineSnapshot, holds map[string]provision.MoveState, failed bool) {
v.pubMu.Lock()
defer v.pubMu.Unlock()
- v.snap.Store(keep)
+ if keep != nil {
+ v.snap.Store(keep)
+ }
next := &offlineHolds{Holds: holds, Failed: failed}
if prev := v.holds.Load(); prev != nil {
next.Rev = prev.Rev + 1
@@ -340,9 +343,13 @@ func holdsOf(v *offlineView) holdingSource {
if v == nil {
return nil
}
- return v.snapshot().holding
+ return holdsAt(v.snapshot())
}
+// holdsAt is the writer's holdingSource over the one snapshot h a pass
+// loaded (nil h: nothing published yet, so no move is known).
+func holdsAt(h *offlineHolds) holdingSource { return h.holding }
+
type offlineSnapshot struct {
moves map[string]provision.MoveState // host -> its offline move
owners map[string][2]string // host -> the account and app its record lives under
@@ -396,11 +403,19 @@ func (v *offlineView) refresh(recs []provision.Record, unreadable map[string]boo
v.publish(next, holds, false)
}
-// fail records a listing that could not be read (at agent start, or a moves
-// pass's): keep answers true for every host until a listing is read, and the
-// holds publish a failed snapshot, which knows no move.
+// fail records a listing that could not be read outside a moves pass (at
+// agent start, or reconcile's own read, refreshOfflineView): keep answers
+// true for every host until a listing is read, and the holds publish a
+// failed snapshot, which knows no move.
func (v *offlineView) fail() { v.publish(&offlineSnapshot{failed: true}, nil, true) }
+// failHolds records a moves pass's listing that could not be read: only the
+// holds publish a failed snapshot (no holding entry outlives it), while the
+// snapshot keep and elsewhere read stays the last good listing's, so
+// scheduleSkip keeps protecting a row whose move names another region. The
+// next listing, read or failed, replaces both.
+func (v *offlineView) failHolds() { v.publish(nil, nil, true) }
+
// built reports whether any listing, read or failed, has been recorded.
func (v *offlineView) built() bool { return v.snap.Load() != nil }
diff --git a/internal/host/movespass.go b/internal/host/movespass.go
index ca882cd5a..1ee44482c 100644
--- a/internal/host/movespass.go
+++ b/internal/host/movespass.go
@@ -1164,11 +1164,12 @@ func movesPass(ctx context.Context, s release.Store, w *movesWorker) error {
// Judged, a record its slot no longer vouches for is not listed.
recs, unreadable, err := provision.ListAllRecordsStrictJudged(ctx, s, cfg.Judge)
if err != nil {
- // The offline view records the failure (movesoffline.go): its holds
- // publish a failed snapshot, so no holding entry outlives a listing
- // that could not be read, and keep fails closed until one reads.
+ // The offline view's holds record the failure (movesoffline.go): a
+ // failed snapshot, so no holding entry outlives a listing that could
+ // not be read. What keep and elsewhere read stays the last good
+ // listing's.
if cfg.Offline != nil {
- cfg.Offline.fail()
+ cfg.Offline.failHolds()
}
return fmt.Errorf("moves: list records: %w", err)
}
diff --git a/internal/provision/plan14_root_reads_test.go b/internal/provision/plan14_root_reads_test.go
index ace1ca59a..f3ab1ddfb 100644
--- a/internal/provision/plan14_root_reads_test.go
+++ b/internal/provision/plan14_root_reads_test.go
@@ -1387,8 +1387,10 @@ func TestPlan14RootReadCensus(t *testing.T) {
// seven accepted-legacy units (the route_aliases schema, its scanner,
// writer and fingerprint; the reconciler's two provenance writers; the
// edges file loop) and freezes two new ones (keepDerived and
- // edgesfile.AliasPublishable), which moves the seal.
- seal = "272a2c07e3ed3e8df91d45561b002492f3f358527011339659043c2a137a80fd"
+ // edgesfile.AliasPublishable), which moves the seal. Its fix round 1
+ // re-pins edgesFileLoop in place (the view's snapshot loaded once per
+ // tick), which moves it again.
+ seal = "3d790560864a2ffead59b55836b00029501e54ec59f9893c64ea10e57977ff5a"
errs := append(compareRootReads(got, inv), checkRootReadLegacy(inv, seal)...)
errs = append(errs, checkRootReadDeclarations(got, inv)...)
if len(errs) > 0 {
diff --git a/internal/provision/testdata/plan14-root-reads-self-serve-moves-3.tsv b/internal/provision/testdata/plan14-root-reads-self-serve-moves-3.tsv
index 42fd91c5d..ca1bf7afe 100644
--- a/internal/provision/testdata/plan14-root-reads-self-serve-moves-3.tsv
+++ b/internal/provision/testdata/plan14-root-reads-self-serve-moves-3.tsv
@@ -1,5 +1,6 @@
# Census overlay self-serve-moves-3, marked rev3=PROPOSED-self-serve-moves-3 (not an accepted ruling; docs/superpowers/notes/census-proposal-self-serve-moves-3.md). Loaded after the self-serve-moves-2 overlay.
# Task 1 (alias provenance and the writer's holding entries), rev3=PROPOSED-self-serve-moves-3: no record envelope changes and no root read, record read or task is added. The registry gains route_aliases.explicit (RouteAlias.Explicit, the tracked field Explicit in @fields below): the schema, its scanner, writer and fingerprint are re-pinned. The domains reconciler writes provenance (ReconcileAliases and deriveInstanceAliases re-pinned; keepDerived new). The edges-file writer publishes held rows as offline_moves holding entries (edgesFileWriteClaims re-pinned at F,J1; holdingEntries new at F,J1; edgesfile.AliasPublishable new) and its loop and runAgent wire the offline view in. Seven accepted-legacy units take new frozen fingerprints and two new ones are frozen, which moves the seal. Envelopes re-pinned: internal/domains, internal/edge (holding.go: the HoldSnapshot and HoldState DTOs, no read), internal/edgesfile (eligible.go), internal/host (edgesfile.go, movesoffline.go, movespass.go, agent.go, e2e_bridge.go) and internal/registry.
+# Task 1 fix round 1, rev3=PROPOSED-self-serve-moves-3: a moves pass's listing failure fails only the offline view's holds (failHolds), the snapshot keep and elsewhere read staying the last good listing's; the edges file loop loads the view's snapshot once per tick. edgesFileLoop re-pinned in place (its read site unchanged), which moves the seal; the internal/host envelope re-pinned in place. No root read, class or task is added.
@legacy 652c416a0cd27a153fa259d76ab20538480e2c3afd98cbec6af083902b17f5ff internal/domains/aliases.go ReconcileAliases legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-3;basis=root-route; the agent (/opt/carlos/carlos agent, carlos-edge.service, user carlos), from its fast-converge and instances loops: self-serve moves 1's body (rev3=J1, the catch-all's labels counted through domains.InstanceLabelCountsJudged), which now writes each alias row's provenance: an exact claim on the row's own name upserts it explicit (every pass, so a legacy row is upgraded on the first), and the synthetic "*.<host>" catch-all row not explicit; registry read sites unchanged, offsets moved; a report the edges file lists holding aliases by, not a decision registry:Account@15605,registry:Account@15618,registry:Alias@15220,registry:Alias@15260,registry:Alias@15330,registry:App@12165,registry:App@12365,registry:App@12521,registry:App@12722,registry:App@15631,registry:App@15640,registry:App@2026,registry:App@2222,registry:App@6036,registry:App@6235,registry:Host@15590,registry:Host@15879,registry:Host@15932,registry:Host@16004,registry:Host@16516,registry:Host@16608,registry:Host@16667,registry:Host@16748,registry:Host@406,registry:Prefix@16524,registry:Prefix@16622,registry:Prefix@16675,registry:Prefix@16756,registry:Status@8899,whole:Route@12134,whole:Route@12209,whole:Route@12356,whole:Route@12418,whole:Route@12561,whole:Route@12717,whole:Route@13613,whole:Route@1369,whole:Route@14825,whole:Route@14833,whole:Route@2066,whole:Route@2217,whole:Route@2273,whole:Route@359,whole:Route@388,whole:Route@399,whole:Route@417,whole:Route@5382,whole:Route@6080,whole:Route@6226,whole:Route@8229,whole:Route@8681,whole:Route@8843,whole:RouteAlias@14841,whole:RouteAlias@15195,whole:RoutePath@10426,whole:RoutePath@16409,whole:RoutePath@16451,whole:RoutePath@16565,whole:RoutePath@16595,whole:WildcardDomain@12933,whole:WildcardDomain@15520,whole:WildcardDomain@15569,whole:WildcardDomain@15732,whole:WildcardDomain@15838,whole:WildcardDomain@15867 edef8a1bf06cab5666a52ec19840c54f02913ddc67a32da7f6197db1606556a4
internal/domains/aliases.go ReconcileAliases legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-3;basis=root-route; the agent (/opt/carlos/carlos agent, carlos-edge.service, user carlos), from its fast-converge and instances loops: self-serve moves 1's body (rev3=J1, the catch-all's labels counted through domains.InstanceLabelCountsJudged), which now writes each alias row's provenance: an exact claim on the row's own name upserts it explicit (every pass, so a legacy row is upgraded on the first), and the synthetic "*.<host>" catch-all row not explicit; registry read sites unchanged, offsets moved; a report the edges file lists holding aliases by, not a decision registry:Account@15605,registry:Account@15618,registry:Alias@15220,registry:Alias@15260,registry:Alias@15330,registry:App@12165,registry:App@12365,registry:App@12521,registry:App@12722,registry:App@15631,registry:App@15640,registry:App@2026,registry:App@2222,registry:App@6036,registry:App@6235,registry:Host@15590,registry:Host@15879,registry:Host@15932,registry:Host@16004,registry:Host@16516,registry:Host@16608,registry:Host@16667,registry:Host@16748,registry:Host@406,registry:Prefix@16524,registry:Prefix@16622,registry:Prefix@16675,registry:Prefix@16756,registry:Status@8899,whole:Route@12134,whole:Route@12209,whole:Route@12356,whole:Route@12418,whole:Route@12561,whole:Route@12717,whole:Route@13613,whole:Route@1369,whole:Route@14825,whole:Route@14833,whole:Route@2066,whole:Route@2217,whole:Route@2273,whole:Route@359,whole:Route@388,whole:Route@399,whole:Route@417,whole:Route@5382,whole:Route@6080,whole:Route@6226,whole:Route@8229,whole:Route@8681,whole:Route@8843,whole:RouteAlias@14841,whole:RouteAlias@15195,whole:RoutePath@10426,whole:RoutePath@16409,whole:RoutePath@16451,whole:RoutePath@16565,whole:RoutePath@16595,whole:WildcardDomain@12933,whole:WildcardDomain@15520,whole:WildcardDomain@15569,whole:WildcardDomain@15732,whole:WildcardDomain@15838,whole:WildcardDomain@15867 edef8a1bf06cab5666a52ec19840c54f02913ddc67a32da7f6197db1606556a4
@legacy 887eab39c9584891268b9afedcd81c995849a97545258b3eacc506b5c0165b23 internal/domains/aliases.go deriveInstanceAliases legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-3;basis=root-route; the agent (/opt/carlos/carlos agent, carlos-edge.service, user carlos): self-serve moves 1's body, whose already-the-row-we-want skip now also compares the existing row's provenance (registry:Explicit, one new site): a derived name left explicit by a detached exact claim on the same target and owner is rewritten not explicit, and both keep-as-it-is paths (a listing failure, an unjudged label) hand the kept row to keepDerived; the other registry read sites unchanged, offsets moved; no new route registry:Account@2163,registry:Account@3516,registry:Account@4587,registry:Alias@2225,registry:Alias@2293,registry:Alias@2337,registry:Alias@557,registry:App@1686,registry:App@2189,registry:App@3542,registry:App@4613,registry:Explicit@4632,registry:Host@1843,registry:Host@4566,registry:Host@4576,registry:Host@4787,registry:Provisioned@1636,registry:Replica@1672,registry:Slug@1775,whole:Route@1620,whole:Route@1726,whole:Route@3058,whole:RouteAlias@2143,whole:RouteAlias@2377,whole:RouteAlias@3498,whole:RouteAlias@3867,whole:RouteAlias@4548,whole:RouteAlias@509,whole:RouteAlias@539,whole:RouteAlias@552,whole:RouteAlias@568 42d5a40cccb38c31f306b49c00fde7482e4ee0b3cbf6a6a2dfbaac06592ef883
@@ -8,8 +9,8 @@ internal/domains/aliases.go deriveInstanceAliases legacy accepted-legacy accepte
internal/domains/aliases.go keepDerived legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-3;basis=root-route; the agent (/opt/carlos/carlos agent, carlos-edge.service, user carlos): new, deriveInstanceAliases' provenance half of keeping a catch-all's existing row for a pass: reads the kept row's Alias and Explicit and, when no listed exact claim backs an explicit row, upserts the same row not explicit (target, owner and stamps unchanged); no file, record, release or claim read registry:Alias@116,registry:Alias@257,registry:Explicit@140,registry:Explicit@95,whole:RouteAlias@186 888039b448e6f976d3603c644b7b3d383bb342ed7a5008074b3e9b1e3d8be90b
@legacy 21e0a71fec437d44da824257175004f9a51e771f9fc3beb7f3081b1bf50919e7 internal/edgesfile/eligible.go AliasPublishable legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-3;basis=root-route; the agent (/opt/carlos/carlos agent, carlos-edge.service, user carlos): new, the edges-file writer's one alias publication rule (a non-empty, non-catch-all name stamped pointing, its primary served or a held row the writer publishes, not shadowed by a served row), shared by the served aliases, the holding entries and (plan 3 Task 2) the edge's local holds; a pure predicate over one registry alias row; no file, record, release or claim read registry:Alias@126,registry:Alias@90,registry:DNSStatus@144 f62afa4edc8bccc41563f5843391ee13730d11430601469414bff07f3654874a
internal/edgesfile/eligible.go AliasPublishable legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-3;basis=root-route; the agent (/opt/carlos/carlos agent, carlos-edge.service, user carlos): new, the edges-file writer's one alias publication rule (a non-empty, non-catch-all name stamped pointing, its primary served or a held row the writer publishes, not shadowed by a served row), shared by the served aliases, the holding entries and (plan 3 Task 2) the edge's local holds; a pure predicate over one registry alias row; no file, record, release or claim read registry:Alias@126,registry:Alias@90,registry:DNSStatus@144 f62afa4edc8bccc41563f5843391ee13730d11430601469414bff07f3654874a
-@legacy 73aa8aadc0b840c6558d128d749b06948799dfa515a8b92ff8780da7b98c1439 internal/host/edgesfile.go edgesFileLoop legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-3;basis=the agent (/opt/carlos/carlos agent, carlos-edge.service, user carlos): self-serve moves 1's body, which also takes the armed worker's offline view (runAgent's armedOfflineView, read each tick), folds its holds' digest into the fingerprint the schedule watches with lifetimes on, and hands the writer that view's holds for the pass (holdsOf); read site unchanged, offset moved; a report, not a decision; self-serve moves 3 Task 1 file:Read@2786 42d5ba7d2903b7abc45c5c5be3bc7cbc36e5b7cb19416fe708139e6f100d3781
-internal/host/edgesfile.go edgesFileLoop legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-3;basis=the agent (/opt/carlos/carlos agent, carlos-edge.service, user carlos): self-serve moves 1's body, which also takes the armed worker's offline view (runAgent's armedOfflineView, read each tick), folds its holds' digest into the fingerprint the schedule watches with lifetimes on, and hands the writer that view's holds for the pass (holdsOf); read site unchanged, offset moved; a report, not a decision; self-serve moves 3 Task 1 file:Read@2786 42d5ba7d2903b7abc45c5c5be3bc7cbc36e5b7cb19416fe708139e6f100d3781
+@legacy e60f954dfefb64868de2be178e7690507ff20fb47c2cc7f322812ac4f7b48c5e internal/host/edgesfile.go edgesFileLoop legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-3;basis=the agent (/opt/carlos/carlos agent, carlos-edge.service, user carlos): self-serve moves 1's body, which also takes the armed worker's offline view (runAgent's armedOfflineView, read each tick), folds its holds' digest into the fingerprint the schedule watches with lifetimes on, and hands the writer that view's holds for the pass, the snapshot loaded once per tick for both (holdsAt; fix round 1); read site unchanged, offset moved; a report, not a decision; self-serve moves 3 Task 1 file:Read@2786 16e4a3a979fafef37cbd9f932ad97496e2bcbdc2675eeb4f34549c409ddf1183
+internal/host/edgesfile.go edgesFileLoop legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-3;basis=the agent (/opt/carlos/carlos agent, carlos-edge.service, user carlos): self-serve moves 1's body, which also takes the armed worker's offline view (runAgent's armedOfflineView, read each tick), folds its holds' digest into the fingerprint the schedule watches with lifetimes on, and hands the writer that view's holds for the pass, the snapshot loaded once per tick for both (holdsAt; fix round 1); read site unchanged, offset moved; a report, not a decision; self-serve moves 3 Task 1 file:Read@2786 16e4a3a979fafef37cbd9f932ad97496e2bcbdc2675eeb4f34549c409ddf1183
@legacy d414eb2469658072e579263b4326223edb1241b6373782e0dbb9ce18cdd422dd internal/registry/registry.go Registry.Fingerprint legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-3;basis=the registry's SQLite side, as carlos: in the agent (/opt/carlos/carlos agent, carlos-edge.service) and in the registry child (carlos __registry-child, which a root pass forks as the service user); the remote arm, unchanged, in the process that holds a remote handle: self-serve moves 1's body with route_aliases.explicit folded into the alias part, only when set, so a registry with no explicit row digests as before; self-serve moves 3 Task 1 file:Exec@284,file:Open@188,file:Query@3389,file:Query@368,file:Query@3868,file:Query@5040,file:Query@5894,file:Query@6806,registry:ClaimIncarnation@1158,registry:ClaimIncarnation@2297,registry:ClaimIncarnation@2374,registry:ClaimOwner@1202,registry:ClaimOwner@2498,registry:ClaimOwner@2563,registry:ClaimRowID@1221,registry:ClaimRowID@2590,registry:ClaimRowID@2656,registry:ClaimStamp@1183,registry:ClaimStamp@2407,registry:ClaimStamp@2471,registry:Incarnation@1240,registry:Incarnation@2877,registry:Incarnation@2941,registry:ServingSeq@1259,registry:ServingSeq@2968,registry:ServingSeq@3031,registry:Text@158,registry:path@207,registry:remote@55,registry:remote@87,whole:remoteClient@57,whole:remoteClient@89 cb12369e81d06da6e48b6a4ed6720dfec2a9401c366f99920bbf5343489876b5
internal/registry/registry.go Registry.Fingerprint legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-3;basis=the registry's SQLite side, as carlos: in the agent (/opt/carlos/carlos agent, carlos-edge.service) and in the registry child (carlos __registry-child, which a root pass forks as the service user); the remote arm, unchanged, in the process that holds a remote handle: self-serve moves 1's body with route_aliases.explicit folded into the alias part, only when set, so a registry with no explicit row digests as before; self-serve moves 3 Task 1 file:Exec@284,file:Open@188,file:Query@3389,file:Query@368,file:Query@3868,file:Query@5040,file:Query@5894,file:Query@6806,registry:ClaimIncarnation@1158,registry:ClaimIncarnation@2297,registry:ClaimIncarnation@2374,registry:ClaimOwner@1202,registry:ClaimOwner@2498,registry:ClaimOwner@2563,registry:ClaimRowID@1221,registry:ClaimRowID@2590,registry:ClaimRowID@2656,registry:ClaimStamp@1183,registry:ClaimStamp@2407,registry:ClaimStamp@2471,registry:Incarnation@1240,registry:Incarnation@2877,registry:Incarnation@2941,registry:ServingSeq@1259,registry:ServingSeq@2968,registry:ServingSeq@3031,registry:Text@158,registry:path@207,registry:remote@55,registry:remote@87,whole:remoteClient@57,whole:remoteClient@89 cb12369e81d06da6e48b6a4ed6720dfec2a9401c366f99920bbf5343489876b5
@legacy 87b681f434357ff61191af763ffcc91f73615b2ff811274383ced07e41fe4b9f internal/registry/registry.go Registry.ListAliases legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-3;basis=the registry's SQLite side, as carlos: in the agent (/opt/carlos/carlos agent, carlos-edge.service) and in the registry child (carlos __registry-child, which a root pass forks as the service user); the remote arm, unchanged: rev3=J1,J4's route-and-input body, which also scans route_aliases.explicit into RouteAlias.Explicit; self-serve moves 3 Task 1 file:Query@199,registry:Account@508,registry:Alias@489,registry:Aliases@164,registry:App@520,registry:CertExpiresAt@562,registry:CreatedAt@580,registry:DNSCheckedAt@545,registry:DNSStatus@528,registry:Explicit@594,registry:Host@499,registry:remote@61,registry:remote@93,registry:sql@199,whole:RouteAlias@168,whole:RouteAlias@645,whole:RouteAlias@658,whole:RouteAlias@663,whole:RouteAlias@677,whole:remoteClient@63,whole:remoteClient@95 1de0ab15f79d83aba3aa1db193d332fb67ed82b0dfd2cbc8bc4a9e7df592280b
@@ -25,5 +26,5 @@ internal/host/edgesfile.go holdingEntries authority F,J1 rev3=PROPOSED-self-serv
@coverage internal/domains 0cf7748c97e4caa7d0daa73e251fdd2f2f03e425321599bf046292b0a86ebde8
@coverage internal/edge 571f37acfaffe9691e56807e111133ffd615e5e255786c190b37d21a58af9f24
@coverage internal/edgesfile ee82e43c7d6363297f63abcde9c3e828a53d377573649f0d26a9853056aad4be
-@coverage internal/host 2aae5c9642d02b3554d1c45208e565c592d82b35149b2487b4afb2a864011eb5
+@coverage internal/host ae4a9176fccc8c4af24826698a0f6adeab9ee7efc0f9a211169be343b4628897
@coverage internal/registry ae19eaec5c4dde2c400fcb2fc6afd23c6b129e77306d6916fe55b2091b0fb5ed