carlos / platform Public

region HA 2b: the join refusals for an unreadable identity and a nameless box carry copy rows

RHB-C70 (approved by Paul) and RHB-C71 (internal fragment, ships as
drafted), both pinned in the gate and marked built.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Paul Campbell pushed by claude agent, operated by paul@keymail.dev fb7dd5e88b663d8809d14d4462141777c4f68cd2 parent 39f6759
5 files changed, +12 −9
  • approved_region_ha_2b_copy_test.go +4 −4
  • docs/superpowers/plans/2026-10-17-region-ha-2b-copy.md +5 −2
  • internal/host/joinroot.go +1 −1
  • internal/host/joinroot_test.go +1 −1
  • internal/provision/testdata/plan14-root-reads-region-ha-2b.tsv +1 −1
diff --git a/approved_region_ha_2b_copy_test.go b/approved_region_ha_2b_copy_test.go
index ed48d8c79..34044dfa3 100644
--- a/approved_region_ha_2b_copy_test.go
+++ b/approved_region_ha_2b_copy_test.go
@@ -25,20 +25,20 @@ const regionHA2bCopyRecordPath = "docs/superpowers/plans/2026-10-17-region-ha-2b
var regionHA2bCopyIDPattern = regexp.MustCompile(`^RHB-C[0-9]+$`)
// regionHA2bCopyIDs is the record's inventory, pinned: RHB-C1 to RHB-C64,
-// RHB-C67 to RHB-C69. RHB-C65 and RHB-C66 were withdrawn in review and
+// RHB-C67 to RHB-C71. RHB-C65 and RHB-C66 were withdrawn in review and
// are not reused.
var regionHA2bCopyIDs = func() []string {
- ids := make([]string, 0, 67)
+ ids := make([]string, 0, 69)
for i := 1; i <= 64; i++ {
ids = append(ids, "RHB-C"+strconv.Itoa(i))
}
- return append(ids, "RHB-C67", "RHB-C68", "RHB-C69")
+ return append(ids, "RHB-C67", "RHB-C68", "RHB-C69", "RHB-C70", "RHB-C71")
}()
// regionHA2bCopyBuilt is every id whose text the code or docs carry. The
// task that writes a row's text adds its ids here in the same commit, once
// Paul has approved the rows.
-var regionHA2bCopyBuilt = []string{"RHB-C1", "RHB-C2", "RHB-C3", "RHB-C4", "RHB-C5", "RHB-C6", "RHB-C7", "RHB-C8", "RHB-C9", "RHB-C10", "RHB-C11", "RHB-C12", "RHB-C13", "RHB-C14", "RHB-C15", "RHB-C16", "RHB-C17", "RHB-C18", "RHB-C64", "RHB-C67", "RHB-C68", "RHB-C19", "RHB-C20", "RHB-C21", "RHB-C22", "RHB-C23", "RHB-C24", "RHB-C25", "RHB-C26", "RHB-C27", "RHB-C28", "RHB-C29", "RHB-C30", "RHB-C31", "RHB-C32", "RHB-C33", "RHB-C34", "RHB-C35", "RHB-C36", "RHB-C37", "RHB-C38", "RHB-C39", "RHB-C40", "RHB-C41", "RHB-C42", "RHB-C43", "RHB-C44", "RHB-C45", "RHB-C46", "RHB-C47", "RHB-C48", "RHB-C49", "RHB-C62", "RHB-C63", "RHB-C69"}
+var regionHA2bCopyBuilt = []string{"RHB-C1", "RHB-C2", "RHB-C3", "RHB-C4", "RHB-C5", "RHB-C6", "RHB-C7", "RHB-C8", "RHB-C9", "RHB-C10", "RHB-C11", "RHB-C12", "RHB-C13", "RHB-C14", "RHB-C15", "RHB-C16", "RHB-C17", "RHB-C18", "RHB-C64", "RHB-C67", "RHB-C68", "RHB-C19", "RHB-C20", "RHB-C21", "RHB-C22", "RHB-C23", "RHB-C24", "RHB-C25", "RHB-C26", "RHB-C27", "RHB-C28", "RHB-C29", "RHB-C30", "RHB-C31", "RHB-C32", "RHB-C33", "RHB-C34", "RHB-C35", "RHB-C36", "RHB-C37", "RHB-C38", "RHB-C39", "RHB-C40", "RHB-C41", "RHB-C42", "RHB-C43", "RHB-C44", "RHB-C45", "RHB-C46", "RHB-C47", "RHB-C48", "RHB-C49", "RHB-C62", "RHB-C63", "RHB-C69", "RHB-C70", "RHB-C71"}
func mustParseRegionHA2bRecord(t *testing.T) []copyRow {
t.Helper()
diff --git a/docs/superpowers/plans/2026-10-17-region-ha-2b-copy.md b/docs/superpowers/plans/2026-10-17-region-ha-2b-copy.md
index e93b1a610..fb4ca8457 100644
--- a/docs/superpowers/plans/2026-10-17-region-ha-2b-copy.md
+++ b/docs/superpowers/plans/2026-10-17-region-ha-2b-copy.md
@@ -31,13 +31,14 @@ The rules:
- Ids are not renumbered: a row added in review takes the next free id
and goes in its task's table (RHB-C62 to RHB-C64, round 1; RHB-C65 to
RHB-C67, round 3; RHB-C68, round 4; RHB-C69, build Task 3 round 1,
- approved by Paul 2026-10-17). A withdrawn row's id is not
+ approved by Paul 2026-10-17; RHB-C70 and RHB-C71, build Task 7 round 1;
+ C70 approved by Paul 2026-10-17). A withdrawn row's id is not
reused: RHB-C65 and RHB-C66, the operator alert of round 3, went with
the read-back they belonged to in round 4.
- No em dashes. A full stop and a new sentence instead. Refusals start
lower case, as the CLI's others do.
-Paul approved RHB-C1, C5 to C9, C11 to C32, C34 to C64 and C67 to C68 unedited in the copy review on 2026-10-17, and RHB-C69, added in build Task 3 round 1, the same day. RHB-C2 to C4, C10 and C33 are internal error fragments, left out of the review under his rule that reviews hold only text a person reads in normal use; they ship as drafted.
+Paul approved RHB-C1, C5 to C9, C11 to C32, C34 to C64 and C67 to C68 unedited in the copy review on 2026-10-17, RHB-C69, added in build Task 3 round 1, the same day, and RHB-C70, added in build Task 7 round 1, the same day. RHB-C2 to C4, C10, C33 and C71 are internal error fragments, left out of the review under his rule that reviews hold only text a person reads in normal use; they ship as drafted.
Placeholders: `<reason>` the text of the error underneath; `<path>` a
file; `<region>` a placement code; `<deployment>` a deployment's name;
@@ -117,6 +118,8 @@ as stable; `<platform>` such as linux-arm64; `<box>` a box's reference.
| RHB-C49 | internal/host/joinroot.go | the host floor does not verify, so the guard was not armed: <reason> | approved |
| RHB-C62 | internal/host/units/carlos-system-join.path | carlos joining: arm the host lifecycle guard on agent request | approved |
| RHB-C63 | internal/host/units/carlos-system-join.service | carlos joining (root: arm the host lifecycle guard) | approved |
+| RHB-C70 | internal/host/joinroot.go | this box's AWS identity could not be read, so it cannot join yet: <reason> | approved |
+| RHB-C71 | internal/host/joinroot.go | this box has no name to join under | approved |
## The carlos system usage and the docs (Task 9)
diff --git a/internal/host/joinroot.go b/internal/host/joinroot.go
index b139ea5b7..6c86c6c6a 100644
--- a/internal/host/joinroot.go
+++ b/internal/host/joinroot.go
@@ -133,7 +133,7 @@ func joinBoxLabel(getenv func(string) string, machineID string) (string, error)
if groupReferenced(getenv) {
ref, err := resolveGroupBoxReference()
if err != nil {
- return "", fmt.Errorf("this group box's reference could not be read, so it cannot join: %w", err)
+ return "", fmt.Errorf("this box's AWS identity could not be read, so it cannot join yet: %w", err)
}
return ref, nil
}
diff --git a/internal/host/joinroot_test.go b/internal/host/joinroot_test.go
index c6597fcc0..91d65b272 100644
--- a/internal/host/joinroot_test.go
+++ b/internal/host/joinroot_test.go
@@ -190,7 +190,7 @@ func TestRootJoinTakesNothingFromItsTrigger(t *testing.T) {
func TestRootJoinNamesAGroupBoxByItsReferenceOrFails(t *testing.T) {
group := groupEnv(map[string]string{hostenv.BoxReference: hostenv.GroupReference})
withGroupIdentity(t, "", "", errors.New("IMDS answered 500"))
- if got, err := joinBoxLabel(group, "machine"); err == nil || got != "" || !strings.Contains(err.Error(), "IMDS answered 500") {
+ if got, err := joinBoxLabel(group, "machine"); err == nil || got != "" || err.Error() != "this box's AWS identity could not be read, so it cannot join yet: IMDS answered 500" {
t.Fatalf("no identity: %q %v", got, err)
}
withGroupIdentity(t, "123456789012", "i-0a61f580df1acfa65", nil)
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 90223c1c9..c77f5e7d0 100644
--- a/internal/provision/testdata/plan14-root-reads-region-ha-2b.tsv
+++ b/internal/provision/testdata/plan14-root-reads-region-ha-2b.tsv
@@ -10,7 +10,7 @@
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
internal/hostenv/write_linux.go checkedDir authority A,J4 rev3=census-ruling-region-ha-2b;basis=carlos system host-env write (root, Linux): opens one directory under one already held, O_NOFOLLOW|O_DIRECTORY, creating it 0755 first only when asked and missing, and refuses it unless fstat shows a directory owned by this process's user (root, as write requires) that no group or other can write file:Fstat@576,file:Openat@168,file:Openat@372 012257ac0866f3385044785d8abae9c31e61134928c394e22a2aeaef39ec9f3c
internal/hostenv/write_linux.go writeHostEnv authority A,J4 rev3=census-ruling-region-ha-2b;basis=carlos system host-env write (root, Linux): writes /etc/carlos/host.env only through /etc and /etc/carlos held by descriptor and checked by checkedDir; the new file is created 0600, O_EXCL|O_NOFOLLOW, in that directory, synced, renamed over host.env within it, and the directory synced; its read sites open the root and the temporary file file:Open@68,file:Openat@509 1245fc0f819ba73fb45545ffdcf92d5d4941802175e73bac209ab4d4238adff1
-@coverage internal/host b9ceedace2daef757acc72df1eec6440c1377b2317212040d9c3f7211366c50b
+@coverage internal/host a4e90f7c06e670f4fb4103bc96eb83ac2c1d85d0f3d3428625992ec12e8f8ecb
internal/host/installchannel.go channelInstall.run authority F,J4 rev3=census-ruling-region-ha-2b;basis=carlos system install-channel (root, at a fresh box's start-up): its one read site opens root's apply lock; the install goes through verifyPointer, hostfloor.CheckImage and stageCandidate into both protected slots, and refuses a box whose guard is armed or that already runs another release file:OpenFile@196 f0cdb6dd28e83da282c3b0d7bc2012dcd80db181a8cecbf1a2db6bcc41d1d5e3
internal/host/installchannel_linux.go slotState authority F,J4 rev3=census-ruling-region-ha-2b;basis=carlos system install-channel (root, Linux): opens one protected slot with O_NOFOLLOW (a symlink is never followed), fstats the descriptor and hashes from it, to compare with the sha256 of the signed, verified artifact; a slot holding anything else, a symlink or a non-regular file is refused and left as it is, and nothing in it is executed file:Open@66,file:Stat@388 bc533a29f92ae95d31123b551354ab9f8960576e5ffef1bd6a74a06df0b57a24
internal/host/installchannel_linux.go var openat2 authority F,J4 rev3=census-ruling-region-ha-2b;basis=carlos system install-channel (root, Linux): unix.Openat2 held in a variable so a test can make it fail; removeBeneath is its only user and has no path-based fallback when it fails file:Openat2@14 6081e86f758f4ae3948ec2d4f4c037888e1362f122a410c110e31290ba914816