| 1 | package provision |
| 2 | |
| 3 | import ( |
| 4 | "fmt" |
| 5 | "go/ast" |
| 6 | "go/parser" |
| 7 | "go/token" |
| 8 | "go/types" |
| 9 | "io/fs" |
| 10 | "os" |
| 11 | "path/filepath" |
| 12 | "sort" |
| 13 | "strings" |
| 14 | "sync" |
| 15 | "testing" |
| 16 | |
| 17 | "golang.org/x/tools/go/packages" |
| 18 | ) |
| 19 | |
| 20 | type inputFamily struct { |
| 21 | Owner string |
| 22 | Hostile, Rename, Erasure, Retry, Positive string |
| 23 | } |
| 24 | |
| 25 | // Mixed declarations retain every protected effect: their family is the sorted |
| 26 | // product of these named A-G families, never the first matching owner alone. |
| 27 | // Empty witness slots are explicit acceptance gaps and fail coverage. |
| 28 | var inputFamilies = map[string]inputFamily{ |
| 29 | "A": {"A", "TestPlan14HostileOwnership", "TestPlan14RenameKeepsDescriptor", "TestPlan14RemoveCapability", "TestPlan14DescendRetriesEveryAncestorSync", "TestPlan14AuthorityFilePrivateAncestors"}, |
| 30 | "B": {"B", "TestPlan14ForgedHealthyCannotAdmit", "TestPlan14ForgedHealthyCannotAdmit", "TestPlan14HealthyErasureRequiresProof", "TestPlan14HealthyDuplicateRetryDurability", "TestPlan14ForgedHealthyCannotAdmit"}, |
| 31 | "B2": {"B", "TestPlan14ForgedHealthyCannotBind", "TestPlan14ForgedHealthyCannotBind", "TestPlan14HealthyErasureRequiresProof", "TestPlan14HealthyDuplicateRetryDurability", "TestPlan14ForgedHealthyCannotAdmit"}, |
| 32 | "B3": {"B", "TestPlan14ProofBindingProvenance", "TestPlan14ProofBindingProvenance", "TestPlan14ProofBindingProvenance", "TestPlan14HealthyDuplicateRetryDurability", "TestPlan14AdoptionCanonicalReadiness"}, |
| 33 | "B4": {"B", "TestPlan14ForgedHealthyCannotAdmit", "TestPlan14ForgedHealthyCannotAdmit", "TestPlan14HealthyErasureRequiresProof", "TestPlan14HealthyDuplicateRetryDurability", "TestPlan14HealthyDuplicateRetryDurability"}, |
| 34 | "C": {"C", "TestPlan14RootFences", "TestPlan14RootFences", "TestPlan14RootFences", "TestPlan14InventoryProcessCrash", "TestPlan14HeldRevalidatesCanonicalBindingUnderGuard"}, |
| 35 | "D": {"D", "TestPlan14ExecutionHostileCensusRootRunner", "TestPlan14ExecutionServingRequiresRootSettlement", "TestPlan14ExecutionUpdateExclusion", "TestPlan14ExecutionRecoveryAfterProcessCrash", "TestPlan14ExecutionProductionSettlement"}, |
| 36 | "E1": {"E", "TestPlan14RootHistoryProductionPass", "TestPlan14RootHistoryLegacyTampering", "TestPlan14RootHistoryLegacyTampering", "TestPlan14HistoryRetirementProcessCrash", "TestPlan14RootHistoryProductionPass"}, |
| 37 | "E2": {"E", "TestPlan14GenerationPrivateJournalRootRunner", "TestPlan14GenerationPayloadPhysicalRootRunner", "TestPlan14GenerationDeletionCannotUnpin", "TestPlan14GenerationCompletenessDurability", "TestPlan14GenerationInstalledUnitRootRunner"}, |
| 38 | "E3": {"E", "TestPlan14SwapFailureHostileRootRunner", "TestPlan14SwapFailureSurvivesLegacyTampering", "TestPlan14SwapFailureSurvivesLegacyTampering", "TestPlan14SwapFailureHostileRootRunner", "TestPlan14SwapFailureVerifiedRetry"}, |
| 39 | "F": {"F", "TestPlan14RootFloorCrashOrderingRootRunner", "TestPlan14FloorRiseWithoutPlatformUpdateRootRunner", "TestPlan14RootFloorPublicDenySurvivesPrivateLoss", "TestPlan14RootFloorSyscallCrashOrdering", "TestPlan14RootFloorBootSelectors"}, |
| 40 | "G": {"G", "TestPlan14RootMigrationHistoricEvidence", "TestPlan14RootMigrationHistoricRootRunner", "TestPlan14RootMigrationHistoricRootRunner", "TestPlan14RootMigrationCrashResume", "TestPlan14RootMigrationBootstrapRootRunner"}, |
| 41 | } |
| 42 | |
| 43 | func inputFamilyFor(e classifiedRootRead) ([]string, error) { |
| 44 | var names []string |
| 45 | seen := map[string]bool{} |
| 46 | for _, owner := range strings.Split(e.Task, ",") { |
| 47 | if owner == "J1" || owner == "J2" || owner == "J3" || owner == "J4" || owner == "E4" { |
| 48 | continue |
| 49 | } |
| 50 | family, ok := inputFamilies[owner] |
| 51 | if !ok { |
| 52 | return nil, fmt.Errorf("unknown family %q", owner) |
| 53 | } |
| 54 | if len(family.Owner) != 1 || family.Owner[0] < 'A' || family.Owner[0] > 'G' { |
| 55 | return nil, fmt.Errorf("family %s has no A-G owner", owner) |
| 56 | } |
| 57 | if !seen[owner] { |
| 58 | names = append(names, owner) |
| 59 | seen[owner] = true |
| 60 | } |
| 61 | } |
| 62 | if len(names) == 0 { |
| 63 | return nil, fmt.Errorf("no A-G family") |
| 64 | } |
| 65 | sort.Strings(names) |
| 66 | return names, nil |
| 67 | } |
| 68 | |
| 69 | func inputInventory(t *testing.T) rootReadInventory { |
| 70 | t.Helper() |
| 71 | inv := rootReadInventory{Entries: map[string]classifiedRootRead{}, Coverage: map[string]string{}} |
| 72 | // Use the very same ordered overlay list as the legacy-seal test. |
| 73 | f, err := parser.ParseFile(token.NewFileSet(), "plan14_p3_census_test.go", nil, 0) |
| 74 | if err != nil { |
| 75 | t.Fatal(err) |
| 76 | } |
| 77 | var paths []string |
| 78 | ast.Inspect(f, func(n ast.Node) bool { |
| 79 | if s, ok := n.(*ast.BasicLit); ok && s.Kind == token.STRING { |
| 80 | name := strings.Trim(s.Value, "\"") |
| 81 | if strings.HasPrefix(name, "plan14-root-reads") && strings.HasSuffix(name, ".tsv") && !strings.Contains(name, "/") { |
| 82 | paths = append(paths, name) |
| 83 | } |
| 84 | } |
| 85 | return true |
| 86 | }) |
| 87 | seen := map[string]bool{} |
| 88 | for _, name := range paths { |
| 89 | if seen[name] { |
| 90 | continue |
| 91 | } |
| 92 | seen[name] = true |
| 93 | if err := loadRootReadInventory(filepath.Join("testdata", name), &inv, len(seen) > 1); err != nil { |
| 94 | t.Fatal(err) |
| 95 | } |
| 96 | } |
| 97 | if len(seen) < 21 { |
| 98 | t.Fatal("missing production overlays") |
| 99 | } |
| 100 | return inv |
| 101 | } |
| 102 | |
| 103 | func inputTests(root string) (map[string]bool, error) { |
| 104 | out := map[string]bool{} |
| 105 | err := filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error { |
| 106 | if err != nil { |
| 107 | return err |
| 108 | } |
| 109 | if d.IsDir() && path != root && (strings.HasPrefix(d.Name(), ".") || d.Name() == "testdata") { |
| 110 | return filepath.SkipDir |
| 111 | } |
| 112 | if d.IsDir() || !strings.HasSuffix(path, "_test.go") { |
| 113 | return nil |
| 114 | } |
| 115 | f, err := parser.ParseFile(token.NewFileSet(), path, nil, 0) |
| 116 | if err != nil { |
| 117 | return err |
| 118 | } |
| 119 | for _, d := range f.Decls { |
| 120 | if fn, ok := d.(*ast.FuncDecl); ok && fn.Recv == nil && strings.HasPrefix(fn.Name.Name, "Test") && fn.Type.Params.NumFields() == 1 { |
| 121 | out[fn.Name.Name] = true |
| 122 | } |
| 123 | } |
| 124 | return nil |
| 125 | }) |
| 126 | return out, err |
| 127 | } |
| 128 | |
| 129 | // This is a conservative, typed production call/reference graph. Function values |
| 130 | // count as possible calls; interface dispatch includes only implementing methods. |
| 131 | // Type/var declarations use references (not imaginary calls). Tests never enter |
| 132 | // the graph. Every accepted row records its reachable immediate predecessor. |
| 133 | func inputCallers(root string) (map[string]string, error) { |
| 134 | // Each platform's load type-checks the whole module (most of this |
| 135 | // test's time), and the three are independent, so they run side by |
| 136 | // side. The merge below still takes them in this fixed order, so a key |
| 137 | // reached on several platforms records the same caller as before. |
| 138 | platforms := []string{"linux", "darwin", "windows"} |
| 139 | loaded := make([]map[string]string, len(platforms)) |
| 140 | errs := make([]error, len(platforms)) |
| 141 | var wg sync.WaitGroup |
| 142 | for i, platform := range platforms { |
| 143 | wg.Go(func() { loaded[i], errs[i] = inputPlatformCallers(root, platform) }) |
| 144 | } |
| 145 | wg.Wait() |
| 146 | out := map[string]string{} |
| 147 | for i, platform := range platforms { |
| 148 | callers, err := loaded[i], errs[i] |
| 149 | if err != nil { |
| 150 | return nil, err |
| 151 | } |
| 152 | for key, caller := range callers { |
| 153 | if out[key] == "" { |
| 154 | out[key] = platform + ":" + caller |
| 155 | } |
| 156 | } |
| 157 | } |
| 158 | return out, nil |
| 159 | } |
| 160 | func inputPlatformCallers(root, platform string) (map[string]string, error) { |
| 161 | cfg := &packages.Config{Dir: root, Mode: packages.NeedName | packages.NeedFiles | packages.NeedCompiledGoFiles | packages.NeedSyntax | packages.NeedTypes | packages.NeedTypesInfo | packages.NeedImports, Env: append(os.Environ(), "GOOS="+platform, "GOARCH=amd64", "CGO_ENABLED=0")} |
| 162 | pkgs, err := packages.Load(cfg, "./...") |
| 163 | if err != nil { |
| 164 | return nil, err |
| 165 | } |
| 166 | if packages.PrintErrors(pkgs) > 0 { |
| 167 | return nil, fmt.Errorf("production package loading failed") |
| 168 | } |
| 169 | type decl struct { |
| 170 | key string |
| 171 | node ast.Node |
| 172 | pkg *packages.Package |
| 173 | } |
| 174 | var decls []decl |
| 175 | objects := map[types.Object]string{} |
| 176 | methods := map[string][]*types.Func{} |
| 177 | graph := map[string]map[string]bool{} |
| 178 | add := func(a, b string) { |
| 179 | if a == "" || b == "" || a == b { |
| 180 | return |
| 181 | } |
| 182 | if graph[a] == nil { |
| 183 | graph[a] = map[string]bool{} |
| 184 | } |
| 185 | graph[a][b] = true |
| 186 | } |
| 187 | for _, p := range pkgs { |
| 188 | for _, f := range p.Syntax { |
| 189 | file, _ := filepath.Rel(root, p.Fset.Position(f.Pos()).Filename) |
| 190 | file = filepath.ToSlash(file) |
| 191 | grouped := map[token.Token]int{} |
| 192 | for _, d := range f.Decls { |
| 193 | name := "" |
| 194 | var defs []types.Object |
| 195 | switch d := d.(type) { |
| 196 | case *ast.FuncDecl: |
| 197 | name = d.Name.Name |
| 198 | if obj, ok := p.TypesInfo.Defs[d.Name].(*types.Func); ok { |
| 199 | defs = append(defs, obj) |
| 200 | sig := obj.Type().(*types.Signature) |
| 201 | if sig.Recv() != nil { |
| 202 | r := sig.Recv().Type() |
| 203 | if ptr, ok := r.(*types.Pointer); ok { |
| 204 | r = ptr.Elem() |
| 205 | } |
| 206 | if n, ok := r.(*types.Named); ok { |
| 207 | name = n.Obj().Name() + "." + name |
| 208 | } |
| 209 | methods[obj.Name()] = append(methods[obj.Name()], obj) |
| 210 | } |
| 211 | } |
| 212 | case *ast.GenDecl: |
| 213 | if d.Tok == token.IMPORT { |
| 214 | continue |
| 215 | } |
| 216 | if d.Lparen.IsValid() { |
| 217 | grouped[d.Tok]++ |
| 218 | name = fmt.Sprintf("%s#%d", d.Tok, grouped[d.Tok]) |
| 219 | } else { |
| 220 | switch s := d.Specs[0].(type) { |
| 221 | case *ast.TypeSpec: |
| 222 | name = "type " + s.Name.Name |
| 223 | case *ast.ValueSpec: |
| 224 | name = d.Tok.String() + " " + s.Names[0].Name |
| 225 | } |
| 226 | } |
| 227 | ast.Inspect(d, func(n ast.Node) bool { |
| 228 | if id, ok := n.(*ast.Ident); ok { |
| 229 | if obj := p.TypesInfo.Defs[id]; obj != nil { |
| 230 | defs = append(defs, obj) |
| 231 | } |
| 232 | } |
| 233 | return true |
| 234 | }) |
| 235 | } |
| 236 | if name == "" { |
| 237 | continue |
| 238 | } |
| 239 | key := file + ":" + name |
| 240 | decls = append(decls, decl{key, d, p}) |
| 241 | for _, obj := range defs { |
| 242 | objects[obj] = key |
| 243 | } |
| 244 | } |
| 245 | } |
| 246 | } |
| 247 | for _, d := range decls { |
| 248 | ast.Inspect(d.node, func(n ast.Node) bool { |
| 249 | if call, ok := n.(*ast.CallExpr); ok && len(call.Args) == 2 { |
| 250 | if sel, ok := call.Fun.(*ast.SelectorExpr); ok { |
| 251 | obj := d.pkg.TypesInfo.Uses[sel.Sel] |
| 252 | if obj != nil && obj.Pkg() != nil && obj.Pkg().Path() == "encoding/json" && obj.Name() == "Unmarshal" { |
| 253 | for _, method := range inputJSONMethods(d.pkg.TypesInfo.TypeOf(call.Args[1]), map[types.Type]bool{}) { |
| 254 | add(d.key, objects[method]) |
| 255 | } |
| 256 | } |
| 257 | } |
| 258 | } |
| 259 | if id, ok := n.(*ast.Ident); ok { |
| 260 | add(d.key, objects[d.pkg.TypesInfo.Uses[id]]) |
| 261 | } |
| 262 | if s, ok := n.(*ast.SelectorExpr); ok { |
| 263 | if sel := d.pkg.TypesInfo.Selections[s]; sel != nil { |
| 264 | if iface, ok := sel.Recv().Underlying().(*types.Interface); ok { |
| 265 | if method, ok := sel.Obj().(*types.Func); ok { |
| 266 | for _, candidate := range methods[method.Name()] { |
| 267 | sig := candidate.Type().(*types.Signature) |
| 268 | if types.Implements(sig.Recv().Type(), iface) || types.Implements(types.NewPointer(sig.Recv().Type()), iface) { |
| 269 | add(d.key, objects[candidate]) |
| 270 | } |
| 271 | } |
| 272 | } |
| 273 | } |
| 274 | } |
| 275 | } |
| 276 | return true |
| 277 | }) |
| 278 | // The embedded unit is an input to the reachable installer declaration. |
| 279 | if gd, ok := d.node.(*ast.GenDecl); ok && gd.Doc != nil { |
| 280 | for _, c := range gd.Doc.List { |
| 281 | if strings.HasPrefix(c.Text, "//go:embed ") { |
| 282 | for _, pattern := range strings.Fields(strings.TrimPrefix(c.Text, "//go:embed ")) { |
| 283 | matches, _ := filepath.Glob(filepath.Join(root, filepath.Dir(strings.Split(d.key, ":")[0]), pattern)) |
| 284 | for _, path := range matches { |
| 285 | rel, _ := filepath.Rel(root, path) |
| 286 | add(d.key, filepath.ToSlash(rel)+":external") |
| 287 | } |
| 288 | } |
| 289 | } |
| 290 | } |
| 291 | } |
| 292 | } |
| 293 | // The console is a separate shipped executable. Its floor admission and |
| 294 | // control-store writers were explicitly classified as authority; they do |
| 295 | // not become unreachable because they are outside the carlos binary. |
| 296 | reached := map[string]string{"main.go:main": "root CLI entrypoint", "cmd/console/main.go:main": "console service entrypoint"} |
| 297 | queue := []string{"main.go:main", "cmd/console/main.go:main"} |
| 298 | for len(queue) > 0 { |
| 299 | from := queue[0] |
| 300 | queue = queue[1:] |
| 301 | var next []string |
| 302 | for to := range graph[from] { |
| 303 | next = append(next, to) |
| 304 | } |
| 305 | sort.Strings(next) |
| 306 | for _, to := range next { |
| 307 | if _, ok := reached[to]; !ok { |
| 308 | reached[to] = from |
| 309 | queue = append(queue, to) |
| 310 | } |
| 311 | } |
| 312 | } |
| 313 | return reached, nil |
| 314 | } |
| 315 | |
| 316 | // encoding/json invokes these typed callbacks through reflection, outside the |
| 317 | // repository call graph. Follow only types passed to actual Unmarshal calls. |
| 318 | func inputJSONMethods(typ types.Type, seen map[types.Type]bool) []types.Object { |
| 319 | if typ == nil || seen[typ] { |
| 320 | return nil |
| 321 | } |
| 322 | seen[typ] = true |
| 323 | var out []types.Object |
| 324 | for _, receiver := range []types.Type{typ, types.NewPointer(typ)} { |
| 325 | methods := types.NewMethodSet(receiver) |
| 326 | for i := 0; i < methods.Len(); i++ { |
| 327 | m := methods.At(i).Obj() |
| 328 | if m.Name() == "UnmarshalJSON" { |
| 329 | out = append(out, m) |
| 330 | } |
| 331 | } |
| 332 | } |
| 333 | switch t := typ.(type) { |
| 334 | case *types.Named: |
| 335 | out = append(out, inputJSONMethods(t.Underlying(), seen)...) |
| 336 | case *types.Alias: |
| 337 | out = append(out, inputJSONMethods(types.Unalias(t), seen)...) |
| 338 | case *types.Pointer: |
| 339 | out = append(out, inputJSONMethods(t.Elem(), seen)...) |
| 340 | case *types.Slice: |
| 341 | out = append(out, inputJSONMethods(t.Elem(), seen)...) |
| 342 | case *types.Array: |
| 343 | out = append(out, inputJSONMethods(t.Elem(), seen)...) |
| 344 | case *types.Map: |
| 345 | out = append(out, inputJSONMethods(t.Elem(), seen)...) |
| 346 | case *types.Struct: |
| 347 | for i := 0; i < t.NumFields(); i++ { |
| 348 | out = append(out, inputJSONMethods(t.Field(i).Type(), seen)...) |
| 349 | } |
| 350 | } |
| 351 | return out |
| 352 | } |
| 353 | |
| 354 | type inputExemption struct { |
| 355 | Ruling string |
| 356 | Pending string |
| 357 | } |
| 358 | |
| 359 | var inputExemptions = map[string]inputExemption{ |
| 360 | "internal/host/claimservingpark_other.go:newServingParkFS": {Ruling: "activation-scope-2026-10-16; run12: non-Linux refusal-only seed backend; production constructor refuses before allocation"}, |
| 361 | "internal/host/claimservingpark_other.go:servingParkFS.Capture": {Ruling: "activation-scope-2026-10-16; run12: non-Linux refusal-only seed backend; production constructor refuses before allocation"}, |
| 362 | "internal/host/claimservingpark_other.go:servingParkFS.CheckParked": {Ruling: "activation-scope-2026-10-16; run12: non-Linux refusal-only seed backend; production constructor refuses before allocation"}, |
| 363 | "internal/host/claimservingpark_other.go:servingParkFS.Delete": {Ruling: "activation-scope-2026-10-16; run12: non-Linux refusal-only seed backend; production constructor refuses before allocation"}, |
| 364 | "internal/host/claimservingpark_other.go:servingParkFS.Move": {Ruling: "activation-scope-2026-10-16; run12: non-Linux refusal-only seed backend; production constructor refuses before allocation"}, |
| 365 | "internal/host/claimservingpark_other.go:servingParkFS.Prepare": {Ruling: "activation-scope-2026-10-16; run12: non-Linux refusal-only seed backend; production constructor refuses before allocation"}, |
| 366 | "internal/host/claimservingreboot_prepare_linux.go:servingParkRoot.prepareServingReboot": {Ruling: "activation-scope-2026-10-16; run12: frozen standalone preparation entrypoint; production recovery uses prepareOrCommitServingReboot"}, |
| 367 | "internal/hostbox/servingpark_transport.go:ServingParkClient.ReplayUncertain": {Ruling: "activation-scope-2026-10-16; run12: frozen explicit readback/replay entrypoint; enabled requests use journalSend"}, |
| 368 | "internal/host/rootmovesfloor.go:rootMovesFloors.reconstruct": {Ruling: "P3-R4.6"}, |
| 369 | "internal/host/rootmovesfloor_authorization.go:rootMovesFloors.authorizeReconstruction": {Ruling: "P3-R4.6"}, |
| 370 | "internal/host/rootmovesfloor_authorization.go:rootMovesFloors.authorizedReconstruction": {Ruling: "P3-R4.6"}, |
| 371 | "internal/hostclaim/relocation.go:StartRelocation": {Ruling: "R-E2a"}, |
| 372 | "internal/hostrelocate/participant.go:Participant.Sweep": {Ruling: "R-E2a"}, |
| 373 | "internal/hostrelocate/worker.go:Worker.Start": {Ruling: "R-E2a"}, |
| 374 | // This inventory stops at activation-tools, so seed's row outlives the |
| 375 | // deleted function; the roster-binding overlay removes it from the full |
| 376 | // census and declares servingPriorBinding at the same class. |
| 377 | "internal/host/claimservinghandover.go:servingPriorRevision": {Ruling: "census-ruling-roster-binding: deleted, renamed servingPriorBinding (equality on the box binding), which takes over all its callers"}, |
| 378 | } |
| 379 | |
| 380 | // Update this checkpoint status when merging E's completion report. The 4a |
| 381 | // exemption must be removed before either Part 2 or Part 3 lands. |
| 382 | var inputTrackEComplete = map[string]bool{"4a": false} |
| 383 | |
| 384 | func checkInputExemptions(inv rootReadInventory, callers map[string]string, exemptions map[string]inputExemption, completed map[string]bool) []string { |
| 385 | var failures []string |
| 386 | for key, ex := range exemptions { |
| 387 | e, exists := inv.Entries[key] |
| 388 | if !exists || e.Class != "authority" || inv.Removed[key] != "" { |
| 389 | failures = append(failures, key+": exemption has no authority declaration") |
| 390 | } |
| 391 | if ex.Ruling == "" { |
| 392 | failures = append(failures, key+": exemption has no ruling") |
| 393 | } |
| 394 | if callers[key] != "" { |
| 395 | failures = append(failures, key+": exempt unit now has a production caller; remove exemption and supply coverage") |
| 396 | } |
| 397 | if ex.Pending != "" && completed[ex.Pending] { |
| 398 | failures = append(failures, key+": temporary exemption survived completed track E item "+ex.Pending) |
| 399 | } |
| 400 | } |
| 401 | return failures |
| 402 | } |
| 403 | |
| 404 | func inputActivationEnabled(e classifiedRootRead) bool { |
| 405 | if inputExemptions[e.File+":"+e.Unit].Ruling != "" { |
| 406 | return true |
| 407 | } |
| 408 | for _, prefix := range []string{ |
| 409 | "internal/hostrelocate/", "internal/hostclaim/relocat", |
| 410 | "internal/host/relocation", "internal/activator/worker", |
| 411 | } { |
| 412 | if strings.HasPrefix(e.File, prefix) { |
| 413 | return false |
| 414 | } |
| 415 | } |
| 416 | for _, owner := range strings.Split(e.Task, ",") { |
| 417 | if owner != "E4" && owner != "J1" && owner != "J2" && owner != "J3" && owner != "J4" { |
| 418 | return true |
| 419 | } |
| 420 | } |
| 421 | return false |
| 422 | } |
| 423 | |
| 424 | func checkInputRow(e classifiedRootRead, callers map[string]string, tests map[string]bool) []string { |
| 425 | if e.Class != "authority" || !inputActivationEnabled(e) { |
| 426 | return nil |
| 427 | } |
| 428 | key := e.File + ":" + e.Unit |
| 429 | families, err := inputFamilyFor(e) |
| 430 | if err != nil { |
| 431 | return []string{key + ": " + err.Error()} |
| 432 | } |
| 433 | var failures []string |
| 434 | if callers[key] == "" && inputExemptions[key].Ruling == "" { |
| 435 | failures = append(failures, key+": no production caller reachable from root entrypoint") |
| 436 | } |
| 437 | for _, name := range families { |
| 438 | f := inputFamilies[name] |
| 439 | for slot, test := range []string{f.Hostile, f.Rename, f.Erasure, f.Retry, f.Positive} { |
| 440 | if test == "" || !tests[test] { |
| 441 | failures = append(failures, "family "+name+" missing "+[]string{"hostile", "rename", "erasure", "retry", "positive"}[slot]+" fixture "+test) |
| 442 | } |
| 443 | } |
| 444 | } |
| 445 | return failures |
| 446 | } |
| 447 | |
| 448 | func TestPlan14RootInputInventoryCoverage(t *testing.T) { |
| 449 | // Reads the source tree and read-only package maps only, and spends |
| 450 | // most of its time type-checking the module, so it runs in parallel |
| 451 | // rather than holding up the package's serial tests. |
| 452 | t.Parallel() |
| 453 | root := censusRoot(t) |
| 454 | inv := inputInventory(t) |
| 455 | tests, err := inputTests(root) |
| 456 | if err != nil { |
| 457 | t.Fatal(err) |
| 458 | } |
| 459 | callers, err := inputCallers(root) |
| 460 | if err != nil { |
| 461 | t.Fatal(err) |
| 462 | } |
| 463 | failures := checkInputExemptions(inv, callers, inputExemptions, inputTrackEComplete) |
| 464 | var report []string |
| 465 | for key, e := range inv.Entries { |
| 466 | if inv.Removed[key] != "" { |
| 467 | continue |
| 468 | } |
| 469 | failures = append(failures, checkInputRow(e, callers, tests)...) |
| 470 | if e.Class == "authority" { |
| 471 | family, familyErr := inputFamilyFor(e) |
| 472 | if familyErr != nil { |
| 473 | family = []string{e.Task} |
| 474 | } |
| 475 | mode := "enabled" |
| 476 | if !inputActivationEnabled(e) { |
| 477 | mode = "report-only" |
| 478 | } |
| 479 | report = append(report, fmt.Sprintf("%s\t%s\t%s\t%s\t%s\t%s", mode, e.File, e.Unit, strings.Join(family, "+"), callers[e.File+":"+e.Unit], e.Basis)) |
| 480 | } |
| 481 | } |
| 482 | sort.Strings(report) |
| 483 | for _, row := range report { |
| 484 | t.Log("J4\t" + row) |
| 485 | } |
| 486 | path := os.Getenv("PLAN14_J4_REPORT") |
| 487 | if path == "" { |
| 488 | path = filepath.Join(t.TempDir(), "plan14-root-input-report.tsv") |
| 489 | } |
| 490 | if err := os.WriteFile(path, []byte("scope\tfile\tunit\tfamily\tcaller\tbasis\n"+strings.Join(report, "\n")+"\n"), 0600); err != nil { |
| 491 | t.Fatal(err) |
| 492 | } |
| 493 | t.Logf("J4 report: %s", path) |
| 494 | sort.Strings(failures) |
| 495 | last := "" |
| 496 | for _, failure := range failures { |
| 497 | if failure != last { |
| 498 | t.Error(failure) |
| 499 | last = failure |
| 500 | } |
| 501 | } |
| 502 | t.Logf("authority coverage: %d reachable declarations; %d failures", len(callers), len(failures)) |
| 503 | } |
| 504 | |
| 505 | func TestPlan14RootInputInventoryCoverageMutations(t *testing.T) { |
| 506 | e := classifiedRootRead{rootReadEntry: rootReadEntry{File: "root.go", Unit: "effect"}, Class: "authority", Task: "A,J4"} |
| 507 | callers := map[string]string{"root.go:effect": "main.go:main"} |
| 508 | tests := map[string]bool{} |
| 509 | for _, f := range inputFamilies { |
| 510 | for _, name := range []string{f.Hostile, f.Rename, f.Erasure, f.Retry, f.Positive} { |
| 511 | tests[name] = true |
| 512 | } |
| 513 | } |
| 514 | if errors := checkInputRow(e, callers, tests); len(errors) != 0 { |
| 515 | t.Fatal(errors) |
| 516 | } |
| 517 | delete(tests, inputFamilies["A"].Positive) |
| 518 | if len(checkInputRow(e, callers, tests)) == 0 { |
| 519 | t.Fatal("removed fixture accepted") |
| 520 | } |
| 521 | tests[inputFamilies["A"].Positive] = true |
| 522 | e.Task = "unknown" |
| 523 | if len(checkInputRow(e, callers, tests)) == 0 { |
| 524 | t.Fatal("unknown family accepted") |
| 525 | } |
| 526 | e.Task = "A" |
| 527 | delete(callers, "root.go:effect") |
| 528 | if len(checkInputRow(e, callers, tests)) == 0 { |
| 529 | t.Fatal("orphan accepted") |
| 530 | } |
| 531 | e.Class = "legacy" |
| 532 | if len(checkInputRow(e, callers, tests)) != 0 { |
| 533 | t.Fatal("legacy entered J4") |
| 534 | } |
| 535 | } |
| 536 | |
| 537 | func TestPlan14RootInputExemptions(t *testing.T) { |
| 538 | inv := rootReadInventory{Entries: map[string]classifiedRootRead{}} |
| 539 | for key := range inputExemptions { |
| 540 | inv.Entries[key] = classifiedRootRead{Class: "authority"} |
| 541 | } |
| 542 | tests := map[string]bool{} |
| 543 | for _, name := range []string{inputFamilies["A"].Hostile, inputFamilies["A"].Rename, inputFamilies["A"].Erasure, inputFamilies["A"].Retry, inputFamilies["A"].Positive} { |
| 544 | tests[name] = true |
| 545 | } |
| 546 | for key := range inputExemptions { |
| 547 | parts := strings.SplitN(key, ":", 2) |
| 548 | row := classifiedRootRead{rootReadEntry: rootReadEntry{File: parts[0], Unit: parts[1]}, Class: "authority", Task: "A"} |
| 549 | if errs := checkInputRow(row, nil, tests); len(errs) != 0 { |
| 550 | t.Fatal("callerless exemption refused", errs) |
| 551 | } |
| 552 | if len(checkInputRow(row, nil, nil)) == 0 { |
| 553 | t.Fatal("exemption waived family fixtures") |
| 554 | } |
| 555 | } |
| 556 | callers := map[string]string{} |
| 557 | if errs := checkInputExemptions(inv, callers, inputExemptions, nil); len(errs) != 0 { |
| 558 | t.Fatal(errs) |
| 559 | } |
| 560 | for key := range inputExemptions { |
| 561 | t.Run(key, func(t *testing.T) { |
| 562 | callers[key] = "main.go:main" |
| 563 | if len(checkInputExemptions(inv, callers, inputExemptions, nil)) == 0 { |
| 564 | t.Fatal("production wiring escaped exemption guard") |
| 565 | } |
| 566 | delete(callers, key) |
| 567 | e := inv.Entries[key] |
| 568 | delete(inv.Entries, key) |
| 569 | if len(checkInputExemptions(inv, callers, inputExemptions, nil)) == 0 { |
| 570 | t.Fatal("stale exemption accepted") |
| 571 | } |
| 572 | inv.Entries[key] = e |
| 573 | }) |
| 574 | } |
| 575 | pending := map[string]inputExemption{"internal/host/claimunit_binding_source.go:readClaimUnitShapes": {Ruling: "pending track E item 4a", Pending: "4a"}} |
| 576 | if len(checkInputExemptions(inv, callers, pending, map[string]bool{"4a": true})) == 0 { |
| 577 | t.Fatal("completed E4a accepted temporary exemption") |
| 578 | } |
| 579 | if len(checkInputExemptions(inv, callers, map[string]inputExemption{"unknown": {}}, nil)) == 0 { |
| 580 | t.Fatal("unruled exemption accepted") |
| 581 | } |
| 582 | } |
| 583 | |
| 584 | func TestPlan14RootInputExemptionGraphMutation(t *testing.T) { |
| 585 | root := t.TempDir() |
| 586 | write := func(name, body string) { |
| 587 | t.Helper() |
| 588 | path := filepath.Join(root, name) |
| 589 | if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil { |
| 590 | t.Fatal(err) |
| 591 | } |
| 592 | if err := os.WriteFile(path, []byte(body), 0600); err != nil { |
| 593 | t.Fatal(err) |
| 594 | } |
| 595 | } |
| 596 | write("go.mod", "module fixture/root\n\ngo 1.24\n") |
| 597 | write("main.go", "package main\nfunc main(){}\n") |
| 598 | write("internal/hostclaim/relocation.go", "package hostclaim\nfunc StartRelocation(){}\n") |
| 599 | key := "internal/hostclaim/relocation.go:StartRelocation" |
| 600 | inv := rootReadInventory{Entries: map[string]classifiedRootRead{key: {Class: "authority"}}} |
| 601 | exemptions := map[string]inputExemption{key: inputExemptions[key]} |
| 602 | callers, err := inputCallers(root) |
| 603 | if err != nil { |
| 604 | t.Fatal(err) |
| 605 | } |
| 606 | if errs := checkInputExemptions(inv, callers, exemptions, nil); len(errs) != 0 { |
| 607 | t.Fatal(errs) |
| 608 | } |
| 609 | write("main.go", "package main\nimport \"fixture/root/internal/hostclaim\"\nfunc main(){hostclaim.StartRelocation()}\n") |
| 610 | callers, err = inputCallers(root) |
| 611 | if err != nil { |
| 612 | t.Fatal(err) |
| 613 | } |
| 614 | if callers[key] == "" || len(checkInputExemptions(inv, callers, exemptions, nil)) == 0 { |
| 615 | t.Fatal("production relocation wiring escaped the exemption") |
| 616 | } |
| 617 | } |
| 618 | |
| 619 | func TestPlan14ActivationInventoryScope(t *testing.T) { |
| 620 | e := classifiedRootRead{rootReadEntry: rootReadEntry{File: "root.go", Unit: "effect"}, Class: "authority", Task: "E4,J4"} |
| 621 | if inputActivationEnabled(e) || len(checkInputRow(e, nil, nil)) != 0 { |
| 622 | t.Fatal("dropped E4 slots enforced") |
| 623 | } |
| 624 | for _, owner := range []string{"A", "B", "B2", "B3", "B4", "C", "D", "E1", "E2", "E3", "F", "G"} { |
| 625 | e.Task = owner + ",E4,J4" |
| 626 | if !inputActivationEnabled(e) || len(checkInputRow(e, nil, nil)) == 0 { |
| 627 | t.Fatal("enabled mixed family waived", owner) |
| 628 | } |
| 629 | f := inputFamilies[owner] |
| 630 | for _, absent := range []string{f.Hostile, f.Rename, f.Erasure, f.Retry, f.Positive} { |
| 631 | tests := map[string]bool{} |
| 632 | for _, name := range []string{f.Hostile, f.Rename, f.Erasure, f.Retry, f.Positive} { |
| 633 | tests[name] = true |
| 634 | } |
| 635 | delete(tests, absent) |
| 636 | if len(checkInputRow(e, map[string]string{"root.go:effect": "main.go:main"}, tests)) == 0 { |
| 637 | t.Fatal("missing enabled fixture accepted", owner, absent) |
| 638 | } |
| 639 | } |
| 640 | } |
| 641 | e.File, e.Unit, e.Task = "internal/hostrelocate/worker.go", "Worker.disabled", "unknown" |
| 642 | if inputActivationEnabled(e) || len(checkInputRow(e, nil, nil)) != 0 { |
| 643 | t.Fatal("frozen family enforced") |
| 644 | } |
| 645 | e.Unit, e.Task = "Worker.Start", "C,D" |
| 646 | if !inputActivationEnabled(e) || len(checkInputRow(e, nil, nil)) == 0 { |
| 647 | t.Fatal("exemption escaped scope") |
| 648 | } |
| 649 | } |
| 650 | |