Module 5: Context adaptation

Encode personal and situational context into VCP's adaptation layer. Personal state dimensions, signal sources, context transitions, privacy, and elicitation.

DEV 25 min

Learning objectives

By the end of this module, you will be able to:

  • Encode personal and situational context into VCP's adaptation layer
  • Recognize the four context transition severity levels and plan how your application responds to each
  • Give your model a protection level derived from personal state and observe how replies change
  • Minimize personal context before it leaves your application, and keep its raw values away from the model

5.1 — Why context matters

A constitutional value like "be honest" means different things in different situations:

  • A doctor discussing a terminal diagnosis with a patient (gentle, staged disclosure)
  • A doctor discussing the same diagnosis with a colleague (direct, clinical)
  • A doctor documenting in a medical record (precise, factual)

The constitution doesn't change. The context changes, and VCP's adaptation layer lets the AI adjust accordingly.

5.2 — Personal context: personal state dimensions

VCP defines five personal state dimensions, each with categorical values and a 1–5 intensity rating:

DimensionPossible valuesExample
Cognitive Statefocused, distracted, overloaded, foggy, reflectivefoggy at intensity 4 = very foggy
Emotional Tonecalm, tense, frustrated, neutral, upliftedtense at intensity 3 = moderately tense
Energy Levelrested, low_energy, fatigued, wired, depletedfatigued at intensity 4 = very tired
Perceived Urgencyunhurried, time_aware, pressured, criticalcritical at intensity 5 = emergency
Body Signalsneutral, discomfort, pain, unwell, recoveringneutral at intensity 3 = no notable signals

Each signal can also carry a source field recording how it was obtained. The VCP-X-Personal extension defines these values. The Python SDK's compact personal-state wire value does not carry them, so keep them in your application record:

SourceMeaning
declaredUser explicitly set this value
inferredInferred by a language model from the user's messages
inferred_localInferred locally by pattern matching
measuredRead from an instrument such as a wearable or sensor
presetFrom an activated context preset
decayedSet earlier; decay has since reduced its intensity
elicitationUser self-reported through an MCP elicitation dialog, a structured form or URL-based flow (see 5.6)
personal_context = {
    "cognitive_state": {
        "value": "foggy",
        "intensity": 4,
        "source": "declared",  # local provenance metadata
    },
    "energy_level": {
        "value": "fatigued",
        "intensity": 4,
        "source": "declared",
    },
}

5.3 — Context transitions

An application can detect when context shifts and classify the severity. The reference detector in this site's source (src/lib/vcp/transition-detection.ts, which no page runs) uses the rules below. The Python SDK's StateTracker uses the same four levels but decides them differently. It marks a transition major when occasion, agency or constraints change, or when three or more dimensions change, and an emergency when the new context contains an emergency symbol such as 🚨.

SeverityTriggerExample
noneNo meaningful changeContinuing the same conversation
minor1–2 personal state dimensions changeEnergy drops from rested to low energy; slight mood shift
major3+ dimensions change, one intensity jumps 3+ points, body signals reach pain:4+ or unwell:5, or a constraint flag or persona changesUser reports pain at intensity 4, or three dimensions change at once
emergencyEmergency keywords detected in occasion, environment, or constraintsOccasion marked "emergency", or environment includes "fire"

Your application decides how to respond. A minor transition might only soften the tone, while an emergency transition could hand the conversation to a human. The Python SDK's StateTracker lets you register a handler for each severity.

5.4 — Encoding context in your app

Use the maintained Python adaptation types to build a context and encode it as a wire value, the compact string one application sends to another. The situational values are emoji codes, so 🌙 means night, 🏥 a hospital and 🚨 an emergency occasion. This example describes a focused person under critical time pressure in a hospital emergency at night:

from vcp.adaptation import (
    PersonalState,
    PersonalStateDimension,
    SituationalDimension,
    VCPContext,
)

context = VCPContext(
    situational={
        SituationalDimension.TIME: ["🌙"],
        SituationalDimension.SPACE: ["🏥"],
        SituationalDimension.OCCASION: ["🚨"],
    },
    personal={
        PersonalStateDimension.COGNITIVE_STATE:
            PersonalState("focused", 4),
        PersonalStateDimension.PERCEIVED_URGENCY:
            PersonalState("critical", 5),
    },
)

wire = context.encode()

Keep each value's source and its confidence (a 0 to 1 score for how sure you are of the reading; VCP-X-Personal defines both) beside the wire value in your application record. Do not imply that this metadata was transmitted if the selected wire format omitted it.

5.5 — Privacy and context opacity

GOVERNANCE Personal context is sensitive. In the VCP specification, context opacity means the model receives a derived protection level, never the raw personal signals behind it. (Module 1 used "opaque" in a different sense, for values hidden from the people who use the AI.) VCP provides filtering helpers, but applications must enforce the privacy boundary:

  • Keep raw state away from the model: send the inference model only the protection level (standard, elevated, high or critical, mapped from the state by the table in VCP-X-Personal §6.1), never the raw dimension values. The specification makes this a MUST.
  • Minimize before sharing: derive the smallest useful signal rather than forwarding raw notes
  • Require meaningful consent: bind consent to the platform, fields, purpose, and expiry
  • Separate local provenance: label declared, inferred, preset, and elicited values without claiming that the wire format carries those labels
  • Choose storage deliberately: use ephemeral storage for sensitive state and opt-in continuity for any durable copy
  • Apply lifecycle rules: decay or expire stale state and reject implausible future timestamps
  • Redact audit output: encoded context and hashed identifiers can still be sensitive

5.6 — MCP elicitation as a context source

MCP (the Model Context Protocol, an open standard for connecting AI applications to tools and data) includes a feature called elicitation. Partway through a task, an MCP server can pause and ask the user for structured input. Instead of relying solely on inference or presets, your application can then ask the user directly when context is missing or ambiguous.

Two modes are available:

  • Form mode: the server presents structured fields (text inputs, selections, sliders) and the user fills them in. The response is validated against a schema before acceptance.
  • URL mode: the server directs the user to a browser-based flow (e.g., OAuth consent, detailed questionnaire) and waits for completion.

Elicited context can feed an application-owned VCP context pipeline after validation and consent. Elicited answers do not get context opacity automatically. The application must minimize the response, record provenance locally, and run the same platform filter used for every other context source.

Filling a context gap before the PDP decides

Your application can check for missing context before the PDP (Policy Decision Point, the PDPEnforcer from Module 4) makes a decision. For example, if a constitution requires knowing the user's role before granting access to certain tools, the application can elicit that information instead of defaulting to a restrictive fallback. VCP-SDK 4.2.0 has no context-gap detection, and elicit() belongs to the MCP host, so the sketch below is application code.

# Integration pseudocode. elicit() belongs to the MCP host, not VCP-SDK;
# filter_for_platform() stands in for your wrapper around
# vcp.privacy.filter_context_for_platform(context, manifest, consent).
response = await mcp_host.elicit(schema=ContextGapSchema)
if response.accepted:
    validated = ContextGapSchema.model_validate(response.data)
    local_provenance.record("energy_level", source="elicitation")
    candidate = current_context.set_personal(
        PersonalStateDimension.ENERGY_LEVEL,
        validated.energy,
        validated.intensity,
    )
    shareable = filter_for_platform(candidate, consent_record)

Bilateral elicitation

The same mechanism can carry a conversation about values, not only a missing fact. When the application finds a tension between constitutional rules and the current context, or a welfare concern, it can put the question to the user rather than deciding alone. This turns safety from a monologue into a dialogue. Bilateral elicitation is a design pattern you build on MCP elicitation; neither the VCP specification nor VCP-SDK 4.2.0 defines it.

Exercise

Modify your chat app to accept context updates (e.g., /cognitive foggy 4, /urgency critical 5), map the state to its protection level, put only that level in the system message, and compare replies before and after. Look for shorter, more focused answers as the level rises, and note where the model does not adapt.

Context adaptation is what separates a value-aligned AI from a rule-following AI. The same values, applied with contextual awareness, produce qualitatively different — and more appropriate — responses.

See it in action

The Marta demo lets you set four of the five personal state dimensions and shows how a pre-scripted Mediator reply changes with them. The Campion demo steps one person through two preset contexts, focused at work in the morning and tired at home in the evening; it has no state controls of its own.