Chapter 9 — The Pod Model — Landscape + Topology Realised
Chapters 1 through 8 walked the schemas and the implementation. This chapter walks what's at runtime when activation completes — the Pod, the unit of horizontal CI parallelism, the integrated state of a Landscape plus a Topology that test code runs against.
The Pod is a concept that crosses Volumes 1, 2, 3, and 6. Each volume carries part of the picture:
- Volume 1 chapter 9 introduced the vocabulary at the contract level.
- Volume 2 chapter 3 walked the Pod as the unit of horizontal parallelism.
- Volume 3 chapter 4 walked Specifications as the Pod-provisioning mechanism.
- Volume 6 (this volume) walks the runtime instance — what the framework holds in memory after activation.
This chapter closes Volume 6's Part II by assembling all the pieces into one runtime picture.
What a Pod is at runtime
A Pod is the runtime instance produced when a
DistributedTestJob activates against a Landscape and a
Topology. Operationally, a Pod is:
- A
Landscapesingleton instance (or a project's subclass). - Its
_layer_configuration._landscape_infoand_topology_infodicts. - Its
_layer_integration._integrated_devicesand_integrated_servicesdicts holdingLandscapeDeviceandLandscapeServiceinstances. - Its
_layer_operational._operational_clustersdict holdingLandscapeDeviceClusterinstances materialised from the Topology. - The Coordinators managing each subset of devices.
- The ProtocolExtensions attached to each device.
- The
CredentialManagerholding resolved credentials. - The
DataProfileManager(Volume 2 chapter 12) holding resolved data profiles.
The Pod is the in-memory state. Test code reaches into it
through LandscapeSingleton() and the device / service /
cluster accessors.
The realisation flow
A reader should be able to walk Pod realisation step-by-step. The full flow:
- CI tooling reads a Specification (Volume 3 chapter 4), checks out or creates resources, emits a Landscape file and a Topology file.
- Orchestration invokes a
DistributedTestJobwith the Landscape and Topology names as inputs. DistributedTestJob.__enter__runsbegin(), which constructs the recorder and theDistributedTestSequencer.fire_attach_to_environmentandfire_attach_to_frameworkpropagate the landscape to integration couplings.fire_activate_configurationcallslandscape.activate_configuration():LandscapeConfigurationLayer.load_landscapereads the Landscape file via theLANDSCAPE_CONFIGURATION_MAP.load_topologyreads the Topology file.initialize_credentialsresolves the credentials.validate_topology(the project's override) verifies the Topology's claim against the actual environment.
fire_activate_integrationcallslandscape.activate_integration():- For each declared device: look up the right Coordinator
by deviceType, call
coordinator.create_landscape_device(...), get back aLandscapeDevice(or project subclass). - For each device, walk the
_featuresdict: built-in Features get their ProtocolExtensions wired automatically; project-specific Features are dispatched through the project'sLandscapeDevicesubclass'sattach_features. - Same for services and the special-purpose subsystems (power, serial).
- Result:
_integrated_devicesand_integrated_servicesare populated.
- For each declared device: look up the right Coordinator
by deviceType, call
fire_activate_operationscallslandscape.activate_operations():LandscapeOperationalLayer.activate_coordinators— each Coordinator probes its devices, brings them online, populates metadata._create_clustersconsumes theclusterssection of the Topology, materialisesLandscapeDeviceClusterinstances.establish_connectivityverifies every device is reachable.
- The Pod is realised. Test code can now call
landscape.device(name),landscape.devices_by_role(role),landscape.cluster(name), etc.
After step 8 the Pod's runtime state is complete. Tests run against it sequentially (Volume 2 chapter 8).
What test code sees
A test inside a realised Pod has the following accessors:
landscape = LandscapeSingleton()
# Single device by name
casey_01 = landscape.device("casey-01")
casey_01.ssh.run_cmd("uptime") # primary access
casey_01.serial.write("...") # service-reference Feature
casey_01.power.cycle() # service-reference Feature
# Devices by role (Landscape's role: field)
workers = landscape.devices_by_role("worker")
for w in workers:
w.ssh.run_cmd("...")
# Cluster by name
db_cluster = landscape.cluster("db-cluster")
for node in db_cluster.nodes.values():
node.ssh.run_cmd("...")
spares = list(db_cluster.spares.values())
# Project-specific subclass accessors
zone_a_devices = landscape.devices_in_cooling_zone("zone-a") # custom
# Service by name
vault = landscape.service("lab-vault")
vault.api.get("/secrets/...") # service-specific extension
# Topology accessors (project-specific)
deps = landscape.topology.dependencies("primary") # custom
The accessors are uniform — devices, services, and clusters are queried the same way regardless of how they were declared (apod / infrastructure / project-specific sections). The Pod's runtime exposes a clean test surface.
The Pod's properties as a unit of parallelism
Volume 2 chapter 3 walked these properties; this chapter restates them with the schema-level grounding from Volume 6:
Internal interoperability
Within a Pod, the framework has wired every device's
ProtocolExtensions, every Cluster's membership, every
service's connection. Test code calling device.ssh.run_cmd
hits a real SSH session; calling cluster.nodes
returns the actual node instances; calling
service.db.execute hits the actual database connection.
The framework's activation built all the wires.
External isolation
Pods don't share state. When CI runs ten Specifications producing ten Pods, each Pod has its own Landscape singleton, its own Coordinators, its own Credentials manager. Effects in Pod A — a cluster failover, a service restart, a credential rotation — cannot propagate to Pod B because Pod B has no shared object referencing Pod A's resources.
The isolation property depends on Pod independence at the infrastructure level (different physical resources, different network namespaces, different shared services). The framework assumes this; the orchestration tier (Volume 3 chapter 4) is what provides it through Specifications.
Single-actor execution
Within a Pod, one test runs at a time (Volume 2 chapter 2's commitment). Tandem validators (Volume 2 chapter 5) attach to the test for parallel observation; the test is the only actor.
When Pod realisation fails
Pod realisation can fail at each step:
- Step 5 — Configuration. Files don't parse, credentials
don't resolve,
validate_topologyrejects the topology. The framework'sConfigurationErrororRuntimeErrorbubbles; activation aborts. - Step 6 — Integration. Unknown deviceTypes, misconfigured Features, Coordinator init errors. Activation aborts.
- Step 7 — Operational.
establish_connectivityfinds unreachable devices. Ifallow_missing_devices=False, activation aborts; ifTrue, activation continues with the missing devices flagged.
Each failure mode produces a structured error report the orchestrator can read (Volume 3 chapter 21). The orchestrator decides whether to re-provision the Pod, mark the run as infrastructure-failed, or escalate the campaign.
The Pod as the unit of horizontal CI parallelism
The framework's commitment from Volume 2 chapter 3:
| Level | Parallel? | Why |
|---|---|---|
| Within a test | No | Single actor commitment |
| Across tests in one Pod | No | Shared environment |
| Across Pods | Yes | Pods are independent |
Horizontal scaling at the CI layer is more Pods. Volume 3
chapter 4 walked Specifications as the Pod-provisioning
mechanism. When a campaign needs to finish faster, the
orchestrator runs more Specifications concurrently → more
Pods → more DistributedTestJob processes → more parallel
test execution at the job level, with serial test
execution within each job.
What the next chapter does
Chapter 10 walks the provisioning layer that produces
Landscapes and Topologies from Specifications. The chapter
is honest about the current state: codecraftai-provisioning is
in scaffolding stage — the abstract CloudProvisioner is
empty, the AWS / Azure / GCP backends are stub subclasses,
the ProvisioningEngine parses YAML but doesn't yet
implement checkout-or-create. Chapter 10 walks the intended
shape with anchors on the present scaffolding and forward
references to where the implementation will land.
This is the volume's first chapter on a topic outside Landscape/Topology proper. Chapters 11 and 12 cover UI automation; chapters 13 and 14 cover the GitLab and Flask worked examples; chapter 15 is the volume's case study.
End of Part II.