contexa-core contexa-iam contexa-autoconfigure

Prompt Inspection & Bundle Versions

A security decision prompt runs as a versioned prompt bundle. This page covers how a bundle is identified, how the bundle of a request is chosen, how a bundle version moves through the registry states behind the release gate, how shadow runs, canaries and rollbacks work, and how the prompt inspector checks the sealed prompts. For the prompt itself, see Security Decision Prompt Pipeline.

Prompt bundles

A bundle is a governance descriptor (its prompt key, template key, prompt version, contract version, release status and rollback version, among others) together with the user prompt layout its code draws. Its prompt version is the bundle version and names its manifest on the classpath: prompt-bundles/manifests/<promptKey>/<version>.json. A build runs exactly two bundles of cortex.security-decision: the build default (v19 sections) and the v20 bundle (dictionary blocks). Older manifests stay as records of earlier bundles; they cannot be run.

B1
System text
Rendered system text of the L1 and L2 budget profiles
B2
Dictionary
Every dictionary entry with all its fields
B3
Golden render
Digest of the renders of the golden inputs
B4
Output format
Format instructions and the response fields
B5
Minimum action rules
The rule source and the default settings
B6
Response inspector
The response inspector definition source
B7
Retrieval
Retrieval counts, thresholds, waits, truncation
B8
Budgets
All prompt budget profiles
B9
Verifier contracts
Marked ABSENT: not part of the prompt
B10
Vocabulary
The vocabulary version and its classifier sources
B11
Second analysis
INCLUDED: rendered inside B3
  • Element states — PRESENT hashes the element content; ABSENT, UNVERSIONED and INCLUDED hash a fixed marker, so the state itself is part of the identity. The bundle hash covers every element. The v19 bundle, for example, marks B2 (no dictionary) and B5 (no minimum action rules) ABSENT.
  • Checked on read — a manifest whose bundle hash does not match its elements, or two manifests of one name with different hashes, fail fast.
  • Manifest fields — besides the elements a manifest names its golden inputs, parent version, vocabulary version and compatible vocabulary versions, its rollback targets and its declared model profiles.

Choosing the bundle of a request

One selector chooses the bundle each request runs, picked by contexa.security.tiered.prompt-bundle.selector. The first prompt of an event fixes the bundle on the event, and its later prompts (Layer2 or a second pass) reuse it. A bundle version this build does not run falls back to the build default, reported once per version.

Selection sourceWhen
BUILD_DEFAULTNothing chose a bundle, the selector failed, or the chosen version is not one this build runs.
CONFIGURATIONselector=configuration (default): the version in contexa.security.tiered.prompt-bundle.active, the same for every request.
REGISTRYselector=registry: the latest ACTIVE release of the request's scope, or of PLATFORM_GLOBAL when the scope has none.
CANARYselector=registry: the scope's CANARY release, for a request inside its share and targets.
SHADOWThe scope's SHADOW release, run on sampled copies apart from the decision (see shadow runs).
Registry reads. The registry selector caches the releases of a scope for up to 60 seconds and polls for invalidations every 5 seconds; a transition or a rollback publishes one. When the registry cannot be read, the build default bundle runs until it can.

Registry states

PromptBundleState is the one lifecycle vocabulary of the registry. Older stored values are read through it, so gates, queries and screens classify a release the same way. Only ACTIVE and CANARY are enforced at runtime.

1
AVAILABLE
DRAFT
In the build with a verified manifest; not executed
2
SHADOW
EVAL_ONLY
Executed on sampled requests and only recorded
3
CANARY
CANARY
Enforced inside a scope's share
4
ACTIVE
PRODUCTION
Enforced by default
5
DEPRECATED
ROLLED_BACK
No longer selectable; a rollback target and a record
6
RETIRED
RETIRED
Removed from the build; only the record is kept

The small label under each state is the older release status name. The transition API takes either name and reads it through PromptBundleState; its receipt and the screens name the state canonically.

Moving a release to ACTIVE deprecates the other ACTIVE releases of its scope, prompt key and template, so one release is active. A release moves to DEPRECATED only while another release of its scope is active (NO_OTHER_ACTIVE_RELEASE).

Current stateAllowed next states
AVAILABLESHADOW, RETIRED
SHADOWCANARY, RETIRED
CANARYACTIVE, DEPRECATED, RETIRED
ACTIVECANARY, DEPRECATED, RETIRED
DEPRECATEDCANARY, RETIRED
RETIREDNone
Start-up registration. At start the build's bundles are registered in the PLATFORM_GLOBAL scope. A new row takes its descriptor's status, so the v19 bundle starts ACTIVE and the v20 draft starts AVAILABLE. A row declared ACTIVE starts AVAILABLE instead when the scope already has an ACTIVE release of a bundle this build runs, so a code sync never takes the default selection from a release the gate activated; an ACTIVE row of a bundle this build no longer runs does not hold the new build default back. Later syncs create missing rows and refresh code-owned content, never the operational fields.

Release gate

Every transition goes through PromptGovernanceReleaseGateService. A transition that is not allowed from the current state is blocked with ILLEGAL_STATE_TRANSITION; otherwise the gate checks the evidence the target state needs. A blocked transition changes nothing and is recorded with its blockers; an applied one sets the registry status, records the approval and rollback state, and tells the runtime caches.

TargetBlocked when
SHADOWCHANGE_LEDGER_MISSING — no change record exists
CANARYEVALUATION_NOT_FOR_BUNDLE_HASH, CHANGE_LEDGER_MISSING, MODEL_COMPATIBILITY_MISSING, BUDGET_PROFILE_MISSING, ROLLBACK_TARGET_MISSING
ACTIVEThe CANARY blockers, plus PROMPT_INJECTION_GATE_FAILED, NON_FUNCTIONAL_GATE_FAILED (latency or cost), EMPIRICAL_EVAL_MISSING, TEVV_GATE_NOT_READY (the evaluation covers the scenario families the bundle manifest declares in evaluationScenarioFamilies, or by default PROMPT_INJECTION, TOOL_MISUSE and MEMORY_POISONING), TOKEN_COUNTING_POLICY_MISSING, CACHE_POLICY_MISSING, COMPACTION_POLICY_MISSING
DEPRECATEDROLLBACK_TARGET_MISSING

EVALUATION_NOT_FOR_BUNDLE_HASH means the evaluation on record was not made with this bundle's hash: the evidence of a bundle is the evaluation of that bundle.

POST /contexa/admin/api/prompt-quality/releases/transition
Content-Type: application/json

{
  "registryScope": "PLATFORM_GLOBAL",
  "promptKey": "cortex.security-decision",
  "promptVersion": "<bundle version>",
  "targetReleaseStatus": "EVAL_ONLY",
  "rationale": "Open the shadow run of the candidate bundle"
}

The response is the transition receipt: 200 when applied, 422 with the blockers when blocked. A blank registryScope means PLATFORM_GLOBAL.

Shadow runs and canaries

Shadow run

A shadow run tries a candidate bundle on real requests without letting it decide anything. It is read from the registry's SHADOW release whichever selector chooses the decision's bundle, so a host that runs its configured bundle can still try a candidate.

  • Sampling — a request is in the sample by a stable hash of its event ID below contexa.security.tiered.prompt-bundle.shadow.sample-percent (0 to 100; 0, the default, runs none). The shadow runner exists only when this property is set.
  • Daily cap — at most shadow.daily-call-cap runs (default 100) per UTC day, one model call each.
  • Apart from the decision — the event is copied before the decision, analysed after it on its own executor, and sealed as the SHADOW stage. The copy never reaches the enforcement handler, so nothing is enforced or learned, and it adds nothing to the session context.
  • Only runnable bundles — a SHADOW release of a version this build does not run is skipped.

Canary

With selector=registry, a CANARY release runs for the requests inside its share; the others run the latest ACTIVE release. The share is the registry row's canary_percent (0 to 100; no value means the whole scope), applied to a stable hash of the requester (the user, else the session, else the event). Optional canary_tenants and canary_resource_families narrow the canary to those tenants and to requests whose resource address starts with, or whose resource ID equals, a listed family.

Rollback

PromptBundleRollbackService switches the executing bundle of a scope at once to a rollback target its manifest declares. The source becomes DEPRECATED, the target ACTIVE, the runtime caches are told, and the execution is recorded with the conditions it was checked against. A refused rollback is recorded with its reasons and changes nothing.

RefusalCondition
SOURCE_NOT_EXECUTINGThe source version is not ACTIVE or CANARY in the scope
TARGET_NOT_IN_BUILDThe target's manifest is not in this build
TARGET_NOT_DECLAREDThe source manifest does not list the target in rollbackTargets
VOCABULARY_INCOMPATIBLEThe target's manifest does not read the current vocabulary version
TARGET_NOT_REGISTEREDThe target has no registry row in the scope
POLICY_SET_MISSINGThe target's policy set is missing (a host without policy sets records NOT_APPLICABLE)
POST /contexa/admin/api/prompt-quality/releases/rollback
Content-Type: application/json

{
  "registryScope": "PLATFORM_GLOBAL",
  "promptKey": "cortex.security-decision",
  "fromVersion": "<executing version>",
  "toVersion": "<rollback target>",
  "reason": "Decision quality dropped after the release",
  "emergency": false
}

The result carries an execution key (RBX-...), its status and the checked conditions: EXECUTED, or REFUSED with HTTP 422. An emergency stop ("emergency": true) runs the same way and ends AWAITING_APPROVAL; another administrator then approves it with POST .../rollback/{executionKey}/approve. The operator who ran it cannot approve it.

Prompt inspector

PromptInspector reads the user prompt the model got from a sealed evidence package and runs each check over it, counting what matched, what did not and what could not be checked. It points at problems for a person to review; it never changes a prompt, a decision or a rule. The automatic round runs on the scheduler's thread under one lock across instances, inspecting the oldest uninspected packages up to the batch size; no decision thread runs it.

CheckLayoutsWhat it compares
SEAL_INTEGRITYAllThe package hash covers every sealed field, the system and user prompts were captured and match their hashes, a difference from the rendered prompt is recorded, and the request and correlation IDs are there.
BUNDLE_TRACEAllThe sealed descriptor names a registry release, and its contract version, artifact hash and approval are that release's. Without a release registry every trace is unverifiable.
DICTIONARY_CONTRACTv20Line feeds, block headers in dictionary order, only entry lines of their block in order, values in the entry's form or missing text, numbered lines up to the cap, and fact IDs in drawn order.
DECISION_FACT_MATCHv20Every sealed decision fact the dictionary draws has its line, its value letter for letter and the fact ID sealed with it.
FACT_ACCURACYv20The facts the prompt draws against their sealed source, field by field and not by rendering the prompt again: in the REQUEST block the subject, time, roles, permissions, action, target, size, class, network and device. A line whose source was not sealed is counted as unverifiable.
UNKNOWN_DECLAREDv20Every fact the engine could not establish is on the UNKNOWN line with its kind, and no fact named there is drawn with a value elsewhere. A missing value is drawn the way the dictionary notes it: an entry with a rendered missing text draws it on its own line (unless the render’s own rule names the fact unknown, for example without a session); an entry the dictionary leaves out is not named on the UNKNOWN line unless the caller marked it so (past records only when the sealed search did not retrieve them); any other entry has its line or its name on the UNKNOWN line.
PAST_RECORD_NOTATIONv20The PAST RECORDS state line counts the records drawn, each record line has its twelve fields, and the order is same target first, then the latest first.
SOURCE_TRUTHv20The facts against the host's own records, given by a host PromptFactSourceVerifier. Without one the check records that no host source is integrated.
SAME_FACTv20One fact the prompt states twice has one value.
REASON_MATCHv20Each reason code of the sealed decision cites an ID on a line of the prompt, that line is one the code cites, and the code's condition holds on the sealed context.
  • Layouts — the v20 checks run only on packages drawn as v20-dictionary-blocks; a v20 package drawn with another bundle's dictionary or render is counted as unverifiable for those checks.
  • Verdicts — PASS (every item matched), FAIL (at least one mismatch), INCOMPLETE (no mismatch, some items unverifiable), NOT_APPLICABLE (not a dictionary prompt; only the layout-free checks ran), ERROR (the package could not be inspected).
  • Findings — MISMATCH or UNVERIFIABLE; an unverifiable item never counts as passing.
  • Versions — every result names the inspector version (prompt-inspector/N) and the dictionary version (v20@ and the head of the dictionary digest). A new inspector version inspects the packages again.

Sealed evidence sampling and retention

In an OSS installation (contexa.enterprise.enabled not true) with capture on (contexa.pqa.oss.sealed-evidence.capture-enabled, default true), decisions are sealed into evidence packages: the system and user prompts as sent, the canonical context and the execution metadata. Enterprise keeps sealed packages in its own schema, so the OSS inspector beans are not created while Enterprise is enabled.

  • Window after a change — the first full-capture-after-change decisions (default 1000) after a start, or after the active bundle changes, are all sealed.
  • Sample — after the window, sample-percent (default 1, 1 to 100) of the requests are sealed, by a stable hash of the correlation ID.
  • Retention — a package is kept for retention.period (default 90 days); one whose latest inspection found nothing wrong is removed after retention.clean-period (default 30 days). A package whose inspection found something keeps the default retention.
  • Inspection sample — contexa.prompt-inspection.sample-percent (default 100) of the sealed packages are inspected, by a stable hash of the package ID.

Console and API

Every screen and endpoint requires the ADMIN role.

MethodPathPurpose
GET/contexa/admin/prompt-inspectionInspection overview screen (also /packages and /packages/{packageId})
GET/contexa/admin/api/prompt-inspection/overviewCounts of a period (from, to; the last 7 days by default)
GET/contexa/admin/api/prompt-inspection/packagesInspected packages, filtered by verdict, check code, block and entry
GET/contexa/admin/api/prompt-inspection/packages/{packageId}One package with its findings
POST/contexa/admin/api/prompt-inspection/packages/{packageId}/reinspectInspect one package again
POST / GET/contexa/admin/api/prompt-inspection/reinspectionsAsk the automatic round to inspect a capture period again, and read its progress
GET/contexa/admin/api/prompt-quality/releasesRegistry releases (registryScope optional)
POST/contexa/admin/api/prompt-quality/releases/transitionA state transition through the release gate
POST/contexa/admin/api/prompt-quality/releases/rollbackA rollback or emergency stop
POST/contexa/admin/api/prompt-quality/releases/rollback/{executionKey}/approveThe second approval of an emergency stop

Configuration

PropertyTypeDefaultDescription
contexa.security.tiered.prompt-bundle.activeString(blank)The bundle version the configuration selector runs; blank runs the build default.
contexa.security.tiered.prompt-bundle.selectorEnumCONFIGURATIONCONFIGURATION or REGISTRY.
contexa.security.tiered.prompt-bundle.shadow.sample-percentint0Share of requests a shadow release runs on, 0 to 100.
contexa.security.tiered.prompt-bundle.shadow.daily-call-capint100Shadow runs allowed per UTC day.
contexa.prompt.governance.enabledbooleantrueRegistry, release gate, rollback and release API on or off together.
contexa.prompt-inspection.enabledbooleantrueThe inspector, its round, re-inspection and console (OSS only).
contexa.prompt-inspection.intervalDurationPT30SDelay between automatic rounds.
contexa.prompt-inspection.initial-delayDurationPT30SDelay before the first round.
contexa.prompt-inspection.batch-sizeint50Packages per round (at least 1).
contexa.prompt-inspection.sample-percentint100Share of sealed packages inspected, 1 to 100.
contexa.pqa.oss.sealed-evidence.capture-enabledbooleantrueSeal decisions into evidence packages (OSS only).
contexa.pqa.oss.sealed-evidence.sample-percentint1Share of requests sealed after the change window, 1 to 100.
contexa.pqa.oss.sealed-evidence.full-capture-after-changeint1000Decisions sealed in full after a start or a bundle change.
contexa.pqa.oss.sealed-evidence.retention.enabledbooleantrueRun the retention jobs.
contexa.pqa.oss.sealed-evidence.retention.periodDuration90dHow long a sealed package is kept.
contexa.pqa.oss.sealed-evidence.retention.clean-periodDuration30dHow long a package whose latest inspection found nothing wrong is kept.
contexa:
  security:
    tiered:
      prompt-bundle:
        selector: registry
        shadow:
          sample-percent: 5
          daily-call-cap: 100
  prompt-inspection:
    batch-size: 50
    sample-percent: 100
  pqa:
    oss:
      sealed-evidence:
        sample-percent: 1
        full-capture-after-change: 1000

The minimum action rule and prompt budget settings are listed in AI Engine Configuration.