Session ModelΒΆ
A Session is the top-level delivery resource. It is instantiated from a
SessionDefinitionidentified by asession_typethat declares one or more parts and a session orchestration lifecycle (how those parts are admitted, sequenced and rolled up). Each part is a first-classTimedResourcewith its own, richer content-driven lifecycle, resolves its content to aFormby a selector pattern + requirements; the resolvedFormoptionally carries aPodDefinition(ADR-059). This page covers the catalog (definitions), the two lifecycle layers, and the runtime profiles. See resource-model.md for the underlyingTimedResourceabstraction.
Definitions vs runtimeΒΆ
- Definitions are
ResourceDefinitions (provisioning_source = seed) β content-driven catalog entries shipped in seed content. They describe what a session is.SessionDefinitiongeneralises the legacySessionType;PartDefinitiongeneralises the legacySessionPartRequirement(see definition-catalog-model.md and ADR-052). - Runtime resources (
Session,SessionPart,PodInstance) areTimedResources created from definitions at schedule time and reconciled toward their desired state.
classDiagram
class SessionDefinition {
+str session_type
+str~None form_qualified_name
+str part_execution
+list~PhaseDef session_lifecycle
+list~PartDefinition session_parts
+dict metadata
}
class PartDefinition {
+str name
+str selector_pattern
+dict requirements
+int order
+bool gates_next
+list~PhaseDef part_lifecycle
}
class Form {
+str fqn
+str sync_status
+str content_ref
+PodDefinitionRef~None pod_ref
}
class PodDefinitionRef {
+str definition_id
+str version
+str pod_type
+str content_hash
+is_synced() bool
}
class PodDefinition {
+str definition_id
+str version
+str pod_type
+str host_type
+dict topology
+str content_ref
}
class PhaseDef {
+str name
+str engine
+str trigger_on_status
+str workflow_ref
}
SessionDefinition "1" *-- "1..N" PartDefinition : session_parts
SessionDefinition "1" *-- "1..N" PhaseDef : session_lifecycle
PartDefinition ..> Form : selects (pattern + requirements)
Form "1" o-- "0..1" PodDefinitionRef : pod_ref
PartDefinition "1" *-- "0..N" PhaseDef : part_lifecycle
PodDefinitionRef ..> PodDefinition : resolves
SessionDefinitionΒΆ
The umbrella catalog entry is identified by a session_type (a stable definition key such as
CCIE-EI-Lab-Exam), not universally by a form-qualified name. It declares
session_parts: list[PartDefinition], a session lifecycle, and shared metadata.
Identity depends on cardinality:
- Single-part types (
lablet,practicelab) are the form β the one part's content is keyed by its form-qualified name (FQN), so the session is effectively addressed by that FQN. - Multi-part types (
expertlab= CCIE, 2 parts;expertdesign= CCDE, 4 parts) are addressed by theirsession_type; eachPartDefinitionselects a separateForm(its own FQN) by pattern + requirements.
The session lifecycle is an orchestration lifecycle defined in the seed definition
file and may be inherited from the session_type (lablet / practicelab / expertlab /
expertdesign); together with the part_execution policy (single / sequential /
parallel) it declares how the parts are admitted, sequenced and rolled up. The complementary,
much richer part lifecycle β what happens inside one part β is defined in the PAv1 content
package. See Two lifecycle layers below.
PartDefinition, selectors and requirementsΒΆ
Each part binds to its content by a selector pattern over form-qualified names plus optional
requirements (declarative filters the resolved form must satisfy β track, version, node
count, capability flags), resolved at schedule time. This is what makes parts reusable across
many exams while still constraining what may fill each slot.
| Selector pattern | Resolves to |
|---|---|
"Exam ** LAB" |
any exam's LAB part |
"Exam CCIE * * DES*" |
the CCIE design (DES) part for any track/version |
"* CCDE * COR" |
the CCDE core (COR) part |
The requirements block carries the admissibility filters ported from the legacy
SessionPartRequirement β they constrain which authored Form may fill the slot:
| Requirement | Constrains the resolved form by |
|---|---|
track_types / track_levels / track_acronyms |
the form's Track taxonomy coordinates |
exam_versions |
the form's Exam version |
module_acronyms |
the form's Module |
parts_count |
how many parts of this kind the session admits |
requires_pod |
whether the resolved Form must carry a PodDefinition (vs web/device-only) |
A part may:
- select a
Formthat references 0 or 1PodDefinition(none for web-only parts such as a CCDECORor CCIEDESdesign item β the pod travels with the resolvedForm, ADR-059); - declare its own lifecycle of phases (
PhaseDef), each bound to apipeline(native LCM) orworkflow(SE job) engine; - gate the next part (
gates_next = true) β parts are sequential and gated.
See ADR-045 for the model and selector resolution.
PodDefinitionΒΆ
The canonical pod spec (un-retired β see ADR-046).
It declares the pod's PodType (workload), target HostType (platform), topology, and
a pointer to synced content. PodInstances are created from it and bound to a Host. The pod ref
is owned by the Form the part resolves (ADR-059),
not the PartDefinition β a Form is a self-contained content + optional pod deliverable.
| Axis | Field | Examples |
|---|---|---|
| Workload (what the pod is) | PodType |
cml_on_aws, proxmox, vmware |
| Platform (where it runs) | HostType |
cml_on_aws, vmware, proxmox, native_aws, hybrid_hardware |
The two axes are split because they are not always the same: e.g. a hybrid_hardware host runs
physical appliances with no hypervisor, while native_aws uses AWS-managed services directly.
Not every part has a pod. Design items (e.g.
DES/COR) are web-only β noPodDefinition. Device-based parts target pre-existing devices reached through the ROC / RADkit collection mechanism; LCM provisions nothing for them, soroc_radkitis not aPodTypeβ it is an access/collection transport used by SE's Collect step (see glossary and ADR-046).
Session profiles (session_type)ΒΆ
The same structure expresses every delivery kind; the session_type selects the session
lifecycle template and how the session is addressed:
session_type |
Parts | part_execution |
Addressed by | Pods | Notes |
|---|---|---|---|---|---|
| lablet | 1 | single |
FQN | 1 Γ cml_on_aws |
The classic single-lab delivery. |
| practicelab | 1 | single |
FQN | 1 Γ any platform | Single-part, different platform. |
| expertlab (CCIE) | 2 | sequential |
session_type | per-part | e.g. DOO (1 device/pod) β AI-DOO (1 cml_on_aws pod). |
| expertdesign (CCDE) | 4 | sequential |
session_type | per-part (often none) | 4 Γ COR core-design parts resolved by "* CCDE * COR" selectors; mostly web items, no pod. |
Two lifecycle layersΒΆ
Both the Session and each SessionPart are TimedResources, so both have a lifecycle β but
they are complementary, not redundant: the session lifecycle is horizontal (orchestration
around and between parts), the part lifecycle is vertical (content delivery inside one
part). They never duplicate each other.
stateDiagram-v2
direction LR
state "Session (orchestration)" as S {
[*] --> admit
admit --> running : gate cleared
running --> aggregating : all parts settled
aggregating --> submitting : combined score report
submitting --> finalizing
finalizing --> [*]
}
state "SessionPart (content-driven, PAv1)" as P {
[*] --> p_pending
p_pending --> instantiating
instantiating --> waiting
waiting --> monitoring
monitoring --> collecting
collecting --> grading
grading --> reviewing
reviewing --> archiving
archiving --> [*]
}
| Layer | Phases | Defined in | Driven by |
|---|---|---|---|
| Session (orchestration) | seeded session_lifecycle, e.g. admit β run_parts β aggregate β submit β finalize; degenerates to pending β active β inactive for a single-part, no-gate type |
seed definition β session_lifecycle + part_execution policy (may inherit from session_type) |
the session-controller reconcile loop (ADR-047 / ADR-054) β admits the session, drives parts by part_execution, rolls up status. |
| SessionPart (content) | pending β instantiating β waiting β monitoring β collecting β grading β reviewing β archiving |
PAv1 content package | content-driven engine (ADR-044); the per-part business logic. |
The session lifecycle owns only what no single part can decide:
- cross-part ordering & gating β admit the next part once the gating part (
gates_next) completes, per thepart_executionpolicy; - session-scoped gates with no owning part β authorization / proctor admission, a global ready-to-begin gate, result aggregation then submission of the single combined score report for the whole session (one per session, not one per part), and archival rollup;
- aggregate status & failure policy β a session is healthy/consistent only while at
least one part is consistent (
status == desired_status, notFailed); if every part fails the session is inconsistent and escalates (ADR-047); - cross-part data flow β a later part whose content depends on an earlier part's outcome.
Each session phase is a PhaseDef bound to an engine exactly like a part phase β pipeline
(native orchestration / gating steps) or workflow (an SE Job, e.g. publish a combined report)
β so no new machinery is needed: the same generic loop reconciles the session's
session_lifecycle. The thin pending β active β inactive is simply the empty-orchestration
special case (one part, no session-scoped gates), so nothing is lost by generalising.
part_execution policyΒΆ
How the parts of a session are activated is data on the definition, not a different lifecycle:
part_execution |
Behaviour | Example |
|---|---|---|
single |
activate the one part; the session is effectively that part | lablet, practicelab |
sequential |
activate parts in order, each gated on the previous (gates_next) |
expertlab (CCIE), expertdesign (CCDE) |
parallel |
activate all parts at once; aggregate when all settle | (future multi-track) |
So "a lablet activates only one part while CCIE sequences two" is the same session reconcile
algorithm with a different part_execution value + part count β not a different lifecycle
definition.
Timeslot inheritance and containmentΒΆ
Parts inherit the session Timeslot as their default bounds and are constrained by it:
- Containment β
part.timeslot β session.timeslot(a part may not start beforesession.startnor end aftersession.end). - Cumulative compatibility β the parts' windows must fit collectively:
Ξ£ part.timeslot β€ session.timeslot. Sequential, gated parts cannot together overrun the session window.
These rules are validated at schedule time, before any part is provisioned.
Multi-part timelineΒΆ
Parts are sequential and gated, but a later part's eager pod (long lead_time) may
begin provisioning while an earlier part is still active β so the hardware is ready when the
part's window opens.
gantt
title CCIE expert lab (expertlab, 2 parts) β eager pod provisions early
dateFormat HH:mm
axisFormat %H:%M
section DOO part (1 pod, eager hardware)
Eager pod provision :crit, doo_prov, 08:30, 120m
Active window :doo, 09:00, 120m
section AI-DOO part (1 cml_on_aws pod, JIT)
JIT pod provision :aidoo_prov, 10:35, 20m
Active window :aidoo, after doo, 90m
Legacy mapping (Session Manager)ΒΆ
The legacy Session Manager modelled the lifecycle imperatively on the Session aggregate
(Empty β Assigned β Instantiating β Pending β Running β Pausing β Paused β Completed β Archived)
and a parallel SessionPart machine (Pending β Running β Paused β Completed β Grading β Graded β
Locked, plus a SessionPodStatus of None β Assigning β Assigned). There was no
desired_status and no reconciliation β transitions were driven by explicit commands.
The port collapses that onto the resource model:
| Legacy concept | LCM equivalent |
|---|---|
SessionType (+ SessionPartRequirement[]) |
SessionDefinition (+ PartDefinition[]) β a seed ResourceDefinition. |
Session rich state machine |
Session orchestration lifecycle (admit β run_parts β aggregate β finalize; thin pending β active β inactive is the degenerate single-part/no-gate case). |
SessionPart rich state machine |
SessionPart content-driven lifecycle from PAv1 (the rich phases now live at part level). |
SessionPodStatus (Assigning β Assigned) |
the part's PodInstance reconciliation (desired_status). |
| explicit command-driven transitions | desired_status + a reconcile loop (intent down, status up). |
| EventStore + Mongo projection | state-based MotorRepository + state_version, @dispatch reducers. |
The key shift: the legacy session-level richness is re-homed at the part level, and every
imperative transition becomes a declarative desired_status the reconciler converges toward.
RelatedΒΆ
- resource-model.md β
TimedResourceabstraction and reconciliation. - definition-catalog-model.md β content taxonomy, Form delivery, catalogue/config plane, authorization.
- flow-session-delivery.md β the end-to-end multi-part delivery flow.
- unified-resource-management.md β lifecycle vs automation.