| 1 | package provision |
| 2 | |
| 3 | import ( |
| 4 | "bytes" |
| 5 | "crypto/sha256" |
| 6 | "encoding/hex" |
| 7 | "encoding/json" |
| 8 | "fmt" |
| 9 | "go/ast" |
| 10 | "go/importer" |
| 11 | "go/parser" |
| 12 | "go/token" |
| 13 | "go/types" |
| 14 | "io" |
| 15 | "io/fs" |
| 16 | "os" |
| 17 | "os/exec" |
| 18 | "path/filepath" |
| 19 | "reflect" |
| 20 | "sort" |
| 21 | "strings" |
| 22 | "testing" |
| 23 | ) |
| 24 | |
| 25 | type rootReadEntry struct{ File, Unit, Kinds, Digest string } |
| 26 | type rootReadScan struct { |
| 27 | Declarations map[string]string |
| 28 | Entries []rootReadEntry |
| 29 | Coverage map[string]string |
| 30 | Fields []string |
| 31 | } |
| 32 | |
| 33 | // Match names conservatively, including method values, aliases and writes. |
| 34 | // Scanning all production packages avoids an unsound static call graph for |
| 35 | // callbacks, interfaces, root CLI invocations, and build-tagged root helpers. |
| 36 | var rootReadIO = strings.Fields(`Open OpenFile OpenRoot OpenInRoot Openat Openat2 Openat64 OpenByHandleAt OpenTree Fdopen Create CreateTemp ReadFile ReadDir Readlink Readlinkat Read ReadAt ReadFrom ReadAll ReadBytes ReadString Readdir Readdirnames Stat Lstat Fstat Fstatat Statx Statfs Fstatfs Access Faccessat Faccessat2 EvalSymlinks Glob Walk WalkDir Mmap Sendfile CopyFileRange Splice Query QueryContext QueryRow QueryRowContext Prepare PrepareContext Syscall Syscall6 RawSyscall RawSyscall6 Command CommandContext StartProcess Exec LoadLibrary LoadDLL NewLazyDLL NewLazySystemDLL`) |
| 37 | |
| 38 | type rootReadPackage struct { |
| 39 | Dir, ImportPath, Export string |
| 40 | GoFiles, CgoFiles, EmbedFiles []string |
| 41 | } |
| 42 | |
| 43 | func rootReadTyped(root string) (map[string]string, map[string]bool, error) { |
| 44 | cmd := exec.Command("go", "list", "-deps", "-export", "-json", "./...") |
| 45 | cmd.Dir = root |
| 46 | cmd.Env = append(os.Environ(), "GOOS=linux", "GOARCH=amd64", "CGO_ENABLED=0") |
| 47 | var stderr bytes.Buffer |
| 48 | cmd.Stderr = &stderr |
| 49 | raw, err := cmd.Output() |
| 50 | if err != nil { |
| 51 | return nil, nil, fmt.Errorf("census package exports: %w: %s", err, stderr.String()) |
| 52 | } |
| 53 | dec := json.NewDecoder(bytes.NewReader(raw)) |
| 54 | var packages []rootReadPackage |
| 55 | exports := map[string]string{} |
| 56 | for { |
| 57 | var p rootReadPackage |
| 58 | err = dec.Decode(&p) |
| 59 | if err == io.EOF { |
| 60 | break |
| 61 | } |
| 62 | if err != nil { |
| 63 | return nil, nil, err |
| 64 | } |
| 65 | packages = append(packages, p) |
| 66 | exports[p.ImportPath] = p.Export |
| 67 | } |
| 68 | sites := map[string]string{} |
| 69 | active := map[string]bool{} |
| 70 | for _, p := range packages { |
| 71 | rel, err := filepath.Rel(root, p.Dir) |
| 72 | if err != nil { |
| 73 | return nil, nil, err |
| 74 | } |
| 75 | if rel == ".." || strings.HasPrefix(rel, ".."+string(filepath.Separator)) { |
| 76 | continue |
| 77 | } |
| 78 | fset := token.NewFileSet() |
| 79 | var files []*ast.File |
| 80 | for _, name := range append(p.GoFiles, p.CgoFiles...) { |
| 81 | path := filepath.Join(p.Dir, name) |
| 82 | f, err := parser.ParseFile(fset, path, nil, 0) |
| 83 | if err != nil { |
| 84 | return nil, nil, err |
| 85 | } |
| 86 | files = append(files, f) |
| 87 | active[path] = true |
| 88 | } |
| 89 | for _, name := range p.EmbedFiles { |
| 90 | path := filepath.Join(p.Dir, name) |
| 91 | if _, ok := active[path]; !ok { |
| 92 | active[path] = false |
| 93 | } |
| 94 | } |
| 95 | if len(files) == 0 { |
| 96 | continue |
| 97 | } |
| 98 | imp := importer.ForCompiler(fset, "gc", func(path string) (io.ReadCloser, error) { |
| 99 | if exports[path] == "" { |
| 100 | return nil, fmt.Errorf("no export for %s", path) |
| 101 | } |
| 102 | return os.Open(exports[path]) |
| 103 | }) |
| 104 | cfg := types.Config{Importer: imp, GoVersion: "go1.26", Sizes: types.SizesFor("gc", "amd64")} |
| 105 | info := types.Info{Selections: map[*ast.SelectorExpr]*types.Selection{}, Uses: map[*ast.Ident]types.Object{}} |
| 106 | if _, err := cfg.Check(p.ImportPath, fset, files, &info); err != nil { |
| 107 | return nil, nil, fmt.Errorf("census types %s: %w", p.ImportPath, err) |
| 108 | } |
| 109 | selectorBases := map[ast.Node]bool{} |
| 110 | for _, f := range files { |
| 111 | ast.Inspect(f, func(n ast.Node) bool { |
| 112 | if s, ok := n.(*ast.SelectorExpr); ok { |
| 113 | selectorBases[s.X] = true |
| 114 | } |
| 115 | return true |
| 116 | }) |
| 117 | } |
| 118 | for id, obj := range info.Uses { |
| 119 | v, ok := obj.(*types.Var) |
| 120 | if !ok || selectorBases[id] { |
| 121 | continue |
| 122 | } |
| 123 | typ := types.Unalias(v.Type()) |
| 124 | for { |
| 125 | switch t := types.Unalias(typ).(type) { |
| 126 | case *types.Pointer: |
| 127 | typ = t.Elem() |
| 128 | case *types.Slice: |
| 129 | typ = t.Elem() |
| 130 | case *types.Array: |
| 131 | typ = t.Elem() |
| 132 | case *types.Map: |
| 133 | typ = t.Elem() |
| 134 | default: |
| 135 | goto peeled |
| 136 | } |
| 137 | } |
| 138 | peeled: |
| 139 | n, ok := types.Unalias(typ).(*types.Named) |
| 140 | if !ok || n.Obj().Pkg() == nil || n.Obj().Pkg().Path() != "github.com/carlosframework/platform/internal/registry" || n.Obj().Name() == "Registry" { |
| 141 | continue |
| 142 | } |
| 143 | if _, ok = n.Underlying().(*types.Struct); !ok { |
| 144 | continue |
| 145 | } |
| 146 | pos := fset.Position(id.Pos()) |
| 147 | sites[fmt.Sprintf("%s@%d:%d", pos.Filename, pos.Offset, fset.Position(id.End()).Offset)] = "whole:" + n.Obj().Name() |
| 148 | } |
| 149 | for expr, sel := range info.Selections { |
| 150 | field, ok := sel.Obj().(*types.Var) |
| 151 | if !ok || !field.IsField() || field.Pkg() == nil || field.Pkg().Path() != "github.com/carlosframework/platform/internal/registry" { |
| 152 | continue |
| 153 | } |
| 154 | pos := fset.Position(expr.Pos()) |
| 155 | sites[fmt.Sprintf("%s@%d:%d", pos.Filename, pos.Offset, fset.Position(expr.End()).Offset)] = "registry:" + field.Name() |
| 156 | } |
| 157 | } |
| 158 | return sites, active, nil |
| 159 | } |
| 160 | |
| 161 | func rootReadProduction(root string) (rootReadScan, error) { |
| 162 | out := rootReadScan{Coverage: map[string]string{}, Declarations: map[string]string{}} |
| 163 | typed, active, err := rootReadTyped(root) |
| 164 | if err != nil { |
| 165 | return out, err |
| 166 | } |
| 167 | files := map[string][]byte{} |
| 168 | extra := map[string][]byte{} |
| 169 | for path, compiled := range active { |
| 170 | if compiled { |
| 171 | continue |
| 172 | } |
| 173 | rel, err := filepath.Rel(root, path) |
| 174 | if err != nil { |
| 175 | return out, err |
| 176 | } |
| 177 | body, err := os.ReadFile(path) |
| 178 | if err != nil { |
| 179 | return out, err |
| 180 | } |
| 181 | extra[filepath.ToSlash(rel)] = body |
| 182 | } |
| 183 | err = filepath.WalkDir(root, func(p string, d fs.DirEntry, err error) error { |
| 184 | if err != nil { |
| 185 | return err |
| 186 | } |
| 187 | if d.IsDir() { |
| 188 | if p != root && (strings.HasPrefix(d.Name(), ".") || strings.HasPrefix(d.Name(), "_") || d.Name() == "testdata") { |
| 189 | return filepath.SkipDir |
| 190 | } |
| 191 | return nil |
| 192 | } |
| 193 | if !strings.HasSuffix(p, ".go") || strings.HasSuffix(p, "_test.go") { |
| 194 | if strings.HasSuffix(p, ".s") || strings.HasSuffix(p, ".sh") || strings.HasSuffix(p, ".service") || strings.HasSuffix(p, ".path") || strings.HasSuffix(p, ".socket") || strings.HasSuffix(p, ".timer") || strings.HasSuffix(p, ".conf") || strings.HasSuffix(p, ".tf") || strings.HasSuffix(p, ".tftpl") || strings.HasSuffix(p, ".tmpl") || d.Name() == "go.mod" || d.Name() == "go.sum" { |
| 195 | b, e := os.ReadFile(p) |
| 196 | if e != nil { |
| 197 | return e |
| 198 | } |
| 199 | rel, e := filepath.Rel(root, p) |
| 200 | if e != nil { |
| 201 | return e |
| 202 | } |
| 203 | extra[filepath.ToSlash(rel)] = b |
| 204 | } |
| 205 | return nil |
| 206 | } |
| 207 | b, e := os.ReadFile(p) |
| 208 | if e != nil { |
| 209 | return e |
| 210 | } |
| 211 | rel, e := filepath.Rel(root, p) |
| 212 | if e != nil { |
| 213 | return e |
| 214 | } |
| 215 | files[filepath.ToSlash(rel)] = b |
| 216 | return nil |
| 217 | }) |
| 218 | if err != nil { |
| 219 | return out, err |
| 220 | } |
| 221 | for path, compiled := range active { |
| 222 | if !compiled { |
| 223 | continue |
| 224 | } |
| 225 | rel, err := filepath.Rel(root, path) |
| 226 | if err != nil { |
| 227 | return out, err |
| 228 | } |
| 229 | if _, ok := files[filepath.ToSlash(rel)]; !ok { |
| 230 | return out, fmt.Errorf("production input source excluded from census: %s", path) |
| 231 | } |
| 232 | } |
| 233 | paths := make([]string, 0, len(files)) |
| 234 | for p := range files { |
| 235 | paths = append(paths, p) |
| 236 | } |
| 237 | sort.Strings(paths) |
| 238 | fields := map[string]bool{} |
| 239 | // Derive names from ALL registry structs, including embedded ownership, |
| 240 | // timers, archives, and the typed registry-child transport. No field list |
| 241 | // in the test can silently omit a newly added registry column. |
| 242 | for _, p := range paths { |
| 243 | if filepath.ToSlash(filepath.Dir(p)) != "internal/registry" { |
| 244 | continue |
| 245 | } |
| 246 | f, e := parser.ParseFile(token.NewFileSet(), p, files[p], 0) |
| 247 | if e != nil { |
| 248 | return out, e |
| 249 | } |
| 250 | ast.Inspect(f, func(n ast.Node) bool { |
| 251 | s, ok := n.(*ast.StructType) |
| 252 | if !ok { |
| 253 | return true |
| 254 | } |
| 255 | for _, f := range s.Fields.List { |
| 256 | for _, n := range f.Names { |
| 257 | fields[n.Name] = true |
| 258 | } |
| 259 | if len(f.Names) == 0 { |
| 260 | fields[censusRecvName(f.Type)] = true |
| 261 | } |
| 262 | } |
| 263 | return true |
| 264 | }) |
| 265 | } |
| 266 | for f := range fields { |
| 267 | out.Fields = append(out.Fields, f) |
| 268 | } |
| 269 | sort.Strings(out.Fields) |
| 270 | ioNames := map[string]bool{} |
| 271 | for _, n := range rootReadIO { |
| 272 | ioNames[n] = true |
| 273 | } |
| 274 | coverage := map[string][]string{} |
| 275 | for _, p := range paths { |
| 276 | src := files[p] |
| 277 | sum := sha256.Sum256(src) |
| 278 | dir := filepath.ToSlash(filepath.Dir(p)) |
| 279 | coverage[dir] = append(coverage[dir], p+"\x00"+hex.EncodeToString(sum[:])) |
| 280 | fset := token.NewFileSet() |
| 281 | f, e := parser.ParseFile(fset, p, src, 0) |
| 282 | if e != nil { |
| 283 | return out, e |
| 284 | } |
| 285 | grouped := map[string]int{} |
| 286 | for _, decl := range f.Decls { |
| 287 | var name string |
| 288 | switch d := decl.(type) { |
| 289 | case *ast.FuncDecl: |
| 290 | name = d.Name.Name |
| 291 | if d.Recv != nil { |
| 292 | name = censusRecvName(d.Recv.List[0].Type) + "." + name |
| 293 | } |
| 294 | case *ast.GenDecl: |
| 295 | if d.Tok == token.IMPORT { |
| 296 | continue |
| 297 | } |
| 298 | if d.Lparen.IsValid() { |
| 299 | grouped[d.Tok.String()]++ |
| 300 | name = fmt.Sprintf("%s#%d", d.Tok, grouped[d.Tok.String()]) |
| 301 | } else { |
| 302 | switch s := d.Specs[0].(type) { |
| 303 | case *ast.ValueSpec: |
| 304 | name = d.Tok.String() + " " + s.Names[0].Name |
| 305 | case *ast.TypeSpec: |
| 306 | name = "type " + s.Name.Name |
| 307 | } |
| 308 | } |
| 309 | default: |
| 310 | continue |
| 311 | } |
| 312 | start, end := fset.Position(decl.Pos()).Offset, fset.Position(decl.End()).Offset |
| 313 | var kinds []string |
| 314 | done := map[*ast.Ident]bool{} |
| 315 | ast.Inspect(decl, func(n ast.Node) bool { |
| 316 | mark := func(kind string, pos token.Pos) { |
| 317 | kinds = append(kinds, fmt.Sprintf("%s@%d", kind, fset.Position(pos).Offset-start)) |
| 318 | } |
| 319 | switch n := n.(type) { |
| 320 | case *ast.SelectorExpr: |
| 321 | if field := typed[fmt.Sprintf("%s@%d:%d", filepath.Join(root, filepath.FromSlash(p)), fset.Position(n.Pos()).Offset, fset.Position(n.End()).Offset)]; field != "" { |
| 322 | mark(field, n.Pos()) |
| 323 | } else if !active[filepath.Join(root, filepath.FromSlash(p))] && fields[n.Sel.Name] { |
| 324 | mark("candidate:"+n.Sel.Name, n.Pos()) |
| 325 | } |
| 326 | if ioNames[n.Sel.Name] { |
| 327 | mark("file:"+n.Sel.Name, n.Pos()) |
| 328 | } |
| 329 | done[n.Sel] = true |
| 330 | case *ast.Ident: |
| 331 | if kind := typed[fmt.Sprintf("%s@%d:%d", filepath.Join(root, filepath.FromSlash(p)), fset.Position(n.Pos()).Offset, fset.Position(n.End()).Offset)]; strings.HasPrefix(kind, "whole:") { |
| 332 | mark(kind, n.Pos()) |
| 333 | } |
| 334 | // Includes unqualified functions, function-field declarations, dot |
| 335 | // imports, and aliases of an input primitive. Definitions overcount. |
| 336 | if !done[n] && ioNames[n.Name] { |
| 337 | mark("file:"+n.Name, n.Pos()) |
| 338 | } |
| 339 | case *ast.BasicLit: |
| 340 | if n.Kind == token.STRING && (strings.Contains(n.Value, "/data") || strings.Contains(n.Value, "CARLOS_DATA") || strings.Contains(n.Value, "DATA_DIR")) { |
| 341 | mark("data-literal", n.Pos()) |
| 342 | } |
| 343 | } |
| 344 | return true |
| 345 | }) |
| 346 | sum := sha256.Sum256(src[start:end]) |
| 347 | out.Declarations[p+":"+name] = hex.EncodeToString(sum[:]) |
| 348 | if len(kinds) == 0 { |
| 349 | continue |
| 350 | } |
| 351 | sort.Strings(kinds) |
| 352 | s := sha256.Sum256(src[start:end]) |
| 353 | out.Entries = append(out.Entries, rootReadEntry{p, name, strings.Join(kinds, ","), hex.EncodeToString(s[:])}) |
| 354 | } |
| 355 | } |
| 356 | // A source envelope pins even declarations that use a previously unknown |
| 357 | // input primitive, indirect reflection, inline assembly or a wrapper. A |
| 358 | // changed/new source cannot disappear through a scanner naming blind spot. |
| 359 | for p, body := range extra { |
| 360 | sum := sha256.Sum256(body) |
| 361 | dir := filepath.ToSlash(filepath.Dir(p)) |
| 362 | coverage[dir] = append(coverage[dir], p+"\x00"+hex.EncodeToString(sum[:])) |
| 363 | // A shell command or unit can read data via a variable. Review the |
| 364 | // complete file as authority, rather than matching only literal /data. |
| 365 | if p != "go.mod" && p != "go.sum" { |
| 366 | out.Declarations[p+":external"] = hex.EncodeToString(sum[:]) |
| 367 | out.Entries = append(out.Entries, rootReadEntry{p, "external", "file:external@0", hex.EncodeToString(sum[:])}) |
| 368 | } |
| 369 | } |
| 370 | sort.Slice(out.Entries, func(i, j int) bool { |
| 371 | a, b := out.Entries[i], out.Entries[j] |
| 372 | if a.File != b.File { |
| 373 | return a.File < b.File |
| 374 | } |
| 375 | return a.Unit < b.Unit |
| 376 | }) |
| 377 | for dir, rows := range coverage { |
| 378 | sort.Strings(rows) |
| 379 | s := sha256.Sum256([]byte(strings.Join(rows, "\n"))) |
| 380 | out.Coverage[dir] = hex.EncodeToString(s[:]) |
| 381 | } |
| 382 | return out, nil |
| 383 | } |
| 384 | |
| 385 | type classifiedRootRead struct { |
| 386 | rootReadEntry |
| 387 | Class, Task, Basis string |
| 388 | } |
| 389 | |
| 390 | type rootReadInventory struct { |
| 391 | Frozen map[string]classifiedRootRead |
| 392 | Removed map[string]string |
| 393 | Entries map[string]classifiedRootRead |
| 394 | Coverage map[string]string |
| 395 | Fields string |
| 396 | Legacy map[string]bool |
| 397 | } |
| 398 | |
| 399 | func loadRootReadInventory(path string, inv *rootReadInventory, delta bool) error { |
| 400 | raw, err := os.ReadFile(path) |
| 401 | if err != nil { |
| 402 | return err |
| 403 | } |
| 404 | seen := map[string]bool{} |
| 405 | for i, line := range strings.Split(strings.TrimSpace(string(raw)), "\n") { |
| 406 | if line == "" || strings.HasPrefix(line, "#") { |
| 407 | continue |
| 408 | } |
| 409 | col := strings.Split(line, "\t") |
| 410 | key := "" |
| 411 | switch col[0] { |
| 412 | case "@legacy": |
| 413 | if len(col) != 9 || len(col[1]) != 64 { |
| 414 | return fmt.Errorf("%s:%d: legacy fingerprint", path, i+1) |
| 415 | } |
| 416 | if _, err := hex.DecodeString(col[1]); err != nil { |
| 417 | return err |
| 418 | } |
| 419 | e := classifiedRootRead{rootReadEntry: rootReadEntry{File: col[2], Unit: col[3], Kinds: col[7], Digest: col[8]}, Class: col[4], Task: col[5], Basis: col[6]} |
| 420 | if e.Class != "legacy" || rootReadLegacyFingerprint(e) != col[1] { |
| 421 | return fmt.Errorf("invalid frozen declaration %s:%s", e.File, e.Unit) |
| 422 | } |
| 423 | if inv.Frozen == nil { |
| 424 | inv.Frozen = map[string]classifiedRootRead{} |
| 425 | } |
| 426 | identity := e.File + ":" + e.Unit |
| 427 | if _, exists := inv.Frozen[identity]; exists && !delta { |
| 428 | return fmt.Errorf("duplicate frozen declaration %s", identity) |
| 429 | } |
| 430 | inv.Frozen[identity] = e |
| 431 | key = "@legacy:" + col[1] |
| 432 | if inv.Legacy == nil { |
| 433 | inv.Legacy = map[string]bool{} |
| 434 | } |
| 435 | if inv.Legacy[col[1]] { |
| 436 | return fmt.Errorf("duplicate legacy fingerprint %s", col[1]) |
| 437 | } |
| 438 | inv.Legacy[col[1]] = true |
| 439 | case "@removed": |
| 440 | // H3 may own a removal only under a recorded census ruling (census |
| 441 | // ruling zddr3); it never becomes a kept owner for an authority, |
| 442 | // hint or diagnostic row, so rootReadKeptTask stays unchanged. |
| 443 | owned := rootReadKeptTask(col[3]) || (len(col) == 5 && col[3] == "H3" && strings.HasPrefix(col[4], "census-ruling-")) |
| 444 | if len(col) != 5 || !owned || strings.TrimSpace(col[4]) == "" { |
| 445 | return fmt.Errorf("%s:%d: removal needs owner and basis", path, i+1) |
| 446 | } |
| 447 | key = col[1] + ":" + col[2] |
| 448 | if inv.Removed == nil { |
| 449 | inv.Removed = map[string]string{} |
| 450 | } |
| 451 | inv.Removed[key] = col[3] + ";" + col[4] |
| 452 | key = "@removed:" + key |
| 453 | case "@fields": |
| 454 | if len(col) != 2 { |
| 455 | return fmt.Errorf("%s:%d: fields", path, i+1) |
| 456 | } |
| 457 | key = "@fields" |
| 458 | inv.Fields = col[1] |
| 459 | case "@coverage": |
| 460 | if len(col) != 3 { |
| 461 | return fmt.Errorf("%s:%d: coverage", path, i+1) |
| 462 | } |
| 463 | key = "@coverage:" + col[1] |
| 464 | inv.Coverage[col[1]] = col[2] |
| 465 | case "@remove": |
| 466 | if !delta || len(col) != 3 { |
| 467 | return fmt.Errorf("%s:%d: removal", path, i+1) |
| 468 | } |
| 469 | key = col[1] + ":" + col[2] |
| 470 | if _, ok := inv.Entries[key]; !ok { |
| 471 | return fmt.Errorf("removal of unknown %s", key) |
| 472 | } |
| 473 | delete(inv.Entries, key) |
| 474 | default: |
| 475 | if len(col) != 7 { |
| 476 | return fmt.Errorf("%s:%d: entry", path, i+1) |
| 477 | } |
| 478 | entry := classifiedRootRead{rootReadEntry: rootReadEntry{File: col[0], Unit: col[1], Kinds: col[5], Digest: col[6]}, Class: col[2], Task: col[3], Basis: col[4]} |
| 479 | key = entry.File + ":" + entry.Unit |
| 480 | if entry.Class != "authority" && entry.Class != "hint" && entry.Class != "diagnostic" && entry.Class != "legacy" { |
| 481 | return fmt.Errorf("unclassified %s", key) |
| 482 | } |
| 483 | if entry.Class == "legacy" && (entry.Task != "accepted-legacy" || !strings.HasPrefix(entry.Basis, "accepted-legacy;rev3=")) { |
| 484 | return fmt.Errorf("invalid legacy disposition %s", key) |
| 485 | } |
| 486 | if entry.Class == "authority" && !rootReadKeptTask(entry.Task) { |
| 487 | return fmt.Errorf("authority without kept-family owner %s", key) |
| 488 | } |
| 489 | if entry.Task == "" || entry.Basis == "" { |
| 490 | return fmt.Errorf("no disposition for %s", key) |
| 491 | } |
| 492 | if !delta { |
| 493 | if _, ok := inv.Entries[key]; ok { |
| 494 | return fmt.Errorf("duplicate %s", key) |
| 495 | } |
| 496 | } |
| 497 | inv.Entries[key] = entry |
| 498 | } |
| 499 | if seen[key] { |
| 500 | return fmt.Errorf("duplicate inventory row %s", key) |
| 501 | } |
| 502 | seen[key] = true |
| 503 | } |
| 504 | return nil |
| 505 | } |
| 506 | |
| 507 | func rootReadKeptTask(tasks string) bool { |
| 508 | for _, task := range strings.Split(tasks, ",") { |
| 509 | switch task { |
| 510 | case "A", "B", "B2", "B3", "B4", "C", "D", "E1", "E2", "E3", "E4", "F", "G": |
| 511 | return true |
| 512 | } |
| 513 | } |
| 514 | return false |
| 515 | } |
| 516 | |
| 517 | func rootReadLegacyFingerprint(e classifiedRootRead) string { |
| 518 | line := strings.Join([]string{e.File, e.Unit, e.Class, e.Task, e.Basis, e.Kinds, e.Digest}, "\t") |
| 519 | sum := sha256.Sum256([]byte(line)) |
| 520 | return hex.EncodeToString(sum[:]) |
| 521 | } |
| 522 | |
| 523 | func rootReadLegacySeal(inv rootReadInventory) string { |
| 524 | var frozen []string |
| 525 | for hash := range inv.Legacy { |
| 526 | frozen = append(frozen, hash) |
| 527 | } |
| 528 | sort.Strings(frozen) |
| 529 | sum := sha256.Sum256([]byte(strings.Join(frozen, "\n"))) |
| 530 | return hex.EncodeToString(sum[:]) |
| 531 | } |
| 532 | |
| 533 | func checkRootReadLegacy(inv rootReadInventory, seal string) []string { |
| 534 | var errs []string |
| 535 | if rootReadLegacySeal(inv) != seal { |
| 536 | errs = append(errs, "changed legacy freeze; separate scope ruling required") |
| 537 | } |
| 538 | for key, e := range inv.Entries { |
| 539 | if e.Class == "legacy" && !inv.Legacy[rootReadLegacyFingerprint(e)] { |
| 540 | errs = append(errs, "changed legacy root read "+key+"; reclassify before changing source or disposition") |
| 541 | } |
| 542 | } |
| 543 | sort.Strings(errs) |
| 544 | return errs |
| 545 | } |
| 546 | |
| 547 | func compareRootReads(got rootReadScan, want rootReadInventory) []string { |
| 548 | var errs []string |
| 549 | seen := map[string]bool{} |
| 550 | for _, g := range got.Entries { |
| 551 | key := g.File + ":" + g.Unit |
| 552 | if seen[key] { |
| 553 | errs = append(errs, "duplicate scanned unit "+key) |
| 554 | } |
| 555 | seen[key] = true |
| 556 | w, ok := want.Entries[key] |
| 557 | if !ok { |
| 558 | errs = append(errs, "unclassified root read "+key+" "+g.Kinds) |
| 559 | continue |
| 560 | } |
| 561 | if w.Kinds != g.Kinds || w.Digest != g.Digest { |
| 562 | errs = append(errs, "changed root read "+key+"; review class, task, sites and digest") |
| 563 | } |
| 564 | } |
| 565 | for key := range want.Entries { |
| 566 | if !seen[key] && !(want.Entries[key].Class != "legacy" && want.Entries[key].Kinds == "" && got.Declarations[key] == want.Entries[key].Digest) { |
| 567 | errs = append(errs, "stale root read "+key) |
| 568 | } |
| 569 | } |
| 570 | for dir, hash := range got.Coverage { |
| 571 | if want.Coverage[dir] != hash { |
| 572 | errs = append(errs, "changed root source envelope "+dir) |
| 573 | } |
| 574 | } |
| 575 | for dir := range want.Coverage { |
| 576 | if _, ok := got.Coverage[dir]; !ok { |
| 577 | errs = append(errs, "stale root source envelope "+dir) |
| 578 | } |
| 579 | } |
| 580 | if strings.Join(got.Fields, ",") != want.Fields { |
| 581 | errs = append(errs, "changed registry schema") |
| 582 | } |
| 583 | sort.Strings(errs) |
| 584 | return errs |
| 585 | } |
| 586 | |
| 587 | func TestPlan14RootReadCensus(t *testing.T) { |
| 588 | // Reads the source tree and testdata only (go list writes nothing |
| 589 | // here but the build cache), so it runs in parallel. |
| 590 | t.Parallel() |
| 591 | root := censusRoot(t) |
| 592 | got, err := rootReadProduction(root) |
| 593 | if err != nil { |
| 594 | t.Fatal(err) |
| 595 | } |
| 596 | inv, seal := rootReadChain(t, root) |
| 597 | errs := append(compareRootReads(got, inv), checkRootReadLegacy(inv, seal)...) |
| 598 | errs = append(errs, checkRootReadDeclarations(got, inv)...) |
| 599 | if len(errs) > 0 { |
| 600 | t.Fatalf("root read census: %d discrepancies\n%s\n\nmake census-card shows what changed; make census-propose NAME=<branch> CLAIMS=<file> writes the overlay and its approval card (AGENTS.md, The census)", len(errs), strings.Join(errs, "\n")) |
| 601 | } |
| 602 | counts := map[string]int{} |
| 603 | sites := 0 |
| 604 | for _, e := range inv.Entries { |
| 605 | counts[e.Class]++ |
| 606 | sites += len(strings.Split(e.Kinds, ",")) |
| 607 | } |
| 608 | t.Logf("%d units, %d sites, %d source envelopes; classes %v", len(inv.Entries), sites, len(inv.Coverage), counts) |
| 609 | } |
| 610 | |
| 611 | // rootReadChain loads the census inventory: the base file and every overlay |
| 612 | // in order, each loaded only after its predecessor's seal is checked, then |
| 613 | // the overlays the chain manifest lists (rootReadManifest). It returns the |
| 614 | // inventory and the seal the last overlay leaves. |
| 615 | func rootReadChain(t testing.TB, root string) (rootReadInventory, string) { |
| 616 | t.Helper() |
| 617 | return rootReadChainBefore(t, root, "") |
| 618 | } |
| 619 | |
| 620 | // rootReadChainBefore is rootReadChain without the manifest's last overlay |
| 621 | // when that overlay is the file named last (a census-propose re-run |
| 622 | // proposing against the chain before its own overlay); "" loads it all. |
| 623 | func rootReadChainBefore(t testing.TB, root, last string) (rootReadInventory, string) { |
| 624 | t.Helper() |
| 625 | var err error |
| 626 | inv := rootReadInventory{Entries: map[string]classifiedRootRead{}, Coverage: map[string]string{}} |
| 627 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads.tsv"), &inv, false); err != nil { |
| 628 | t.Fatal(err) |
| 629 | } |
| 630 | // This is the explicitly pinned integrated variant, not a generic waiver. |
| 631 | // Its exact full production envelope still has to match. |
| 632 | seal := "eded8ea32c869352dd0f15b20e817ff91bdb45844c97b9c72883e603a0b1ec16" |
| 633 | _, err = os.Stat(filepath.Join(root, "internal/host/movesheldrelease_production.go")) |
| 634 | if err == nil { |
| 635 | seal = "fddd1cfd0391db80770074c47156dca161c5618d0d92da08669aea2cc22dcf9e" |
| 636 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-moves.tsv"), &inv, true); err != nil { |
| 637 | t.Fatal(err) |
| 638 | } |
| 639 | } else if !os.IsNotExist(err) { |
| 640 | t.Fatal(err) |
| 641 | } |
| 642 | if errs := checkRootReadLegacy(inv, seal); len(errs) != 0 { |
| 643 | t.Fatal(errs) |
| 644 | } |
| 645 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-merge.tsv"), &inv, true); err != nil { |
| 646 | t.Fatal(err) |
| 647 | } |
| 648 | seal = "c88a37629c95dd397beeb95f5ef101438fa884ac04dc4435aa55387b897d393c" |
| 649 | if errs := checkRootReadLegacy(inv, seal); len(errs) != 0 { |
| 650 | t.Fatal(errs) |
| 651 | } |
| 652 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-fix1.tsv"), &inv, true); err != nil { |
| 653 | t.Fatal(err) |
| 654 | } |
| 655 | seal = "c3351f27f6467fc33b319b36a7e64b75b2d992232f956d9c6243cc49e0c05396" |
| 656 | if errs := checkRootReadLegacy(inv, seal); len(errs) != 0 { |
| 657 | t.Fatal(errs) |
| 658 | } |
| 659 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-fix2.tsv"), &inv, true); err != nil { |
| 660 | t.Fatal(err) |
| 661 | } |
| 662 | if errs := checkRootReadLegacy(inv, seal); len(errs) != 0 { |
| 663 | t.Fatal(errs) |
| 664 | } |
| 665 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-merge3.tsv"), &inv, true); err != nil { |
| 666 | t.Fatal(err) |
| 667 | } |
| 668 | seal = "65dba959f80cc47e9fd99be145ba8b24bb3b37ffaf77b47d1b9cc3e01da14b47" |
| 669 | if errs := checkRootReadLegacy(inv, seal); len(errs) != 0 { |
| 670 | t.Fatal(errs) |
| 671 | } |
| 672 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-fix4.tsv"), &inv, true); err != nil { |
| 673 | t.Fatal(err) |
| 674 | } |
| 675 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-fix5.tsv"), &inv, true); err != nil { |
| 676 | t.Fatal(err) |
| 677 | } |
| 678 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-fix6.tsv"), &inv, true); err != nil { |
| 679 | t.Fatal(err) |
| 680 | } |
| 681 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-zddr3.tsv"), &inv, true); err != nil { |
| 682 | t.Fatal(err) |
| 683 | } |
| 684 | // Census ruling zddr3 (#272): the exec shim's accepted-legacy rows and the |
| 685 | // three refreshed legacy rows add thirteen frozen declarations. |
| 686 | seal = "c748e02bfe907a009ef42eb544b1a44fe813f4f7c13e454be3980aec4fade568" |
| 687 | if errs := checkRootReadLegacy(inv, seal); len(errs) != 0 { |
| 688 | t.Fatal(errs) |
| 689 | } |
| 690 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-hotfix281.tsv"), &inv, true); err != nil { |
| 691 | t.Fatal(err) |
| 692 | } |
| 693 | seal = "6da50872e15f7f396d23e5b1b2e044a73cd9e5c07d85c8e22252b0a1a5304bbd" |
| 694 | if errs := checkRootReadLegacy(inv, seal); len(errs) != 0 { |
| 695 | t.Fatal(errs) |
| 696 | } |
| 697 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-zddr4.tsv"), &inv, true); err != nil { |
| 698 | t.Fatal(err) |
| 699 | } |
| 700 | // Census ruling zddr4 (zero-downtime rev 19, Gate F, #272): ten changed |
| 701 | // activator rows and four new ones (generation socket directories) add |
| 702 | // fourteen frozen declarations. |
| 703 | seal = "e736a5e2652cc546db02c5a385ae7da9fa8a4313d3064f6b431642431974d65d" |
| 704 | // Census overlay reserved285 (#285): envelope-only, for the reserved-sqid |
| 705 | // skip in the account allocator and its materialization log line. No |
| 706 | // root-read unit changes, so the legacy seal above stands. |
| 707 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-reserved285.tsv"), &inv, true); err != nil { |
| 708 | t.Fatal(err) |
| 709 | } |
| 710 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-292.tsv"), &inv, true); err != nil { |
| 711 | t.Fatal(err) |
| 712 | } |
| 713 | // Census ruling 292 (amadan #292, platform-1e, extended): the agent's |
| 714 | // shutdown gates, the legacy run unit's termination proof, the retried |
| 715 | // conditional lease release, and root's per-host ordering of legacy run |
| 716 | // starts against stops (type Server, J2,J4). Eight changed activator |
| 717 | // rows, three new proof units and the reclassified instance-control |
| 718 | // Server declaration add twelve frozen declarations. |
| 719 | seal = "3b2506dd2ab3986d0df0ceda7f596e5a46ea4d1dfbc17b1d0fd8d5f176e8038d" |
| 720 | // Census ruling 275 (amadan #275, platform-1e): envelope-only, for the SDK |
| 721 | // clients held as method values in awsinv, alerting, host, edge and |
| 722 | // steering. No root-read unit changed or was added, so the legacy seal |
| 723 | // above stands. |
| 724 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-census-ruling-275.tsv"), &inv, true); err != nil { |
| 725 | t.Fatal(err) |
| 726 | } |
| 727 | if errs := checkRootReadLegacy(inv, seal); len(errs) != 0 { |
| 728 | t.Fatal(errs) |
| 729 | } |
| 730 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-census-ruling-owed.tsv"), &inv, true); err != nil { |
| 731 | t.Fatal(err) |
| 732 | } |
| 733 | // Census ruling owed (amadan #272, zdd-owed-items, platform-1e): a cold |
| 734 | // wake's build-state record is written at its first exec, after the |
| 735 | // replica-key gate and the cgroup refusals, and the exec is decided |
| 736 | // again after the write. Four changed activator rows add four frozen |
| 737 | // declarations. |
| 738 | seal = "79db70c4c396457e563bf792027f98eeed7cef3dc54fff8abedb025e7db1dfbb" |
| 739 | if errs := checkRootReadLegacy(inv, seal); len(errs) != 0 { |
| 740 | t.Fatal(errs) |
| 741 | } |
| 742 | // Census ruling carlos-run (Plan 14 controller, 2026-10-14): the |
| 743 | // carlos-run branch's overlay (carlos run, #259), loaded last, after |
| 744 | // census ruling owed. It adds 59 frozen declarations: the 58 it was ruled |
| 745 | // with, and instance control's type Server, which census ruling 292 made |
| 746 | // accepted-legacy and which this overlay re-digests as main classed it. |
| 747 | // Its four instance-control rows are accepted legacy on two conditions, |
| 748 | // recorded in the overlay's header: the cmd template-equality test |
| 749 | // stays, and the cmd verb stays on refuseStagedExe. |
| 750 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-carlos-run.tsv"), &inv, true); err != nil { |
| 751 | t.Fatal(err) |
| 752 | } |
| 753 | seal = "96e521e60dfc0b55e2bc08adbb7a7682a607a30599a8cf66e7c5689d6cf7fe5a" |
| 754 | if errs := checkRootReadLegacy(inv, seal); len(errs) != 0 { |
| 755 | t.Fatal(errs) |
| 756 | } |
| 757 | // Census ruling seeded-instances (Plan 14 controller, 2026-10-14): the |
| 758 | // seeded-instances branch's overlay (seeded instances, #260 and #261), |
| 759 | // loaded last, after carlos-run's. It adds 98 frozen declarations. The |
| 760 | // seeds floor's root readers are authority F,J4 and deny-only; |
| 761 | // CollectCensusSeeds and ImportJournalHosts are authority G on the |
| 762 | // condition, recorded in the overlay's header, that they only ever feed |
| 763 | // a refusal. Its last block is census ruling 292fix (Plan 14 controller, |
| 764 | // 2026-10-15), an extension of census ruling 292 for the seeded pre-land |
| 765 | // review's P2: doWake, cleanupFailedWake and reapUnprovenWake, accepted |
| 766 | // legacy as on main. |
| 767 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-seeded-instances.tsv"), &inv, true); err != nil { |
| 768 | t.Fatal(err) |
| 769 | } |
| 770 | seal = "10e024eccd584f40b64e959a105c551302585e3e14e55104fc250dae5960898a" |
| 771 | // Census overlay hostfiles-cap-236 (amadan #236): envelope-only, for |
| 772 | // hostfiles exporting its cap rule as RestorableWithinCap. No root-read |
| 773 | // unit changed or was added, so the legacy seal above stands. |
| 774 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-hostfiles-cap-236.tsv"), &inv, true); err != nil { |
| 775 | t.Fatal(err) |
| 776 | } |
| 777 | if errs := checkRootReadLegacy(inv, seal); len(errs) != 0 { |
| 778 | t.Fatal(errs) |
| 779 | } |
| 780 | // Census overlay moves-overcap-cut-236 (#236, moves' part of Paul's |
| 781 | // ruling of 2026-10-15), loaded last: the moves source round's files |
| 782 | // manifest preflight, under census ruling moves-overcap-cut-236 |
| 783 | // (platform-1e, 2026-10-15). sourceRefusal and sourceRound stay |
| 784 | // accepted-legacy with the ruling's lines, on the binding condition that |
| 785 | // the preflight is deny-only (the overlay's header). The ruling's |
| 786 | // extension (fix round 1) covers archive.walk, buildPrepared and |
| 787 | // readFileBounded for the files tree's cap, on the binding condition that |
| 788 | // no read falls back to a path-based open when a root open fails. The |
| 789 | // seal moves; the internal/host and internal/moves/archive envelopes are |
| 790 | // re-pinned. |
| 791 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-moves-overcap-cut-236.tsv"), &inv, true); err != nil { |
| 792 | t.Fatal(err) |
| 793 | } |
| 794 | seal = "fc38db001ebd8825709a8ca9c41cc0e8f165c1b1967e5967347dc9bd3556afec" |
| 795 | if errs := checkRootReadLegacy(inv, seal); len(errs) != 0 { |
| 796 | t.Fatal(errs) |
| 797 | } |
| 798 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-seed.tsv"), &inv, true); err != nil { |
| 799 | t.Fatal(err) |
| 800 | } |
| 801 | // FS-R6 preserves three legacy readers; FS-R10 re-digests the test-only S3 server. Other proposals remain unapproved. |
| 802 | seal = "624e96c0f0fd397d09575e807f7347f86cbcb109b4b713eb8369f0b64622e483" |
| 803 | if errs := checkRootReadLegacy(inv, seal); len(errs) != 0 { |
| 804 | t.Fatal(errs) |
| 805 | } |
| 806 | // Census overlay home-part2 (amadan #236, moves into home part 2): T3's |
| 807 | // home rule (moves.CompareEnv's targetHome) and the move-portable env |
| 808 | // pin (moves.Pinned, ReportLaunch, LocalDigest; ExecBackend.LaunchLayers). |
| 809 | // Twelve changed or new units re-frozen at main's dispositions, ten of |
| 810 | // them legacy, add ten frozen declarations; Task 4's revert fix adds |
| 811 | // revertRun.probing, frozen at its sibling units' disposition, an |
| 812 | // eleventh. Task 5's move door (a home target under host claims) |
| 813 | // re-freezes Server.handleAPIInstanceMoveRequest and |
| 814 | // Server.startClaimModeMove at main's dispositions, read sets |
| 815 | // unchanged, two more. Ruled 2026-10-16 as census-ruling-home-part2 |
| 816 | // (the overlay's header), on the condition that revertRun.probing's |
| 817 | // re-read accepts only the probe wake's own writes; that check |
| 818 | // re-pinned revertRun.probing at the same disposition, so the seal moved. |
| 819 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-home-part2.tsv"), &inv, true); err != nil { |
| 820 | t.Fatal(err) |
| 821 | } |
| 822 | // Census overlay fix-empty-app-key (RULING OWED, platform-1e), loaded |
| 823 | // last: envelope-only, at main's dispositions. The apps/<sqid>/<app>/ |
| 824 | // family key builders refuse an empty or invalid segment, and the box |
| 825 | // shippers skip a refused app. No root-read unit changed or was added, so |
| 826 | // the seal below is home-part2's. |
| 827 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-fix-empty-app-key.tsv"), &inv, true); err != nil { |
| 828 | t.Fatal(err) |
| 829 | } |
| 830 | seal = "5d1b9b6addf6c9964ed18b417ee5b3d0d49fd43e1acabb775e054bba4b6e314e" |
| 831 | if errs := checkRootReadLegacy(inv, seal); len(errs) != 0 { |
| 832 | t.Fatal(errs) |
| 833 | } |
| 834 | // Census overlay region-ha-2a (region HA spec 2, plan 2a), loaded last. |
| 835 | // Its header says what each plan task added. The accepted-legacy rows |
| 836 | // it adds are census ruling region-ha-2a's (platform-1e), and move the |
| 837 | // seal. |
| 838 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-region-ha-2a.tsv"), &inv, true); err != nil { |
| 839 | t.Fatal(err) |
| 840 | } |
| 841 | seal = "8198674d8809ef90ecebac55324bfd75e69fb3d73bc27b942f29ca2f43e3fe8d" |
| 842 | if errs := checkRootReadLegacy(inv, seal); len(errs) != 0 { |
| 843 | t.Fatal(errs) |
| 844 | } |
| 845 | // Track E precedes Part 3, which records their merged declarations. |
| 846 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-e.tsv"), &inv, true); err != nil { |
| 847 | t.Fatal(err) |
| 848 | } |
| 849 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-p3.tsv"), &inv, true); err != nil { |
| 850 | t.Fatal(err) |
| 851 | } |
| 852 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-activation-tools.tsv"), &inv, true); err != nil { |
| 853 | t.Fatal(err) |
| 854 | } |
| 855 | // Census overlay region-ha-2b (region HA spec 2, plan 2b), loaded last. |
| 856 | // Its header says what each plan task added. The rows it adds are |
| 857 | // census ruling region-ha-2b's (platform-1e); its accepted-legacy rows |
| 858 | // move the seal. |
| 859 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-region-ha-2b.tsv"), &inv, true); err != nil { |
| 860 | t.Fatal(err) |
| 861 | } |
| 862 | seal = "fc584e113d0c6786bdeb3344354ae567c112c607e68f85056352545557a91b5d" |
| 863 | // Census overlay box-publish-record (carlos system box publish-record), |
| 864 | // loaded last, under census-ruling-box-publish-record (platform-1e, |
| 865 | // R1-R4): root unreachable, the verb refuses euid 0 (R1). Envelope-only: |
| 866 | // it re-pins the internal/host and root package envelopes, adds and |
| 867 | // changes no root-read unit, and the seal stands. |
| 868 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-box-publish-record.tsv"), &inv, true); err != nil { |
| 869 | t.Fatal(err) |
| 870 | } |
| 871 | // Census overlay claim-step6-artifacts (claim trial step 6: sign-policy, |
| 872 | // publish-identity, install-console and guard-arm's parse), loaded last. |
| 873 | // Ruled by the controller, accepted as proposed |
| 874 | // (docs/superpowers/notes/census-proposal-claim-step6-artifacts.md). It |
| 875 | // adds no accepted-legacy row, so the seal stands. |
| 876 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-claim-step6-artifacts.tsv"), &inv, true); err != nil { |
| 877 | t.Fatal(err) |
| 878 | } |
| 879 | // Census overlay claim-step6-artifacts-fix1 (fix round 1: F1 bounded ELF |
| 880 | // pre-check, F3 sums bound to the tag, F4 --out reserved before the |
| 881 | // phase), loaded last, under census-ruling-claim-step6-artifacts-fix1 |
| 882 | // (platform-1e, accepted as proposed). It adds no accepted-legacy row, so |
| 883 | // the seal stands. |
| 884 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-claim-step6-artifacts-fix1.tsv"), &inv, true); err != nil { |
| 885 | t.Fatal(err) |
| 886 | } |
| 887 | // Census overlay claim-step6-artifacts-fix2 (fix round 2: F1 name caps, |
| 888 | // F5 a reserved --out file is never removed), loaded last, under |
| 889 | // census-ruling-claim-step6-artifacts-fix2 (platform-1e, accepted as |
| 890 | // proposed). It adds no accepted-legacy row, so the seal stands. |
| 891 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-claim-step6-artifacts-fix2.tsv"), &inv, true); err != nil { |
| 892 | t.Fatal(err) |
| 893 | } |
| 894 | // Census overlay claim-prev-rehearsal (install-channel --policy: arm an |
| 895 | // updater-installed box, controller ruling R1), loaded last, under |
| 896 | // census-ruling-claim-prev-rehearsal (platform-1e, accepted as proposed; |
| 897 | // docs/superpowers/notes/census-proposal-claim-prev-rehearsal.md). It |
| 898 | // adds no accepted-legacy row, so the seal stands. |
| 899 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-claim-prev-rehearsal.tsv"), &inv, true); err != nil { |
| 900 | t.Fatal(err) |
| 901 | } |
| 902 | // Census overlay claim-prev-rehearsal-fix1 (fix round 1: mandatory |
| 903 | // carlos.prev record, unfinished-update refusal, the census branch for |
| 904 | // --policy, the hardened policy read), loaded last, under |
| 905 | // census-ruling-claim-prev-rehearsal-fix1 (platform-1e, accepted as |
| 906 | // proposed; docs/superpowers/notes/census-proposal-claim-prev-rehearsal.md, |
| 907 | // "Fix round 1"). It adds no accepted-legacy row, so the seal stands. |
| 908 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-claim-prev-rehearsal-fix1.tsv"), &inv, true); err != nil { |
| 909 | t.Fatal(err) |
| 910 | } |
| 911 | // Census overlay claim-prev-rehearsal-fix2 (fix round 2: carlos.prev's |
| 912 | // judgement always finished on --policy's already-installed path), |
| 913 | // loaded last. Envelope only. Under census-ruling-claim-prev-rehearsal-fix2 |
| 914 | // (platform-1e, accepted as proposed; |
| 915 | // docs/superpowers/notes/census-proposal-claim-prev-rehearsal.md, |
| 916 | // "Fix round 2"). The seal stands. |
| 917 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-claim-prev-rehearsal-fix2.tsv"), &inv, true); err != nil { |
| 918 | t.Fatal(err) |
| 919 | } |
| 920 | // Census overlay console-invite-delivery (console invite delivery), |
| 921 | // loaded last, under census-ruling-console-invite-delivery (platform-1e, |
| 922 | // R1-R5): seven units accepted legacy under the console (carlos-console) |
| 923 | // or tofu on an operator's machine, since the root carlos binary links |
| 924 | // neither internal/console nor internal/mail (R1, pinned by |
| 925 | // TestRootBinaryLinksNoConsoleOrMail); Keymail.SendApp retired by an H3 |
| 926 | // census-ruling removal (R2); the rev3 tag rewritten to the ruling's |
| 927 | // name (R4). The ruling is the scope ruling the freeze needs, and the |
| 928 | // seal moves. |
| 929 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-console-invite-delivery.tsv"), &inv, true); err != nil { |
| 930 | t.Fatal(err) |
| 931 | } |
| 932 | // Census overlay claim-untagged-pointer (install-channel --policy takes |
| 933 | // --identity, the published release identity, because the channel |
| 934 | // pointer carlos system update writes before a floor is published |
| 935 | // carries no release tag; controller ruling R1), loaded last, under |
| 936 | // census-ruling-claim-untagged-pointer (platform-1e, accepted as |
| 937 | // proposed; docs/superpowers/notes/census-proposal-claim-untagged-pointer.md): |
| 938 | // channelInstall.run and openChannelPolicy re-pinned with corrected |
| 939 | // bases at unchanged digests, sites and class F,J4, and the internal/host |
| 940 | // envelope re-pinned. It adds no accepted-legacy row, so the seal stands. |
| 941 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-claim-untagged-pointer.tsv"), &inv, true); err != nil { |
| 942 | t.Fatal(err) |
| 943 | } |
| 944 | // Census overlay claim-untagged-pointer-fix1 (review fix round 1: a |
| 945 | // missing identity object gets its own refusal; openChannelPolicy's |
| 946 | // comment names both files), loaded last. Envelope only. Under |
| 947 | // census-ruling-claim-untagged-pointer-fix1 (platform-1e, accepted as |
| 948 | // proposed; docs/superpowers/notes/census-proposal-claim-untagged-pointer.md, |
| 949 | // "Fix round 1"). The seal stands. |
| 950 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-claim-untagged-pointer-fix1.tsv"), &inv, true); err != nil { |
| 951 | t.Fatal(err) |
| 952 | } |
| 953 | // Census overlay claim-untagged-pointer-fix2 (Paul's copy approval |
| 954 | // applied: the re-approved RHB-C19 usage), loaded last. Envelope only. |
| 955 | // Under census-ruling-claim-untagged-pointer-fix2 (platform-1e, accepted |
| 956 | // as proposed; docs/superpowers/notes/census-proposal-claim-untagged-pointer.md, |
| 957 | // "Fix round 2"). The seal stands. |
| 958 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-claim-untagged-pointer-fix2.tsv"), &inv, true); err != nil { |
| 959 | t.Fatal(err) |
| 960 | } |
| 961 | // Census overlay claim-guard-umask (guard-arm's directories ignore the |
| 962 | // caller's umask), loaded last, under census-ruling-claim-guard-umask |
| 963 | // (platform-1e): RunSystemGuardArm re-pinned at its class F,J4, the new |
| 964 | // mkdirAllExact classified G,J4, and the internal/host envelope |
| 965 | // re-pinned. It adds no accepted-legacy row, so the seal stands. |
| 966 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-claim-guard-umask.tsv"), &inv, true); err != nil { |
| 967 | t.Fatal(err) |
| 968 | } |
| 969 | // Census overlay adopt-unit-root-pass-315 (amadan #315: a non-root agent |
| 970 | // hands a named unit's routes to the root pass), loaded last. Ruled by the census owner on #280 |
| 971 | // (census-ruling-315, accepted as proposed): adoptHost re-pinned at its |
| 972 | // class, type passConfig gains a bool, and unitServed (new) classified |
| 973 | // with processGroup, whose membership it inverts. Both envelopes |
| 974 | // re-pinned. It adds no accepted-legacy row, so the seal stands. |
| 975 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-adopt-unit-root-pass-315.tsv"), &inv, true); err != nil { |
| 976 | t.Fatal(err) |
| 977 | } |
| 978 | // Census overlay bootstrap-carlos-follows-316 (amadan #316: system status |
| 979 | // compares bootstrap/carlos with stable's build), loaded last. Envelope |
| 980 | // only, ruled accepted by the census owner on #280. The seal |
| 981 | // stands. |
| 982 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-bootstrap-carlos-follows-316.tsv"), &inv, true); err != nil { |
| 983 | t.Fatal(err) |
| 984 | } |
| 985 | seal = "53b3cbb6f46d657ce32f49dad84dae79316c80da949ccf37273833eef5c5b2d7" |
| 986 | // Census overlay claim-step8-readiness (dev cohort-1 step 8: the backfill |
| 987 | // phases, leo's migration census classes and window masks, the floor and |
| 988 | // retry directories' umask, and the absent legacy journal directory), |
| 989 | // loaded last. RULED (platform-1e, 2026-10-20): accepted as proposed |
| 990 | // (docs/superpowers/notes/census-proposal-claim-step8-readiness.md), |
| 991 | // marked rev3=census-ruling-claim-step8-readiness. It adds no accepted-legacy |
| 992 | // row, so the seal stands. |
| 993 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-claim-step8-readiness.tsv"), &inv, true); err != nil { |
| 994 | t.Fatal(err) |
| 995 | } |
| 996 | // Census overlay claim-empty-cohort-fix1 (fix round 1 for |
| 997 | // claim-empty-cohort-done: Backfill.Done lists the records again before |
| 998 | // and after its write; the root migration repairs its floor directories |
| 999 | // before the first floor read), loaded last. Ruled by platform-1e, accepted as |
| 1000 | // proposed (docs/superpowers/notes/census-proposal-claim-step8-readiness.md, |
| 1001 | // "Fix round 1"). It adds no accepted-legacy row, so the seal stands. |
| 1002 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-claim-empty-cohort-fix1.tsv"), &inv, true); err != nil { |
| 1003 | t.Fatal(err) |
| 1004 | } |
| 1005 | // Census overlay claim-empty-cohort-fix2 (fix round 2: the floor repair |
| 1006 | // walks from / by descriptor and checks every ancestor before it makes |
| 1007 | // or changes anything, through walkNoFollowDir, consoleDir's walk |
| 1008 | // factored out unchanged), loaded last. Ruled by platform-1e, accepted as proposed |
| 1009 | // (docs/superpowers/notes/census-proposal-claim-step8-readiness.md, |
| 1010 | // "Fix round 2"). It adds no accepted-legacy row, so the seal stands. |
| 1011 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-claim-empty-cohort-fix2.tsv"), &inv, true); err != nil { |
| 1012 | t.Fatal(err) |
| 1013 | } |
| 1014 | // Census overlay claim-rootaws-sentinel (rootaws's scrub treats its own |
| 1015 | // inherited disabled-file sentinels as no override, so a guard-armed |
| 1016 | // box's updater can read a candidate's version), loaded last. Envelope |
| 1017 | // only, ruled by platform-1e. The seal stands. |
| 1018 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-claim-rootaws-sentinel.tsv"), &inv, true); err != nil { |
| 1019 | t.Fatal(err) |
| 1020 | } |
| 1021 | // Census overlay claim-harness-census (the migration census keeps both |
| 1022 | // pointer-digest ratchets and install-control's activation lock, the |
| 1023 | // deployment harness's 8.2 stops), loaded last. Ruled by platform-d8, |
| 1024 | // accepted as proposed; no accepted-legacy row, so the seal stands. |
| 1025 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-claim-harness-census.tsv"), &inv, true); err != nil { |
| 1026 | t.Fatal(err) |
| 1027 | } |
| 1028 | // Census overlay claim-own-child (the generation census accepts |
| 1029 | // journal-migrate's own registry child past the root-task rule, untrusted), |
| 1030 | // loaded last. Ruled by platform-d8, accepted as proposed; no |
| 1031 | // accepted-legacy row, so the seal stands. |
| 1032 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-claim-own-child.tsv"), &inv, true); err != nil { |
| 1033 | t.Fatal(err) |
| 1034 | } |
| 1035 | // Census overlay claim-ack-refresh (G10: the box refreshes its own |
| 1036 | // execution acknowledgement while the op waits on the console, and the |
| 1037 | // regional enrollment check ignores the roster-wide revision but carries |
| 1038 | // the row's quarantine revision), loaded last. |
| 1039 | // census-ruling-claim-ack-refresh: Ruled by platform-d8, accepted |
| 1040 | // (docs/superpowers/notes/census-proposal-claim-step8-readiness.md, |
| 1041 | // "Ack refresh"); no accepted-legacy row, so the seal stands. |
| 1042 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-claim-ack-refresh.tsv"), &inv, true); err != nil { |
| 1043 | t.Fatal(err) |
| 1044 | } |
| 1045 | if errs := checkRootReadLegacy(inv, seal); len(errs) != 0 { |
| 1046 | t.Fatal(errs) |
| 1047 | } |
| 1048 | // Census overlay moves-offline (the offline operator move), loaded last. |
| 1049 | // census-ruling-moves-offline: RULED by the census owner (platform-1c, |
| 1050 | // 2026-10-20), accepted as proposed |
| 1051 | // (docs/superpowers/notes/census-proposal-moves-offline.md). Its 39 new |
| 1052 | // units run as the agent, the console or the CLI and make no root |
| 1053 | // decision, so they are proposed accepted legacy at their moves |
| 1054 | // siblings' dispositions; four changed legacy units are re-frozen and |
| 1055 | // two authority units re-pinned at main's class. 43 frozen declarations |
| 1056 | // are added, so the seal moves. The ruling's extension (platform-1c, |
| 1057 | // for 8dc2a544's release item) re-freezes landRun.release at its |
| 1058 | // disposition and accepts landRun.liveNotSealed as legacy, on the |
| 1059 | // condition that it compares the live link's Readlink target string and |
| 1060 | // follows nothing beyond the link; 44 frozen declarations in all. |
| 1061 | // OfflineStart re-pinned for a text-only helper extraction (copy gate), |
| 1062 | // 2026-10-20; no read, class or behaviour change. |
| 1063 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-moves-offline.tsv"), &inv, true); err != nil { |
| 1064 | t.Fatal(err) |
| 1065 | } |
| 1066 | seal = "393e7f456d8f02b2eee0c683880e5f7bf696b6ee0c87f20e5c113434065bec29" |
| 1067 | // Census overlay armed-box-updates (cherry-picked from the claim branch |
| 1068 | // for v0.28.5): rootaws's scrub treats its own inherited disabled-file |
| 1069 | // sentinels as no override, and hostfloor's guarded run returns stdout |
| 1070 | // only, so a guard-armed box's updater can read a candidate's version and |
| 1071 | // plan. Loaded last, under census-ruling-claim-rootaws-sentinel |
| 1072 | // (platform-1c). No legacy row; the seal stands. |
| 1073 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-armed-box-updates.tsv"), &inv, true); err != nil { |
| 1074 | t.Fatal(err) |
| 1075 | } |
| 1076 | // Census overlay install-console-upgrade (claims install-console |
| 1077 | // --upgrade --from: a guard-armed box's protected console can be |
| 1078 | // replaced, not only installed once), loaded last. |
| 1079 | // census-ruling-install-console-upgrade: RULED by platform-d8, accepted as proposed at da0a33b7 and b913bb65 |
| 1080 | // (docs/superpowers/notes/census-proposal-install-console-upgrade.md). |
| 1081 | // runSystemClaims is re-pinned at its class, consoleSlotHolds's read |
| 1082 | // moves into consoleSlotSHA, and upgradeProtectedConsole and |
| 1083 | // writeConsoleTemp are new, all authority G,J4. Round 2 (Astra's |
| 1084 | // review): readConsoleSource's read moves into readConsoleSourceFile, |
| 1085 | // and consoleDigest and consoleCounter.Read are new, also G,J4. No |
| 1086 | // legacy row; the seal stands. |
| 1087 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-install-console-upgrade.tsv"), &inv, true); err != nil { |
| 1088 | t.Fatal(err) |
| 1089 | } |
| 1090 | // Census overlay moves-offline-fixes (harness Run 9's defects 6 to 8 in |
| 1091 | // the offline operator move), loaded last, under |
| 1092 | // census-ruling-moves-offline-fixes (platform-1c, 2026-10-20); the |
| 1093 | // proposal names each unit's executing identity |
| 1094 | // (docs/superpowers/notes/census-proposal-moves-offline-fixes.md). Every |
| 1095 | // unit runs in the agent and makes no root decision: changed legacy |
| 1096 | // units re-frozen at main's disposition, new ones proposed accepted |
| 1097 | // legacy at their siblings'. Frozen declarations are added, so the seal |
| 1098 | // moves; main's is checked first. |
| 1099 | if errs := checkRootReadLegacy(inv, seal); len(errs) != 0 { |
| 1100 | t.Fatal(errs) |
| 1101 | } |
| 1102 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-moves-offline-fixes.tsv"), &inv, true); err != nil { |
| 1103 | t.Fatal(err) |
| 1104 | } |
| 1105 | seal = "826c54be9447f0a7c42386dba6be5888fc5a708bf1e5a57f2a6de6da4662a62f" |
| 1106 | // Census overlay console-switcher-filter (the console's account switcher |
| 1107 | // gains a client-side filter box and a scrolling list), loaded last. |
| 1108 | // PROPOSED-console-switcher-filter, asked of the census owner on #280. |
| 1109 | // layout.html is a legacy UI template re-frozen at its main disposition; |
| 1110 | // only browser-side CSS and script change. One frozen declaration is |
| 1111 | // added, so the seal moves; moves-offline-fixes' is checked first. |
| 1112 | if errs := checkRootReadLegacy(inv, seal); len(errs) != 0 { |
| 1113 | t.Fatal(errs) |
| 1114 | } |
| 1115 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-console-switcher-filter.tsv"), &inv, true); err != nil { |
| 1116 | t.Fatal(err) |
| 1117 | } |
| 1118 | seal = "a42b90d7a5c440ae8d94981fa3d80f46891e676ed0783574e193afc3534fef1a" |
| 1119 | // Paul approved this scoped inventory update on 2026-10-21. The final |
| 1120 | // independent branch review remains pending. Preserve the prior freeze |
| 1121 | // before adding the five console entries and six source envelopes. |
| 1122 | if errs := checkRootReadLegacy(inv, seal); len(errs) != 0 { |
| 1123 | t.Fatal(errs) |
| 1124 | } |
| 1125 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-scan-demand-revival.tsv"), &inv, true); err != nil { |
| 1126 | t.Fatal(err) |
| 1127 | } |
| 1128 | seal = "a21dba12b0f3cd0811c624ec352d6ff5c46936ef0da1d4ce9d31cdd13cc0f262" |
| 1129 | if errs := checkRootReadLegacy(inv, seal); len(errs) != 0 { |
| 1130 | t.Fatal(errs) |
| 1131 | } |
| 1132 | // Census overlay run-lifecycle-logs-325 (amadan #325: the box's carlos |
| 1133 | // run worker emits run started and run ended as member log events |
| 1134 | // through the agent's EventSink), loaded last, after scan-demand-revival. Proposed; ruling asked of |
| 1135 | // the census owner on #280. No root read, file read or record write is |
| 1136 | // added: runAgent and runsWorker.execute are re-pinned at their class and |
| 1137 | // task, and the internal/host envelope is re-pinned. The seal moves |
| 1138 | // because execute's legacy declaration was re-pinned. |
| 1139 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-run-lifecycle-logs-325.tsv"), &inv, true); err != nil { |
| 1140 | t.Fatal(err) |
| 1141 | } |
| 1142 | seal = "cf0a526b5f7c913160b1e6a76fa34c34dacd60cbacfd8019e736c2ab852399e3" |
| 1143 | // Census overlay design-plans-and-caps (plans 1: plan catalog, account |
| 1144 | // plans, limits record publisher and operator door), loaded last. |
| 1145 | // PROPOSED-design-plans-and-caps, asked of the census owner on #280. |
| 1146 | // New console store methods, LoadCatalog and the CLI's --sponsored read |
| 1147 | // are accepted legacy at their siblings' disposition; serve and |
| 1148 | // Store.migrate are re-frozen. Frozen declarations are added, so the |
| 1149 | // seal moves; run-lifecycle-logs-325's is checked first. |
| 1150 | if errs := checkRootReadLegacy(inv, seal); len(errs) != 0 { |
| 1151 | t.Fatal(errs) |
| 1152 | } |
| 1153 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-design-plans-and-caps.tsv"), &inv, true); err != nil { |
| 1154 | t.Fatal(err) |
| 1155 | } |
| 1156 | seal = "4f5e0ed6adf7d6bcefdfc9ce50599a21adb06ae3e6d49cab71835e116968df17" |
| 1157 | // Census overlay moves-offline-precopy (parallel copies in |
| 1158 | // lease.MirrorPrefix and the carlos move offline stage door), loaded |
| 1159 | // last, after design-plans-and-caps, under |
| 1160 | // census-ruling-moves-offline-precopy (the coordinator, as census owner, |
| 1161 | // 2026-10-21); the ruling names each unit's executing identity |
| 1162 | // (docs/superpowers/notes/census-proposal-moves-offline-precopy.md). |
| 1163 | // offlineSourceRevert is re-frozen at its disposition, offlineBegin |
| 1164 | // (OfflineStart's body) is new accepted legacy, and OfflineStart is |
| 1165 | // retired by an H3 census-ruling removal; offlineTakeSeal is re-frozen |
| 1166 | // for Astra's round 2. Three frozen declarations are added, so the seal |
| 1167 | // moves; design-plans-and-caps' is checked first. |
| 1168 | if errs := checkRootReadLegacy(inv, seal); len(errs) != 0 { |
| 1169 | t.Fatal(errs) |
| 1170 | } |
| 1171 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-moves-offline-precopy.tsv"), &inv, true); err != nil { |
| 1172 | t.Fatal(err) |
| 1173 | } |
| 1174 | seal = "940ed1bba8d911e0f57af83ecac253c77ba5e14dad590258e0ee03ec98ccb818" |
| 1175 | // Census overlay ci-speed-activator (amadan #291: CI speed), loaded last, |
| 1176 | // after moves-offline-precopy, under census-ruling-ci-speed-activator |
| 1177 | // (Paul, as census owner, 2026-10-21, accepted as proposed). Four |
| 1178 | // activator timings become package vars with the same values and |
| 1179 | // moveprobe gains a link-time grace override; only tests change them. |
| 1180 | // ExecBackend.waitRoleCgroupEmpty and |
| 1181 | // Service.startAdmitted run in the agent (user carlos), make no root |
| 1182 | // decision and are re-frozen at main's class and task with new digests; |
| 1183 | // their sites are unchanged. The internal/activator and cmd/moveprobe |
| 1184 | // envelopes are re-pinned. Two frozen declarations are added, so the seal |
| 1185 | // moves; moves-offline-precopy's is checked first. |
| 1186 | if errs := checkRootReadLegacy(inv, seal); len(errs) != 0 { |
| 1187 | t.Fatal(errs) |
| 1188 | } |
| 1189 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-ci-speed-activator.tsv"), &inv, true); err != nil { |
| 1190 | t.Fatal(err) |
| 1191 | } |
| 1192 | seal = "3737ebbd0ee87811839624724e552e0af02f63d70baaa0516a024440d848fdae" |
| 1193 | // Census overlay securelisten-337 (amadan #337: edge.SecureUnixListen |
| 1194 | // makes its socket owner-only with fchmod before bind instead of setting |
| 1195 | // the process umask around it), loaded last, after ci-speed-activator, |
| 1196 | // under census-ruling-securelisten-337 (Paul, as census owner, |
| 1197 | // 2026-10-21, accepted as proposed). SecureUnixListen in purge_listen_linux.go is new |
| 1198 | // accepted legacy at J4 root-input (as pendingDirs.appeared and its |
| 1199 | // caller ListenControl): one Lstat of the socket the agent has just |
| 1200 | // bound for itself to serve. The internal/edge envelope is re-pinned. |
| 1201 | // One frozen declaration is added, so the seal moves; |
| 1202 | // ci-speed-activator's is checked first. |
| 1203 | if errs := checkRootReadLegacy(inv, seal); len(errs) != 0 { |
| 1204 | t.Fatal(errs) |
| 1205 | } |
| 1206 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-securelisten-337.tsv"), &inv, true); err != nil { |
| 1207 | t.Fatal(err) |
| 1208 | } |
| 1209 | seal = "33a8ff46dd9fcf85664c42c42eaf01585af6c92806cb7830fa8f30fd0950c430" |
| 1210 | // Census overlay claims-on-gate-recover (a claims-on box's update gate |
| 1211 | // waits 120 s and fails fast; carlos-system-recover defers a refused |
| 1212 | // census with no journal open, accepts a service still running the |
| 1213 | // signed current or previous router build it was renamed over, retries |
| 1214 | // task-level refusals for at most 20 s and names each refusal's check, |
| 1215 | // unit, cgroup and exe), loaded last. PROPOSED-claims-on-gate-recover, |
| 1216 | // to be asked of the census owner on #280 |
| 1217 | // (docs/superpowers/notes/census-proposal-claims-on-gate-recover.md). |
| 1218 | // Changed authority units re-pinned at their class and task; the |
| 1219 | // service reader's reads move into three new units at its class (E2); |
| 1220 | // the deleted-exe image proof is a new root read at F,J4, as the |
| 1221 | // guard's own image proof. No legacy row; the seal stands. |
| 1222 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-claims-on-gate-recover.tsv"), &inv, true); err != nil { |
| 1223 | t.Fatal(err) |
| 1224 | } |
| 1225 | // Census overlay claims-on-gate-recover-merge-main (the branch merged |
| 1226 | // with main at 521c6ee9), loaded last: envelope only, internal/host |
| 1227 | // re-pinned over both branches' code. The seal stands. |
| 1228 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-claims-on-gate-recover-merge-main.tsv"), &inv, true); err != nil { |
| 1229 | t.Fatal(err) |
| 1230 | } |
| 1231 | // Census overlay claim-merge-main (claim-empty-cohort-done merged with |
| 1232 | // main after moves-offline, 7af8815), loaded last: envelope only, the |
| 1233 | // internal/host re-pin over both branches' code. The seal stands. |
| 1234 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-claim-merge-main.tsv"), &inv, true); err != nil { |
| 1235 | t.Fatal(err) |
| 1236 | } |
| 1237 | // Census overlay roster-binding (design-roster-revision-holds.md rev 3 |
| 1238 | // and section 7: installed state binds to hostactors.BoxBinding, not the |
| 1239 | // roster write counter), loaded last. Ruled by platform-d8 |
| 1240 | // (docs/superpowers/notes/census-proposal-claim-step8-readiness.md, |
| 1241 | // "Roster binding"), marked rev3=census-ruling-roster-binding (RULED by platform-d8, 2026-10-20, accepted with conditions, all met). Changed units |
| 1242 | // are re-pinned at their existing classes, new units take their callers' |
| 1243 | // classes, servingPriorRevision is removed, and claim-ack-refresh's |
| 1244 | // productionClaimRoster.Actor row is folded in. It adds no accepted-legacy |
| 1245 | // row, so the seal stands. |
| 1246 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-roster-binding.tsv"), &inv, true); err != nil { |
| 1247 | t.Fatal(err) |
| 1248 | } |
| 1249 | // Census overlay claim-merge-main-2 (claim-empty-cohort-done merged with |
| 1250 | // main at e0986eab), loaded last: envelope only, internal/host re-pinned |
| 1251 | // over both branches' code. The seal stands. |
| 1252 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-claim-merge-main-2.tsv"), &inv, true); err != nil { |
| 1253 | t.Fatal(err) |
| 1254 | } |
| 1255 | // Census overlay claim-router-cache (the migration census keeps the |
| 1256 | // agent's router update cache, harness G10 re-run 8.2), loaded last: |
| 1257 | // envelope only. Ruled by platform-d8, accepted as proposed; the seal |
| 1258 | // stands. |
| 1259 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-claim-router-cache.tsv"), &inv, true); err != nil { |
| 1260 | t.Fatal(err) |
| 1261 | } |
| 1262 | // Census overlay claim-fresh-install (harness G10, 2026-10-21: the |
| 1263 | // unit-shape refusal refused every fresh claim install, which has no |
| 1264 | // registry row until its own row write; fix round 2 after Astra on |
| 1265 | // 4dd4c436), loaded last. Only the installation's own work |
| 1266 | // (Reconciler.installWithWork's marked selection) may go on without a |
| 1267 | // row, checked as an empty route for the host; claimUnitShapePolicy's |
| 1268 | // reads move to claimUnitShapeCheck at the same class, and |
| 1269 | // installWithWork is re-pinned at its own. Marked |
| 1270 | // rev3=PROPOSED-claim-fresh-install; envelopes re-pinned; the seal stands. |
| 1271 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-claim-fresh-install.tsv"), &inv, true); err != nil { |
| 1272 | t.Fatal(err) |
| 1273 | } |
| 1274 | // Census overlay claim-merge-main-3 (claim-empty-cohort-done merged with |
| 1275 | // main at 6da1cd0f), loaded last: envelope only, internal/host and |
| 1276 | // internal/hostops re-pinned over both branches' code. The seal stands. |
| 1277 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-claim-merge-main-3.tsv"), &inv, true); err != nil { |
| 1278 | t.Fatal(err) |
| 1279 | } |
| 1280 | // Census overlay claim-merge-main-4 (claim-empty-cohort-done merged with |
| 1281 | // main at abcdd577, which carries claims-on-gate-recover), loaded last: |
| 1282 | // the three generation-census units both branches changed are re-pinned |
| 1283 | // at E2 over the merged code, carrying both bases; internal/host |
| 1284 | // re-pinned. The seal stands. |
| 1285 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-claim-merge-main-4.tsv"), &inv, true); err != nil { |
| 1286 | t.Fatal(err) |
| 1287 | } |
| 1288 | // Census overlay design-fleet-internal-engines (amadan #335 plan 1: the |
| 1289 | // test-only internal/sim/engines simulation package), loaded last, after |
| 1290 | // claim-merge-main-4. RULED as census-ruling-design-fleet-internal-engines |
| 1291 | // (Paul, as census owner, 2026-10-21), accepted as proposed |
| 1292 | // (docs/superpowers/notes/census-proposal-design-fleet-internal-engines.md). |
| 1293 | // No production code changes. Its mutation harness mutate.sh is |
| 1294 | // classified as internal/sim/hostlife/mutate.sh is: accepted legacy, a |
| 1295 | // test harness pinned by the conservative external envelope, run by a |
| 1296 | // developer or CI and never by root. The internal/sim/engines envelope is |
| 1297 | // pinned. One frozen declaration is added, so the seal moves; |
| 1298 | // securelisten-337's, which every later overlay left standing, is checked |
| 1299 | // first. |
| 1300 | if errs := checkRootReadLegacy(inv, seal); len(errs) != 0 { |
| 1301 | t.Fatal(errs) |
| 1302 | } |
| 1303 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-design-fleet-internal-engines.tsv"), &inv, true); err != nil { |
| 1304 | t.Fatal(err) |
| 1305 | } |
| 1306 | seal = "ca73eb766db03c4820076a28e84bbf15b61135a284294feaea78ae14b9b55071" |
| 1307 | // Census overlay email-signin (the console's email sign-in: an address |
| 1308 | // with no Keymail mailbox signs in by an emailed link and code, an email |
| 1309 | // session re-confirms by email, and the Apps-inbox confirmation link is |
| 1310 | // retired), loaded last, after design-fleet-internal-engines. Proposed; every row |
| 1311 | // carries rev3=PROPOSED-email-signin and the ruling is asked of the census |
| 1312 | // owner on #280 (docs/superpowers/notes/census-proposal-email-signin.md). |
| 1313 | // Every unit runs in the console (cmd/console serve under |
| 1314 | // console.service, user carlos-console), and the root carlos binary links |
| 1315 | // neither internal/console nor internal/mail |
| 1316 | // (TestRootBinaryLinksNoConsoleOrMail). Ten changed legacy units are |
| 1317 | // re-frozen at their main disposition, eighteen new ones are accepted |
| 1318 | // legacy at their siblings' class (console SQL on its own database, |
| 1319 | // crypto/rand for an in-memory key, request URL query reads, embedded |
| 1320 | // templates), and handleConfirm, TakePendingAction and CreateSession, |
| 1321 | // which read nothing now, are retired by H3 removals. Frozen declarations |
| 1322 | // are added, so the seal moves; design-fleet-internal-engines' is checked first. |
| 1323 | if errs := checkRootReadLegacy(inv, seal); len(errs) != 0 { |
| 1324 | t.Fatal(errs) |
| 1325 | } |
| 1326 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-email-signin.tsv"), &inv, true); err != nil { |
| 1327 | t.Fatal(err) |
| 1328 | } |
| 1329 | seal = "b25dfda5a68d2183af759b42398f79bb471749d46e68d267545647d1a2e6046b" |
| 1330 | // Census overlay plans-2-box-limits (boxes read account limits behind the |
| 1331 | // console's plans marker; the activator's idle hook), loaded last, after |
| 1332 | // plans-2-box-limits. PROPOSED-plans-2-box-limits, asked of the census owner on |
| 1333 | // #280. The new agent units read routes and two bucket objects and only |
| 1334 | // narrow idle windows. Frozen declarations are added, so the seal moves; |
| 1335 | // email-signin's is checked first. |
| 1336 | if errs := checkRootReadLegacy(inv, seal); len(errs) != 0 { |
| 1337 | t.Fatal(errs) |
| 1338 | } |
| 1339 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-plans-2-box-limits.tsv"), &inv, true); err != nil { |
| 1340 | t.Fatal(err) |
| 1341 | } |
| 1342 | seal = "d044224434b0b375599a076e573c7c4164e3777ae6295abf95d9d9b9ed69e482" |
| 1343 | if errs := checkRootReadLegacy(inv, seal); len(errs) != 0 { |
| 1344 | t.Fatal(errs) |
| 1345 | } |
| 1346 | // Census overlay console-brand-name (the console's chrome and welcome name |
| 1347 | // the deployment from CARLOS_CONSOLE_NAME), loaded last, after |
| 1348 | // plans-2-box-limits. Proposed; ruling asked of the census owner on #280. Three |
| 1349 | // templates change text only; envelopes re-pinned; no read is added. The |
| 1350 | // seal moves because the three templates' legacy declarations are |
| 1351 | // re-pinned. |
| 1352 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-console-brand-name.tsv"), &inv, true); err != nil { |
| 1353 | t.Fatal(err) |
| 1354 | } |
| 1355 | seal = "78e18e222a8178bbe16b638c5e3811b8f64c4c2664ffd6649eb4273e72fae24e" |
| 1356 | // Census overlay db-region-forward-1 (db region forward plan 1: the box |
| 1357 | // side and the wire, dark), loaded last, after console-brand-name. |
| 1358 | // census-ruling-db-region-forward-1: accepted in full by the census |
| 1359 | // owner on #280, 2026-10-22 |
| 1360 | // (docs/superpowers/notes/census-proposal-db-region-forward-1.md). |
| 1361 | // db.go runQueryContext is retired by an H3 removal; its body is |
| 1362 | // internal/dbquery RunContext, accepted legacy at the same J4 footing |
| 1363 | // with both callers named; the agent's db-reads units are accepted |
| 1364 | // legacy at J1 and J4, the worker's at J4, the probe is diagnostic, and |
| 1365 | // the two carlos-dbread units join as external units. Frozen |
| 1366 | // declarations are added, so the seal moves; console-brand-name's is |
| 1367 | // checked first. |
| 1368 | if errs := checkRootReadLegacy(inv, seal); len(errs) != 0 { |
| 1369 | t.Fatal(errs) |
| 1370 | } |
| 1371 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-db-region-forward-1.tsv"), &inv, true); err != nil { |
| 1372 | t.Fatal(err) |
| 1373 | } |
| 1374 | seal = "fc73854854f9e75d122ebd0eacf330d126201aa9d5c4295c634cc1c204a9168d" |
| 1375 | // Census overlay s3-change-index (amadan #336: the deployment stores bump |
| 1376 | // control/changes.json after each mutation; readers armed by |
| 1377 | // CARLOS_CHANGE_INDEX=read read records through a record view snapshot, |
| 1378 | // gate unchanged reads up to the backstop's age and run scoped passes), |
| 1379 | // loaded last, after db-region-forward-1. Proposed; rows carry |
| 1380 | // rev3=PROPOSED-s3-change-index and the ruling, |
| 1381 | // census-ruling-s3-change-index, is asked of the census owner on #280. |
| 1382 | // The units run in the agent (user carlos) or the console, or are store |
| 1383 | // methods shared as their siblings; root's carlos adopt reaches only |
| 1384 | // PassFilter's pointer gate, which it never arms. Four frozen units that |
| 1385 | // now read nothing (DirStore.Put, PutFrom and DeleteIfMatch, whose bodies |
| 1386 | // moved into raw methods, and telemetrySwitches) are retired by H3 |
| 1387 | // removals. Frozen declarations are added, so the seal moves; |
| 1388 | // db-region-forward-1's is checked first. |
| 1389 | if errs := checkRootReadLegacy(inv, seal); len(errs) != 0 { |
| 1390 | t.Fatal(errs) |
| 1391 | } |
| 1392 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-s3-change-index.tsv"), &inv, true); err != nil { |
| 1393 | t.Fatal(err) |
| 1394 | } |
| 1395 | seal = "e7c6062c0e1d29833edda7bd9953282a52edbb45ddbe3cd00a447ec5d61d9b8a" |
| 1396 | // Census overlay db-region-forward-2 (db region forward plan 2: the |
| 1397 | // console and CLI for carlos db query and query --at), loaded last, |
| 1398 | // after s3-change-index. census-ruling-db-region-forward-2: accepted in |
| 1399 | // full by the census owner on #280, 2026-10-22 |
| 1400 | // (docs/superpowers/notes/census-proposal-db-region-forward-2.md). The |
| 1401 | // CLI's read of a refused reads-route answer (dbforward.go |
| 1402 | // dbQueryForward) and the console's read of the request body to EOF |
| 1403 | // (readDBReadBody) are accepted legacy at J4 as main's units are; |
| 1404 | // cmd/console serve, NewServer, dbdoor doorError and the |
| 1405 | // deployment-buckets module are re-frozen at main's disposition with |
| 1406 | // this branch's change appended. Frozen declarations change, so the |
| 1407 | // seal moves; s3-change-index's is checked first. |
| 1408 | if errs := checkRootReadLegacy(inv, seal); len(errs) != 0 { |
| 1409 | t.Fatal(errs) |
| 1410 | } |
| 1411 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-db-region-forward-2.tsv"), &inv, true); err != nil { |
| 1412 | t.Fatal(err) |
| 1413 | } |
| 1414 | seal = "bd4cc1909225cd574c2c609f0cd02177076ddb5c1476d9cf5e178be05acccae1" |
| 1415 | // Census overlay plans-3a-creation-caps (the console refuses creates past |
| 1416 | // an account's plan limits), loaded last, after db-region-forward-2. |
| 1417 | // PROPOSED-plans-3a-creation-caps, asked of the census owner on #280. Two |
| 1418 | // new console store counts, handleAPICreateApp re-frozen, the console |
| 1419 | // envelope re-pinned. Frozen declarations are added, so the seal moves; |
| 1420 | // db-region-forward-2's is checked first. |
| 1421 | if errs := checkRootReadLegacy(inv, seal); len(errs) != 0 { |
| 1422 | t.Fatal(errs) |
| 1423 | } |
| 1424 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-plans-3a-creation-caps.tsv"), &inv, true); err != nil { |
| 1425 | t.Fatal(err) |
| 1426 | } |
| 1427 | seal = "dc7dbb4f562fe31556e7ed1e09fcfb380f551b45a444e94e72662f0b202c605b" |
| 1428 | // Census overlay db-region-forward-copy (Luna's approved copy for the |
| 1429 | // forwarded db read, 2026-10-23), loaded last, after |
| 1430 | // plans-3a-creation-caps. Envelope re-pins only (. and internal/console: |
| 1431 | // changed sentence constants); no read is added, removed or changed, so |
| 1432 | // the seal does not move. census-ruling-db-region-forward-copy, accepted |
| 1433 | // on #280, 2026-10-23 |
| 1434 | // (docs/superpowers/notes/census-proposal-db-region-forward-copy.md). |
| 1435 | if errs := checkRootReadLegacy(inv, seal); len(errs) != 0 { |
| 1436 | t.Fatal(errs) |
| 1437 | } |
| 1438 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-db-region-forward-copy.tsv"), &inv, true); err != nil { |
| 1439 | t.Fatal(err) |
| 1440 | } |
| 1441 | // Census overlay static-redirects (static sites honour a _redirects |
| 1442 | // file), loaded last, after db-region-forward-copy. Proposed; ruling |
| 1443 | // asked of the census owner on #280. PackDir and staticSite.serveTree are |
| 1444 | // re-frozen; staticSite.serveFile takes serveTree's Open, and |
| 1445 | // staticSite.loadRedirects reads the tree's _redirects once per version |
| 1446 | // swap. Every read stays inside the extracted, verified tree. Frozen |
| 1447 | // declarations are added, so the seal moves; plans-3a-creation-caps's |
| 1448 | // (unchanged by db-region-forward-copy) is checked first. |
| 1449 | if errs := checkRootReadLegacy(inv, seal); len(errs) != 0 { |
| 1450 | t.Fatal(errs) |
| 1451 | } |
| 1452 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-static-redirects.tsv"), &inv, true); err != nil { |
| 1453 | t.Fatal(err) |
| 1454 | } |
| 1455 | seal = "65857c29501dc7676f52921bea79d08217542ecb1d775c67d20a3460c851d831" |
| 1456 | // Census overlay db-region-forward-3 (db region forward plan 3: carlos |
| 1457 | // db pull and pull --at for an instance in another region), loaded last, |
| 1458 | // after static-redirects. census-ruling-db-region-forward-3, accepted in |
| 1459 | // full by the census owner on #280, 2026-10-23 |
| 1460 | // (docs/superpowers/notes/census-proposal-db-region-forward-3.md). The |
| 1461 | // worker's pull units (holdAndSend, verifyLTX) are accepted legacy at J4 |
| 1462 | // inside its sandbox, the CLI's (dbPullForward, fileSink.chunk, |
| 1463 | // openDBPull) at J4 as the person, RestoreLocal's context-checked reads |
| 1464 | // (ctxReadCloser.Read, round 1) at J4 as downloaded.Read is; |
| 1465 | // ServeWorker and dbQueryForward are re-frozen with the pull appended. Frozen declarations are added, so |
| 1466 | // the seal moves; static-redirects' is checked first. |
| 1467 | if errs := checkRootReadLegacy(inv, seal); len(errs) != 0 { |
| 1468 | t.Fatal(errs) |
| 1469 | } |
| 1470 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-db-region-forward-3.tsv"), &inv, true); err != nil { |
| 1471 | t.Fatal(err) |
| 1472 | } |
| 1473 | seal = "79cb56b4c7d3a8e155d1eff1902715d818fcf4c09b1d8a82921c413b116938a9" |
| 1474 | // Census overlay wake-latency-1 (wake latency pass 1), loaded last, |
| 1475 | // after db-region-forward-3. census-ruling-wake-latency-1, accepted as |
| 1476 | // proposed by the census owner on #280, 2026-10-24 |
| 1477 | // (docs/superpowers/notes/census-proposal-wake-latency-1.md). doWake, |
| 1478 | // wake and startUnitAt are re-frozen with per-step wake timing appended: |
| 1479 | // clock reads and journal lines, no read added. The internal/activator |
| 1480 | // and internal/instancectl envelopes are re-pinned. Frozen declarations |
| 1481 | // are added, so the seal moves; db-region-forward-3's is checked first. |
| 1482 | if errs := checkRootReadLegacy(inv, seal); len(errs) != 0 { |
| 1483 | t.Fatal(errs) |
| 1484 | } |
| 1485 | if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-wake-latency-1.tsv"), &inv, true); err != nil { |
| 1486 | t.Fatal(err) |
| 1487 | } |
| 1488 | seal = "09e20ea4bbc534710334e632e903878656dabfb5817d4d96c6e7c56508172148" |
| 1489 | return rootReadManifest(t, root, inv, seal, last) |
| 1490 | } |
| 1491 | |
| 1492 | func TestPlan14RootReadCensusRejectsDrift(t *testing.T) { |
| 1493 | entry := rootReadEntry{File: "internal/host/new.go", Unit: "rootPass", Kinds: "registry:Caps@10", Digest: strings.Repeat("a", 64)} |
| 1494 | base := rootReadInventory{Entries: map[string]classifiedRootRead{}, Coverage: map[string]string{}} |
| 1495 | if got := compareRootReads(rootReadScan{Entries: []rootReadEntry{entry}}, base); len(got) == 0 { |
| 1496 | t.Fatal("unclassified read accepted") |
| 1497 | } |
| 1498 | base.Entries[entry.File+":"+entry.Unit] = classifiedRootRead{rootReadEntry: entry, Class: "authority", Task: "E4", Basis: "root-route"} |
| 1499 | if got := compareRootReads(rootReadScan{Entries: []rootReadEntry{entry}}, base); len(got) != 0 { |
| 1500 | t.Fatal(got) |
| 1501 | } |
| 1502 | for _, what := range []string{"body", "site", "removed", "source"} { |
| 1503 | t.Run(what, func(t *testing.T) { |
| 1504 | changed := entry |
| 1505 | scan := rootReadScan{Entries: []rootReadEntry{changed}} |
| 1506 | switch what { |
| 1507 | case "body": |
| 1508 | scan.Entries[0].Digest = strings.Repeat("b", 64) |
| 1509 | case "site": |
| 1510 | scan.Entries[0].Kinds += ";registry:Unit@25" |
| 1511 | case "removed": |
| 1512 | scan.Entries = nil |
| 1513 | case "source": |
| 1514 | scan.Coverage = map[string]string{"internal/host": "new primitive"} |
| 1515 | } |
| 1516 | if got := compareRootReads(scan, base); len(got) == 0 { |
| 1517 | t.Fatal("changed read accepted") |
| 1518 | } |
| 1519 | }) |
| 1520 | } |
| 1521 | } |
| 1522 | |
| 1523 | // Census ruling zddr3: H3 owns a removal only with a census-ruling basis, |
| 1524 | // and never owns an authority row. |
| 1525 | func TestPlan14RemovedH3NeedsCensusRuling(t *testing.T) { |
| 1526 | for name, tc := range map[string]struct { |
| 1527 | row string |
| 1528 | load bool |
| 1529 | }{ |
| 1530 | "ruling": {"@removed\tinternal/cgroup/cgroup.go\tManager.startIn\tH3\tcensus-ruling-zddr3: placement moved into the shim.", true}, |
| 1531 | "no ruling": {"@removed\tinternal/cgroup/cgroup.go\tManager.startIn\tH3\tplacement moved into the shim.", false}, |
| 1532 | "inner": {"@removed\tinternal/cgroup/cgroup.go\tManager.startIn\tH3\tsee census-ruling-zddr3", false}, |
| 1533 | "authority": {"internal/cgroup/cgroup.go\tManager.startIn\tauthority\tH3\tcensus-ruling-zddr3\tfile:Open@1\t" + strings.Repeat("a", 64), false}, |
| 1534 | "other owner": {"@removed\tinternal/cgroup/cgroup.go\tManager.startIn\tH4\tcensus-ruling-zddr3: placement moved into the shim.", false}, |
| 1535 | } { |
| 1536 | t.Run(name, func(t *testing.T) { |
| 1537 | path := filepath.Join(t.TempDir(), "overlay.tsv") |
| 1538 | if err := os.WriteFile(path, []byte(tc.row+"\n"), 0o644); err != nil { |
| 1539 | t.Fatal(err) |
| 1540 | } |
| 1541 | inv := rootReadInventory{Entries: map[string]classifiedRootRead{}, Coverage: map[string]string{}} |
| 1542 | err := loadRootReadInventory(path, &inv, true) |
| 1543 | if tc.load && err != nil { |
| 1544 | t.Fatal(err) |
| 1545 | } |
| 1546 | if !tc.load && err == nil { |
| 1547 | t.Fatal("row loaded without a census-ruling basis or kept owner") |
| 1548 | } |
| 1549 | }) |
| 1550 | } |
| 1551 | } |
| 1552 | |
| 1553 | // Census ruling zddr3: an H3 census-ruling removal retires a still-declared |
| 1554 | // unit only while the scan finds no read in it. Every other removal of a |
| 1555 | // still-declared unit fails as before, and a retired unit that reads again |
| 1556 | // is an unclassified root read that needs a fresh classification. |
| 1557 | func TestPlan14RetiredDeclarationNeedsNoReads(t *testing.T) { |
| 1558 | e := classifiedRootRead{rootReadEntry: rootReadEntry{File: "internal/cgroup/cgroup.go", Unit: "Manager.startIn", Kinds: "file:Open@40", Digest: strings.Repeat("a", 64)}, Class: "legacy", Task: "accepted-legacy", Basis: "accepted-legacy;rev3=J1;basis=placement"} |
| 1559 | key := e.File + ":" + e.Unit |
| 1560 | body := strings.Repeat("b", 64) |
| 1561 | inv := rootReadInventory{Entries: map[string]classifiedRootRead{}, Coverage: map[string]string{}, Legacy: map[string]bool{rootReadLegacyFingerprint(e): true}, Frozen: map[string]classifiedRootRead{key: e}, Removed: map[string]string{key: "H3;census-ruling-zddr3: no longer reads"}} |
| 1562 | present := []string{"removed legacy declaration still present " + key} |
| 1563 | |
| 1564 | // Still declared, no reads: retired. |
| 1565 | quiet := rootReadScan{Declarations: map[string]string{key: body}} |
| 1566 | if errs := checkRootReadDeclarations(quiet, inv); len(errs) != 0 { |
| 1567 | t.Fatal(errs) |
| 1568 | } |
| 1569 | if errs := compareRootReads(quiet, inv); len(errs) != 0 { |
| 1570 | t.Fatal(errs) |
| 1571 | } |
| 1572 | |
| 1573 | // (a) Still declared and still reading: the census-ruling basis does not help. |
| 1574 | reading := rootReadScan{Entries: []rootReadEntry{{File: e.File, Unit: e.Unit, Kinds: "file:Open@40", Digest: body}}, Declarations: map[string]string{key: body}} |
| 1575 | if errs := checkRootReadDeclarations(reading, inv); !reflect.DeepEqual(errs, present) { |
| 1576 | t.Fatalf("retired unit with reads: %v", errs) |
| 1577 | } |
| 1578 | // Re-reading: the retired entry gives no silent pass. |
| 1579 | if errs := compareRootReads(reading, inv); !reflect.DeepEqual(errs, []string{"unclassified root read " + key + " file:Open@40"}) { |
| 1580 | t.Fatalf("retired unit reading again: %v", errs) |
| 1581 | } |
| 1582 | |
| 1583 | // (b) Any removal that is not H3 with a census-ruling basis fails as before. |
| 1584 | for _, removal := range []string{"C; removed obsolete delegate after reviewed root authority conversion", "E4;census-ruling-zddr3: no longer reads", "H3;no longer reads", "H3; census-ruling-zddr3: no longer reads"} { |
| 1585 | inv.Removed[key] = removal |
| 1586 | if errs := checkRootReadDeclarations(quiet, inv); !reflect.DeepEqual(errs, present) { |
| 1587 | t.Fatalf("%q retired a still-declared unit: %v", removal, errs) |
| 1588 | } |
| 1589 | } |
| 1590 | |
| 1591 | // Still classified: fails even with no reads. |
| 1592 | inv.Removed[key] = "H3;census-ruling-zddr3: no longer reads" |
| 1593 | inv.Entries[key] = e |
| 1594 | if errs := checkRootReadDeclarations(quiet, inv); !reflect.DeepEqual(errs, present) { |
| 1595 | t.Fatalf("classified unit retired: %v", errs) |
| 1596 | } |
| 1597 | } |
| 1598 | |
| 1599 | func TestPlan14LegacyFreezeRequiresReclassification(t *testing.T) { |
| 1600 | e := classifiedRootRead{rootReadEntry: rootReadEntry{File: "internal/host/old.go", Unit: "oldRootRead", Kinds: "registry:Unit@10", Digest: strings.Repeat("a", 64)}, Class: "legacy", Task: "accepted-legacy", Basis: "accepted-legacy;rev3=J1;basis=root-route"} |
| 1601 | key := e.File + ":" + e.Unit |
| 1602 | inv := rootReadInventory{Entries: map[string]classifiedRootRead{key: e}, Legacy: map[string]bool{rootReadLegacyFingerprint(e): true}} |
| 1603 | seal := rootReadLegacySeal(inv) |
| 1604 | if errs := checkRootReadLegacy(inv, seal); len(errs) != 0 { |
| 1605 | t.Fatal(errs) |
| 1606 | } |
| 1607 | for _, change := range []string{"body", "site", "basis", "task", "new"} { |
| 1608 | t.Run(change, func(t *testing.T) { |
| 1609 | changed := e |
| 1610 | switch change { |
| 1611 | case "body": |
| 1612 | changed.Digest = strings.Repeat("b", 64) |
| 1613 | case "site": |
| 1614 | changed.Kinds = "registry:Caps@10" |
| 1615 | case "basis": |
| 1616 | changed.Basis += ";excuse" |
| 1617 | case "task": |
| 1618 | changed.Task = "J4" |
| 1619 | case "new": |
| 1620 | changed.File = "internal/host/new.go" |
| 1621 | } |
| 1622 | inv.Entries[key] = changed |
| 1623 | // Even a fully refreshed source inventory must fail while legacy. |
| 1624 | if errs := compareRootReads(rootReadScan{Entries: []rootReadEntry{changed.rootReadEntry}}, rootReadInventory{Entries: map[string]classifiedRootRead{changed.File + ":" + changed.Unit: changed}}); len(errs) != 0 { |
| 1625 | t.Fatal(errs) |
| 1626 | } |
| 1627 | if errs := checkRootReadLegacy(inv, seal); len(errs) == 0 { |
| 1628 | t.Fatal("changed legacy accepted") |
| 1629 | } |
| 1630 | inv.Legacy[rootReadLegacyFingerprint(changed)] = true |
| 1631 | if errs := checkRootReadLegacy(inv, seal); len(errs) == 0 { |
| 1632 | t.Fatal("refreshed freeze accepted") |
| 1633 | } |
| 1634 | delete(inv.Legacy, rootReadLegacyFingerprint(changed)) |
| 1635 | for _, class := range []string{"authority", "hint", "diagnostic"} { |
| 1636 | promoted := changed |
| 1637 | promoted.Class = class |
| 1638 | promoted.Task = "E4" |
| 1639 | promoted.Basis = "reviewed independent grant binding" |
| 1640 | inv.Entries[key] = promoted |
| 1641 | if errs := checkRootReadLegacy(inv, seal); len(errs) != 0 { |
| 1642 | t.Fatal(errs) |
| 1643 | } |
| 1644 | } |
| 1645 | inv.Entries[key] = e |
| 1646 | }) |
| 1647 | } |
| 1648 | } |
| 1649 | |
| 1650 | // Frozen identities survive removal of every recognized input primitive. |
| 1651 | func checkRootReadDeclarations(scan rootReadScan, inv rootReadInventory) []string { |
| 1652 | var errs []string |
| 1653 | for key, frozen := range inv.Frozen { |
| 1654 | digest, exists := scan.Declarations[key] |
| 1655 | current, classified := inv.Entries[key] |
| 1656 | removal := inv.Removed[key] |
| 1657 | if removal != "" { |
| 1658 | // Census ruling zddr3: an H3 census-ruling removal may retire a |
| 1659 | // unit that is still declared only when this scan finds no read in |
| 1660 | // it. The basis text is never evidence; the scan is. |
| 1661 | retired := exists && strings.HasPrefix(removal, "H3;census-ruling-") && !rootReadScanned(scan, key) |
| 1662 | if (exists && !retired) || classified { |
| 1663 | errs = append(errs, "removed legacy declaration still present "+key) |
| 1664 | } |
| 1665 | continue |
| 1666 | } |
| 1667 | if !exists || !classified { |
| 1668 | errs = append(errs, "legacy declaration needs explicit reclassification or removal "+key) |
| 1669 | continue |
| 1670 | } |
| 1671 | if current.Class == "legacy" && (digest != frozen.Digest || rootReadLegacyFingerprint(current) != rootReadLegacyFingerprint(frozen)) { |
| 1672 | errs = append(errs, "changed legacy declaration "+key) |
| 1673 | } |
| 1674 | if current.Class != "legacy" && current.Digest != digest { |
| 1675 | errs = append(errs, "changed reclassified declaration "+key) |
| 1676 | } |
| 1677 | } |
| 1678 | for key, removal := range inv.Removed { |
| 1679 | if strings.HasSuffix(removal, ";census-ruling-J4") || strings.HasSuffix(removal, ";census-ruling-activation-tools-removal") { |
| 1680 | if _, exists := scan.Declarations[key]; exists || inv.Entries[key].Class != "" { |
| 1681 | errs = append(errs, "removed declaration still present "+key) |
| 1682 | } |
| 1683 | continue |
| 1684 | } |
| 1685 | if _, ok := inv.Frozen[key]; !ok { |
| 1686 | errs = append(errs, "removal of unfrozen declaration "+key) |
| 1687 | } |
| 1688 | } |
| 1689 | sort.Strings(errs) |
| 1690 | return errs |
| 1691 | } |
| 1692 | |
| 1693 | // rootReadScanned reports whether the scan has a read entry for the unit. |
| 1694 | func rootReadScanned(scan rootReadScan, key string) bool { |
| 1695 | for _, e := range scan.Entries { |
| 1696 | if e.File+":"+e.Unit == key { |
| 1697 | return true |
| 1698 | } |
| 1699 | } |
| 1700 | return false |
| 1701 | } |
| 1702 | |
| 1703 | func TestPlan14J4RemovalRequiresAbsentDeclaration(t *testing.T) { |
| 1704 | key := "root.go:dead" |
| 1705 | scan := rootReadScan{Declarations: map[string]string{}} |
| 1706 | inv := rootReadInventory{Entries: map[string]classifiedRootRead{}, Removed: map[string]string{key: "E2;census-ruling-J4"}} |
| 1707 | if errs := checkRootReadDeclarations(scan, inv); len(errs) != 0 { |
| 1708 | t.Fatal(errs) |
| 1709 | } |
| 1710 | scan.Declarations[key] = "digest" |
| 1711 | if len(checkRootReadDeclarations(scan, inv)) == 0 { |
| 1712 | t.Fatal("live removed declaration accepted") |
| 1713 | } |
| 1714 | delete(scan.Declarations, key) |
| 1715 | inv.Entries[key] = classifiedRootRead{Class: "authority"} |
| 1716 | if len(checkRootReadDeclarations(scan, inv)) == 0 { |
| 1717 | t.Fatal("retained removed row accepted") |
| 1718 | } |
| 1719 | delete(inv.Entries, key) |
| 1720 | inv.Removed[key] = "E2;unruled" |
| 1721 | if len(checkRootReadDeclarations(scan, inv)) == 0 { |
| 1722 | t.Fatal("unruled removal accepted") |
| 1723 | } |
| 1724 | } |
| 1725 | |
| 1726 | func TestPlan14ActivationToolsRemovalRequiresAbsentDeclaration(t *testing.T) { |
| 1727 | key := "root.go:dead" |
| 1728 | scan := rootReadScan{Declarations: map[string]string{}} |
| 1729 | inv := rootReadInventory{Entries: map[string]classifiedRootRead{}, Removed: map[string]string{key: "G,J4;census-ruling-activation-tools-removal"}} |
| 1730 | if errs := checkRootReadDeclarations(scan, inv); len(errs) != 0 { |
| 1731 | t.Fatal(errs) |
| 1732 | } |
| 1733 | scan.Declarations[key] = "digest" |
| 1734 | if len(checkRootReadDeclarations(scan, inv)) == 0 { |
| 1735 | t.Fatal("live removed declaration accepted") |
| 1736 | } |
| 1737 | delete(scan.Declarations, key) |
| 1738 | inv.Entries[key] = classifiedRootRead{Class: "authority"} |
| 1739 | if len(checkRootReadDeclarations(scan, inv)) == 0 { |
| 1740 | t.Fatal("retained removed row accepted") |
| 1741 | } |
| 1742 | delete(inv.Entries, key) |
| 1743 | inv.Removed[key] = "E2;unruled" |
| 1744 | if len(checkRootReadDeclarations(scan, inv)) == 0 { |
| 1745 | t.Fatal("unruled removal accepted") |
| 1746 | } |
| 1747 | } |
| 1748 | |
| 1749 | // rootReadChainManifest lists the overlays loaded after the chain written |
| 1750 | // out in rootReadChain, in order, one per line: the overlay's file name |
| 1751 | // under testdata and the seal it leaves. census-propose appends to it |
| 1752 | // (rootReadPropose), so a new overlay needs no edit to this file. |
| 1753 | const rootReadChainManifest = "internal/provision/testdata/plan14-root-reads-chain.tsv" |
| 1754 | |
| 1755 | type rootReadChainLink struct{ File, Seal string } |
| 1756 | |
| 1757 | func readRootReadChainManifest(path string) ([]rootReadChainLink, error) { |
| 1758 | raw, err := os.ReadFile(path) |
| 1759 | if err != nil { |
| 1760 | return nil, err |
| 1761 | } |
| 1762 | var links []rootReadChainLink |
| 1763 | for i, line := range strings.Split(strings.TrimSpace(string(raw)), "\n") { |
| 1764 | if line == "" || strings.HasPrefix(line, "#") { |
| 1765 | continue |
| 1766 | } |
| 1767 | col := strings.Split(line, "\t") |
| 1768 | if len(col) != 2 || len(col[1]) != 64 || strings.ContainsAny(col[0], "/\\") { |
| 1769 | return nil, fmt.Errorf("%s:%d: want <overlay file name>\\t<seal>", path, i+1) |
| 1770 | } |
| 1771 | if _, err := hex.DecodeString(col[1]); err != nil { |
| 1772 | return nil, fmt.Errorf("%s:%d: seal: %v", path, i+1, err) |
| 1773 | } |
| 1774 | links = append(links, rootReadChainLink{col[0], col[1]}) |
| 1775 | } |
| 1776 | return links, nil |
| 1777 | } |
| 1778 | |
| 1779 | // rootReadManifest loads the manifest's overlays onto inv, each after the |
| 1780 | // seal before it is checked, exactly as rootReadChain loads its own. A |
| 1781 | // non-empty last leaves that overlay out, and only when it is the last. |
| 1782 | func rootReadManifest(t testing.TB, root string, inv rootReadInventory, seal, last string) (rootReadInventory, string) { |
| 1783 | t.Helper() |
| 1784 | links, err := readRootReadChainManifest(filepath.Join(root, rootReadChainManifest)) |
| 1785 | if err != nil { |
| 1786 | t.Fatal(err) |
| 1787 | } |
| 1788 | for i, l := range links { |
| 1789 | if last != "" && l.File == last { |
| 1790 | if i != len(links)-1 { |
| 1791 | t.Fatalf("%s is not the last overlay in %s: propose under a new name", last, rootReadChainManifest) |
| 1792 | } |
| 1793 | break |
| 1794 | } |
| 1795 | if errs := checkRootReadLegacy(inv, seal); len(errs) != 0 { |
| 1796 | t.Fatalf("before overlay %s: %v", l.File, errs) |
| 1797 | } |
| 1798 | if err := loadRootReadInventory(filepath.Join(root, "internal/provision/testdata", l.File), &inv, true); err != nil { |
| 1799 | t.Fatal(err) |
| 1800 | } |
| 1801 | seal = l.Seal |
| 1802 | } |
| 1803 | return inv, seal |
| 1804 | } |
| 1805 | |