region HA 2b: a failed ListMod lists the status reports whole
listBoxKeys (ee21b94a) took any release.ModLister's ListMod error as the read's error. The claim's boundary stores (hostops' mutation boundary and conditional record store) always have a ListMod and refuse it with ErrContract when the store they wrap has none, while their List reads. Under such a wrap the console's live edges read failed, and the move door answered "the fleet's edges files could not all be read" before its own refusals (TestMoveDoorHostClaimRefusals/changed-under-the-request, TestMoveDoorHostClaimFailedRecordStartReleasesTheClaim). The horizon is only an optimisation, so a failed ListMod now falls back to List, main's path, with main's errors. The census overlay's internal/status coverage row moves; no unit or seal changes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
3 files changed,
+27
−4
internal/provision/testdata/plan14-root-reads-region-ha-2b.tsv+2 −1internal/status/fleet.go+9 −3internal/status/fleet_listing_test.go+16 −0
CI failed — run details
CI log
=== gate === go: downloading github.com/aws/aws-sdk-go-v2/service/autoscaling v1.74.0 go: downloading github.com/aws/aws-sdk-go-v2/service/ssm v1.74.0 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 319.637s ? github.com/carlosframework/platform/cmd/changelog [no test files] ok github.com/carlosframework/platform/cmd/console 2.768s ok github.com/carlosframework/platform/cmd/moveprobe 93.303s ok github.com/carlosframework/platform/cmd/zddprobe 0.712s ok github.com/carlosframework/platform/internal/activator 250.756s ok github.com/carlosframework/platform/internal/agentsock 0.018s ok github.com/carlosframework/platform/internal/alerting 0.004s ok github.com/carlosframework/platform/internal/analytics 0.019s ok github.com/carlosframework/platform/internal/appconfig 2.465s ok github.com/carlosframework/platform/internal/apperr 0.093s ok github.com/carlosframework/platform/internal/applog 0.161s ok github.com/carlosframework/platform/internal/awsinv 0.003s ok github.com/carlosframework/platform/internal/boxchoice 0.004s ok github.com/carlosframework/platform/internal/boxid 0.959s ? 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.003s ok github.com/carlosframework/platform/internal/cgroup 0.704s ok github.com/carlosframework/platform/internal/changelog 0.334s ok github.com/carlosframework/platform/internal/claimactivation 0.031s ok github.com/carlosframework/platform/internal/clamd 0.059s ? github.com/carlosframework/platform/internal/cliflag [no test files] ok github.com/carlosframework/platform/internal/console 404.170s ? 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.211s ok github.com/carlosframework/platform/internal/dbdoor/dbdoortest 0.008s ok github.com/carlosframework/platform/internal/dbverify 0.006s ok github.com/carlosframework/platform/internal/dnsrec 0.005s ok github.com/carlosframework/platform/internal/docsite 0.005s ok github.com/carlosframework/platform/internal/domains 2.367s ok github.com/carlosframework/platform/internal/doorkey 0.006s ok github.com/carlosframework/platform/internal/economics 0.005s ok github.com/carlosframework/platform/internal/edge 45.113s ? 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.148s ok github.com/carlosframework/platform/internal/gate 1.655s ok github.com/carlosframework/platform/internal/genselect 0.014s ok github.com/carlosframework/platform/internal/geo 0.014s ok github.com/carlosframework/platform/internal/geoip 0.029s ok github.com/carlosframework/platform/internal/historyreadback 0.181s ok github.com/carlosframework/platform/internal/host 178.722s ok github.com/carlosframework/platform/internal/hostactors 5.656s ok github.com/carlosframework/platform/internal/hostactors/awsmembership 0.014s ok github.com/carlosframework/platform/internal/hostbox 2.753s ok github.com/carlosframework/platform/internal/hostclaim 0.575s ok github.com/carlosframework/platform/internal/hostenv 0.014s ok github.com/carlosframework/platform/internal/hostevidence 0.014s ok github.com/carlosframework/platform/internal/hostexec 5.146s ok github.com/carlosframework/platform/internal/hostfence 0.037s ok github.com/carlosframework/platform/internal/hostfiles 10.672s ok github.com/carlosframework/platform/internal/hostfloor 0.014s ok github.com/carlosframework/platform/internal/hostguard 1.031s ok github.com/carlosframework/platform/internal/hostops 1.583s ? github.com/carlosframework/platform/internal/hostops/fencerelease [no test files] ok github.com/carlosframework/platform/internal/hostops/internal/fencerelease 0.013s ok github.com/carlosframework/platform/internal/hostquarantine 0.014s ok github.com/carlosframework/platform/internal/hostrelocate 0.449s ok github.com/carlosframework/platform/internal/hostresolve 0.130s ok github.com/carlosframework/platform/internal/hostwrite 0.013s ok github.com/carlosframework/platform/internal/infraview 0.015s ok github.com/carlosframework/platform/internal/instancectl 5.266s ok github.com/carlosframework/platform/internal/instanceexec 2.740s ok github.com/carlosframework/platform/internal/instancefs 0.018s ok github.com/carlosframework/platform/internal/instancekey 0.015s ok github.com/carlosframework/platform/internal/instanceupdates 0.022s ok github.com/carlosframework/platform/internal/layout 1.534s ok github.com/carlosframework/platform/internal/lbhealth 0.018s ok github.com/carlosframework/platform/internal/lease 9.819s ok github.com/carlosframework/platform/internal/lease/leasetest 0.005s ok github.com/carlosframework/platform/internal/ledger 0.017s ok github.com/carlosframework/platform/internal/mail 0.170s ok github.com/carlosframework/platform/internal/maintenance 0.079s ok github.com/carlosframework/platform/internal/maintenanceexec 5.524s ok github.com/carlosframework/platform/internal/maintenanceguard 0.075s ok github.com/carlosframework/platform/internal/metrics 0.004s ok github.com/carlosframework/platform/internal/moves 21.190s ok github.com/carlosframework/platform/internal/moves/archive 0.789s ok github.com/carlosframework/platform/internal/objstore 0.188s ok github.com/carlosframework/platform/internal/ops 0.178s ok github.com/carlosframework/platform/internal/park 0.211s ok github.com/carlosframework/platform/internal/privatefile 0.003s ok github.com/carlosframework/platform/internal/provision 37.392s ok github.com/carlosframework/platform/internal/publicrelease 0.012s ? github.com/carlosframework/platform/internal/regionname [no test files] ok github.com/carlosframework/platform/internal/registry 5.566s ok github.com/carlosframework/platform/internal/relay 7.239s ok github.com/carlosframework/platform/internal/release 22.467s ok github.com/carlosframework/platform/internal/rollout 3.902s ok github.com/carlosframework/platform/internal/rolloutwords 0.004s ok github.com/carlosframework/platform/internal/rootaws 0.007s ok github.com/carlosframework/platform/internal/rootparticipation 0.057s ok github.com/carlosframework/platform/internal/rootstate 0.026s ok github.com/carlosframework/platform/internal/runs 0.576s 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.507s ok github.com/carlosframework/platform/internal/seeds 0.011s ok github.com/carlosframework/platform/internal/sim/hostlife 10.191s ok github.com/carlosframework/platform/internal/sim/litestreamfacts 3.689s ok github.com/carlosframework/platform/internal/status 0.072s ok github.com/carlosframework/platform/internal/steering 0.005s ok github.com/carlosframework/platform/internal/stopevidence 0.279s 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.005s 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.002s ok github.com/carlosframework/platform/internal/vet 1.590s === 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: 54132898 bytes (ceiling 52000000) the linux/arm64 binary is 54132898 bytes, over the 52000000 ceiling. look for an SDK client stored in an interface under html/template's method retention (every exported method of the client is kept): store method values instead. See discussion #275 and the comment in .amadan/ci.d/20-cross@5. amadan: step cross shard 2 of 5 failed: exit status 1 --- shard 3 of 5 --- cross-compiling windows/amd64 --- shard 4 of 5 --- cross-compiling windows/arm64 amadan: job failed at step cross shard 2 of 5 — its output is above, not at the tail of this log
diff --git a/internal/provision/testdata/plan14-root-reads-region-ha-2b.tsv b/internal/provision/testdata/plan14-root-reads-region-ha-2b.tsv| index 59786c211..20ac219b2 100644 |
| --- a/internal/provision/testdata/plan14-root-reads-region-ha-2b.tsv |
| +++ b/internal/provision/testdata/plan14-root-reads-region-ha-2b.tsv |
| @@ -7,6 +7,7 @@ |
| # Task 7: root's half of joining (internal/host joinroot.go: carlos system join, which runs guard-arm in process; units carlos-system-join.path and .service; bootstrap's enabledUnits arms the path). joinroot.go adds no root-read unit: RunSystemGuardArm, authority F,J4, is called and not changed. The two unit files are new root inputs, ruled authority F,J4 on the condition that the path unit's file is only a trigger: root's join reads nothing from it. The legacy seal stands. |
| # Task 8: joining mode and the gate (internal/host joining.go; RunAgent calls hostingGate before runAgent; ClaimWiring.startRegional sets the health answer's hosting condition from the gate's decision and clears it when the worker ends). runAgent, authority B,C,D,F,J4, is not changed. No root-read unit changed or was added. Envelope-only, and the seal stands. Before the gate, RunAgent has a box carrying the group marker resolve its group reference (agentEntry, through Task 6's resolveGroupBoxReference, with backoff), whatever the switches say, so runAgent's loops never keep an empty label; joining touches the join marker again once a minute while the box stays invited, so a failed carlos system join is retried. |
| # Task 9 (system.go): the install-channel, join and host-env verbs join systemVerb's list and runSystem's dispatch, and the usage gains their lines. The two seed-overlay authority declarations are re-digested, class, task, basis and (empty) kinds unchanged: no root read is added. Merged onto main 26065378, whose activation-tools overlay (census-ruling-activation-tools) re-ruled both declarations for the claims verb: the two rows carry that overlay's class, basis (B,C,D,F,G,J4) and text, re-digested over the merged code with both sets of verbs. |
| +# CI fix (internal/status fleet.go listBoxKeys): a store whose ListMod fails is listed whole, as one that is not a ModLister already was, so the claim's boundary stores (whose ListMod refuses when the store they wrap has none) no longer fail the move door's edges read. Only the internal/status coverage row moves; no root-read unit is added or changed, and the seal does not move. |
| @coverage . 9949422c526be724b50a4614fc79623b9823a1aa0960d1472fd9050f8000c3b0 |
| @coverage internal/hostenv 6b65e7987026a4c81f410c0afcfaca13bc2b252d7382c45564f670e36a045bcb |
| internal/hostenv/cli.go runHostEnv authority A,J4 rev3=census-ruling-region-ha-2b;basis=carlos system host-env: write runs as root at a box's start-up and writes /etc/carlos/host.env, 0600, whole (temporary file and rename), from allowlisted parameters with safe values only, refusing a missing required value; its one read site is seed's --from file, an operator's host.env whose values go only to the parameter store; write's path is fixed and reached only through writeHostEnv file:ReadFile@1500 df4470176b229b311927ff0ac4f268d354c3a2357cac4300ac0992d2ebb90899 |
| @@ -22,7 +23,7 @@ internal/host/installchannel_linux.go removeBeneath authority F,J4 rev3=census-r |
| @coverage internal/console e891098191a724430f78e9aa0c5d1307b9343283e0cb7506163383c4dee0df7c |
| @legacy 2f4ca2a51661d8aae1e0f38a41efdcc9df9f1dd75411ca6742d6ee3ae71f1756 internal/hostactors/autoenroll.go AutoEnroller.inputs legacy accepted-legacy accepted-legacy;rev3=J4;basis=root-input;census-ruling-region-ha-2b file:Read@102,file:Read@305 c0ae921ee9d8df9de403d488d9708c565d659a7bf880199313f50bdc7c8f8127 |
| internal/hostactors/autoenroll.go AutoEnroller.inputs legacy accepted-legacy accepted-legacy;rev3=J4;basis=root-input;census-ruling-region-ha-2b file:Read@102,file:Read@305 c0ae921ee9d8df9de403d488d9708c565d659a7bf880199313f50bdc7c8f8127 |
| -@coverage internal/status a1eee66fc906d6a7275017fd30c346b64aaafe4f88a501538248e99e9d95c33d |
| +@coverage internal/status e80d1b6bd86b860a75c7483fb51ab47161f0648a4aff2d0d1e92d5e56495b4b8 |
| @legacy 30a3ee6b3365c202bf93b26087888a582c6b8d6df1a9f1fc6ba12e639ddd8684 internal/host/agent.go statusLoop legacy accepted-legacy accepted-legacy;rev3=census-ruling-region-ha-2b;basis=the agent (/opt/carlos/carlos agent, carlos-edge.service, user carlos): main's body with its tick through groupStatusTick, so a box whose host.env carries the group marker (CARLOS_BOX_REFERENCE=group) publishes its status report only under its group reference, resolved afresh each tick, and nothing until it resolves, never under status.Label; every other box, fleet boxes included, gets statusTick with today's label and key; read sites unchanged whole:Route@6154,whole:Route@6181,whole:Route@7423,whole:Route@9437 666ddbc4779442db4c868253cf5062c690e20ad643197688305e0742f6de9283 |
| internal/host/agent.go statusLoop legacy accepted-legacy accepted-legacy;rev3=census-ruling-region-ha-2b;basis=the agent (/opt/carlos/carlos agent, carlos-edge.service, user carlos): main's body with its tick through groupStatusTick, so a box whose host.env carries the group marker (CARLOS_BOX_REFERENCE=group) publishes its status report only under its group reference, resolved afresh each tick, and nothing until it resolves, never under status.Label; every other box, fleet boxes included, gets statusTick with today's label and key; read sites unchanged whole:Route@6154,whole:Route@6181,whole:Route@7423,whole:Route@9437 666ddbc4779442db4c868253cf5062c690e20ad643197688305e0742f6de9283 |
| @coverage internal/host/units ef87da22b7bc84e059c7bf64d936bc5854427ccde36c7bc0d0c264a43fb83d2e |
diff --git a/internal/status/fleet.go b/internal/status/fleet.go| index 3d4920043..dc0d51031 100644 |
| --- a/internal/status/fleet.go |
| +++ b/internal/status/fleet.go |
| @@ -245,8 +245,14 @@ func LoadBoxesComplete(ctx context.Context, s release.Store) (boxes []Box, compl |
| // an optimisation, never authority: every reader still judges each |
| // report's own GeneratedAt as before (BoxStale, the console's |
| // VerifyReport), and a day keeps every box an operator would still look |
| -// for as stale. A store that cannot say when an object was written |
| -// (release.ModLister) is listed whole, as before. |
| +// for as stale. A store that cannot say when an object was written is |
| +// listed whole, as before: one that is not a release.ModLister, and one |
| +// that is but whose ListMod fails. The second is the claim's boundary |
| +// stores (hostops' mutation boundary and conditional record store, the |
| +// mail claim gate): they always have a ListMod method and answer it with |
| +// their own refusal when the store they wrap has none, while their List |
| +// still reads. Because the horizon is only an optimisation, a failed |
| +// ListMod never fails the read: List answers, with main's errors. |
| const statusListingHorizon = 24 * time.Hour |
| // listBoxKeys lists status/boxes/, without the reports the horizon drops. |
| @@ -257,7 +263,7 @@ func listBoxKeys(ctx context.Context, s release.Store) ([]string, error) { |
| } |
| mods, err := ml.ListMod(ctx, "status/boxes/") |
| if err != nil { |
| - return nil, err |
| + return s.List(ctx, "status/boxes/") |
| } |
| cut := time.Now().Add(-statusListingHorizon) |
| keys := make([]string, 0, len(mods)) |
diff --git a/internal/status/fleet_listing_test.go b/internal/status/fleet_listing_test.go| index 0f354ce23..71c7adf26 100644 |
| --- a/internal/status/fleet_listing_test.go |
| +++ b/internal/status/fleet_listing_test.go |
| @@ -3,6 +3,7 @@ package status |
| import ( |
| "context" |
| "encoding/json" |
| + "errors" |
| "fmt" |
| "os" |
| "path/filepath" |
| @@ -16,6 +17,14 @@ import ( |
| // object was written does. |
| type listOnly struct{ release.Store } |
| +// refusesListMod has a ListMod that always refuses, as the claim's |
| +// boundary stores answer when the store they wrap has none. |
| +type refusesListMod struct{ release.Store } |
| + |
| +func (refusesListMod) ListMod(context.Context, string) ([]release.KeyMod, error) { |
| + return nil, errors.New("required contract unavailable") |
| +} |
| + |
| // T2 (region HA plan 2b): 250 reports of boxes a group replaced long ago |
| // do not hide the two boxes running now. The old reports are dropped from |
| // the listing before any read and before the key cap, and the reading is |
| @@ -64,4 +73,11 @@ func TestStaleReportsDoNotHideLiveBoxes(t *testing.T) { |
| if _, complete, err := LoadBoxesComplete(ctx, listOnly{s}); err != nil || complete { |
| t.Fatalf("listed whole: complete %v, err %v", complete, err) |
| } |
| + // A store whose ListMod refuses is listed whole too: the horizon is an |
| + // optimisation, so its failure never fails the read (the move door read |
| + // the fleet through the claim's boundary stores and answered 409 when |
| + // it did). |
| + if _, complete, err := LoadBoxesComplete(ctx, refusesListMod{s}); err != nil || complete { |
| + t.Fatalf("refused ListMod listed whole: complete %v, err %v", complete, err) |
| + } |
| } |