Project: Covert Cubicles Build Phase 6 / 7 \u00B7 PHASE OPENING MODULE

MODULE 20 / 31 \u00B7 LAYER 6 \u00B7 DATA_TELEMETRY \u00B7 PHASE 6 OPENS Per-Session Telemetry Projection

First module of Phase 6 (Research Capture). A thin projection over SM_Game's existing session metadata. Aggregates identity, timing, progression, and pipeline log counts into a structured blob for export by M21 DATA_Recorder and consumption by post-hoc analysis scripts.

DEPENDENCIES: SM_Game, PM_Phase, DATA_Decisions
Pending
PHASE 6 BEGINS \u00B7 RESEARCH CAPTURE LAYER
Phase 6 modules are thin projections, not stateful processors.
ADR-054 establishes the Phase 6 architectural pattern: research-capture modules own no private state and add no SM_Game extensions. They aggregate over what already exists. M20 reads SM_Game's session metadata and seven log counts, computing a structured telemetry blob on demand. This is the first module since Phase 1 that doesn't extend SM_Game — the seven extensions stand at seven, with mandatory consolidation at M31 still ahead.
Single source of truth: SM_Game owns session timing
Originally drafted with private state for explicit end-marking — until review found that SM_Game already maintains createdAt, endedAt, endReason, sessionId, and gameState on its session shape. M20 reads these fields directly. No synchronisation bugs possible because there is no second place to synchronise to. This is the discipline that should propagate to M21–M25.

Automated Assertions

#AssertionResultDetail

Interactive Workbench — Live Telemetry Viewer

Drive the session lifecycle on the left; the telemetry blob on the right updates after every action. Fields whose values just changed are momentarily highlighted in amber to make the projection visible.

Session lifecycle harness
Session creation
Phase 1 decisions (build pipeline activity)
Scrutiny escalation
End session (with semantic reason)
Reset
Live telemetry blob (re-computes after each action)
Identity
sessionIdnull
conditionVariantnull
insiderAssignednull
Timing
createdAtnull
endedAtnull
durationSecondsnull
Progression
gameStatenull
currentPhasenull
decisionsSubmitted / total 0 / 16
percentComplete 0.0%
Pipeline log counts
0decisions
0consequences
0events
0confusion
0reasoning
0morality
0trigger
0condition
Termination
terminationReason no_session
endReasonnull
Provenance
telemetryVersion
computedAt
Raw JSON (the structure analysis scripts will receive)
{}
Telemetry schema v1.0 \u2014 ADR-056
FieldTypeSourceDescription
Identity
sessionIdstring | nullSM_Game.session.sessionIdUUID generated by SM_Game at createSession()
conditionVariant'adaptive' | 'static' | nullSM_Game.session.conditionVariantRQ1b experimental treatment assignment
insiderAssignedstring | nullSM_Game.session.insiderAssignedWhich peer is the insider (arthur / liam / sarah / dave)
Timing
createdAtISO 8601 UTC | nullSM_Game.session.createdAtSession start timestamp
endedAtISO 8601 UTC | nullSM_Game.session.endedAtSession end timestamp; null if still active
durationSecondsnumber | nullcomputed: end - startTotal session duration; null if not yet ended
Progression
gameState'active' | 'ended' | nullSM_Game.session.gameStateWhether the session is in flight or finished
currentPhase1 | 2 | 3 | 4 | nullPM_Phase.getCurrentPhase()Active phase; null if no session
decisionsSubmittednumberSM_Game.getDecisionHistory().lengthCount of decisions committed so far
decisionsTotalnumberDATA_Decisions.getIds().lengthTotal decisions in the architecture (16)
percentCompletenumber [0..1]computed: submitted / totalSession progress fraction
Pipeline log counts
logCounts.decisionHistorynumberSM_Game.getDecisionHistory().lengthDecisions submitted (mirror of decisionsSubmitted)
logCounts.consequenceTagsnumberSM_Game.getConsequenceTags().lengthStory-flag effects recorded by M11
logCounts.eventsnumberSM_Game.getEventLog().lengthNarrative events fired by M14
logCounts.confusionnumberSM_Game.getConfusionLog().lengthConfusion classifications by M15
logCounts.reasoningnumberSM_Game.getReasoningLog().lengthReasoning classifications by M16
logCounts.moralitynumberSM_Game.getMoralityLog().lengthMorality classifications by M17 (PRIMARY DV)
logCounts.triggernumberSM_Game.getTriggerLog().lengthTrigger decisions by M18
logCounts.conditionnumberSM_Game.getConditionLog().lengthGating decisions by M19
Termination
terminationReason'completed' | 'in_progress' | 'incomplete' | 'no_session'computed: gameState + decisionsStandardised classification for analysis filters (ADR-055)
endReasonstring | nullSM_Game.session.endReasonFree-text reason from endSession() call (e.g. 'player_accused', 'time_expired')
Provenance
telemetryVersionstringmodule constantSchema version — increment for breaking changes (ADR-056)
computedAtISO 8601 UTCcomputed: nowWhen this telemetry blob was generated

API Surface — Manual Verification

M20 has the smallest public API of any module so far — three entries.