# Identity Packs — Reference **Status:** Operational reference doctrine. Update when pack format, loader contract, or CLI flag semantics change. **Last updated:** 2026-05-17 **Companion docs:** [`adr/ADR-0027-identity-packs.md`](adr/ADR-0027-identity-packs.md), [`teaching_order.md`](teaching_order.md), [`specs/runtime_contracts.md`](specs/runtime_contracts.md) ## What an identity pack is An identity pack is the on-disk, content-addressed representation of an `IdentityManifold`. At runtime startup, CORE loads exactly one identity pack and uses it to construct the manifold that drives `PersonaMotor.from_identity_manifold()` and `IdentityCheck`. Replacing the pack replaces the model's identity surface without touching code. Identity packs sit alongside language packs in the trust hierarchy: - **Language packs** (`packs/en/`, `packs/grc/`, `packs/he/`, …) — what CORE *speaks*. - **Identity packs** (`packs/identity/.json`) — *who* CORE is while speaking. - **Safety packs** (future, `packs/identity_safety/`) — what CORE will *never* be, regardless of identity pack. ## Pack format (v1) A single JSON file. Strings, ints, bools, lists, dicts only — same canonical-JSON discipline as the formation pipeline (no floats embedded in identifying fields; numeric direction vectors are floats but their canonical position in the file is fixed). ```json { "pack_id": "default_general_v1", "version": "1.0.0", "description": "Balanced general identity. Default shipping pack.", "schema_version": "1.0.0", "mastery_report_sha256": "", "alignment_threshold": 0.45, "boundary_ids": [ "no_fabricated_source", "no_hot_path_repair" ], "value_axes": [ { "axis_id": "truthfulness", "name": "truthfulness", "direction": [1.0, 0.0, 0.0], "weight": 1.0, "theological_note": "Truth is treated as a fixed value axis, not a prompt preference." }, { "axis_id": "coherence", "name": "coherence", "direction": [0.0, 1.0, 0.0], "weight": 1.0, "theological_note": "Operations must preserve field coherence under propagation." }, { "axis_id": "reverence", "name": "reverence", "direction": [0.0, 0.0, 1.0], "weight": 1.0, "theological_note": "Depth-language handling remains bounded by source structure." } ] } ``` ### Field semantics | Field | Required | Meaning | |---|---|---| | `pack_id` | yes | Unique identifier. Convention: `_v`. | | `version` | yes | Semver. Bumping `major` produces a new `pack_id`. | | `description` | yes | Human-facing one-liner. Surfaces in `core pulse --list-identity-packs`. | | `schema_version` | yes | Format version. Currently `"1.0.0"`. | | `surface_preferences` | no | Pack-supplied surface hedge / claim-strength shaping (ADR-0028). Defaults preserve pre-ADR behavior. See §"Surface preferences" below. | | `mastery_report_sha256` | no | SHA of the companion `.mastery_report.json`. Empty for unratified development packs; production deployments refuse to load packs with empty values. | | `alignment_threshold` | yes | Float in [0, 1]. Passed to `IdentityManifold.alignment_threshold`. | | `boundary_ids` | yes | List of boundary identifiers. Mirrors `IdentityManifold.boundary_ids`. | | `value_axes` | yes | List of ≥ 1 axes. Each has: `axis_id`, `name`, `direction` (list of 3 floats in [-1, 1]), `weight` (float ≥ 0), `theological_note`. | ### Axis-specific hedge phrases (ADR-0031) Optional sub-block inside `surface_preferences` that lets the assembler call out *which* axis is deviating when it hedges. When `IdentityScore.deviation_axes` names an axis and the pack supplies an `axis_hedges` entry for that axis, the lex-smallest match is used instead of the generic `preferred_hedge_*`: ```json "axis_hedges": { "truthfulness": { "strong": "Evidence is thin that", "soft": "It is hard to confirm that", "qualifier": "Where evidence is partial," }, "coherence": { "strong": "...", "soft": "...", "qualifier": "..." }, "reverence": { "strong": "...", "soft": "...", "qualifier": "..." } } ``` Each axis entry must supply `strong`, `soft`, and `qualifier` phrases (length 1–64). When no deviating axis matches an `axis_hedges` entry, the generic phrases from §"Surface preferences" fire. Depth languages (Hebrew, Koine Greek) ignore `axis_hedges` at v1 and continue to use the canonical phrases from [ADR-0030](adr/ADR-0030-depth-language-hedge.md). ### Surface preferences (ADR-0028) Optional block driving the assembler's hedge and claim-strength decisions: ```json "surface_preferences": { "hedge_threshold_strong": 0.40, "hedge_threshold_soft": 0.50, "preferred_hedge_strong": "It seems that", "preferred_hedge_soft": "Perhaps", "claim_strength": "balanced", "qualified_band_high": 0.75, "preferred_qualifier": "In some cases," } ``` Bands (in descending hedge strength): 1. `alignment < hedge_threshold_strong` → prepend `preferred_hedge_strong`. 2. `alignment < hedge_threshold_soft` → prepend `preferred_hedge_soft`. 3. `hedge_threshold_soft <= alignment < qualified_band_high` and `claim_strength == "qualified"` → prepend `preferred_qualifier`. 4. Otherwise leave the assertion bare. Threshold ordering required: `hedge_threshold_strong <= hedge_threshold_soft <= qualified_band_high`. Loader enforces this. `claim_strength` must be one of `{"balanced", "qualified", "affirmative"}`. `"balanced"` and `"affirmative"` skip the marginal-band qualifier; only `"qualified"` triggers it. ### Loader bounds (enforced) - `len(value_axes) >= 1` — empty axes are refused. - Each `direction` must have length 3 and each component in `[-1.0, 1.0]`. - `weight` must be in `[0.0, 10.0]` — prevents a single axis from dominating arbitrarily. - `alignment_threshold` must be in `[0.0, 1.0]`. - `axis_id` values must be unique within a pack. - Production mode requires `mastery_report_sha256 != ""` and the companion report's self-seal to verify; development mode (`CORE_ALLOW_UNRATIFIED_IDENTITY=1`) bypasses both. ## Loader contract ```python from packs.identity.loader import load_identity_manifold manifold = load_identity_manifold( pack_id="default_general_v1", # required search_paths=None, # default: ["./packs/identity"] require_ratified=True, # production default ) ``` Returns an `IdentityManifold` (from `core/physics/identity.py`). Raises `IdentityPackError` on missing pack, malformed JSON, bound violations, or unverified self-seal in production mode. The loader is path-aware: deployments may supply `search_paths=("/srv/myapp/packs/identity", "./packs/identity")` so a robotics or app builder can ship overlay packs without touching CORE's own packs directory. ## CLI usage ```bash core chat # Loads the default identity pack (currently default_general_v1). core chat --identity precision_first_v1 # Loads a specific pack. Pack must exist on the loader's search paths. core chat --list-identity-packs # Lists discoverable packs with description + ratification status. # Short-circuits before the REPL launches. core chat --list-identity-packs --json # Same listing as machine-readable JSON (pack_id, version, description, # ratified, path). core chat --identity generosity_first_v1 # A different specialization on the chat surface. CORE_DEFAULT_IDENTITY_PACK=precision_first_v1 core pulse "..." # Environment override of the default. Takes precedence over the # core/config.py constant; --identity on the command line takes # precedence over the env var. ``` ## Shipping packs (v1) | Pack id | Role | Notes | |---|---|---| | `default_general_v1` | Ship default. Balanced. | Encodes the *exact* three axes (`truthfulness`, `coherence`, `reverence`) previously hardcoded in `chat/runtime.py`. Behavioral no-op vs. pre-ADR runtime. ADR-0028 surface_preferences: balanced; hedge thresholds 0.40/0.50/0.75. ADR-0031 axis_hedges: "Evidence is thin that" / "This does not yet cohere:" / "Reports suggest". Ratified: `2ab7d469013509ba5030313ca9a609a443d0716e3ddcc5596f59858ce054f5d3`. | | `precision_first_v1` | Specialization example A. | Boosts `truthfulness` weight, narrows reverence direction. Surface: hedges sooner (0.55/0.70/0.85), uses "Arguably,"/"In some cases,"/"Under certain conditions,"; claim_strength=qualified. ADR-0031 axis_hedges: "The evidence does not support that" / "This contradicts what is established:" / "Source attestation is weak:". Source: `evals/identity_divergence/axes/axis_a.yaml`. Ratified: `78aa1e6a68a35c2c8576b6196a52d421b94f6d11e006128986902a4fd08679af`. | | `generosity_first_v1` | Specialization example B. | Boosts `coherence` weight, broadens reverence direction. Surface: hedges later (0.20/0.30/0.50); claim_strength=affirmative. ADR-0031 axis_hedges: "Some hold that" / "There is a thread connecting this:" / "It is reported that". Source: `evals/identity_divergence/axes/axis_b.yaml`. Ratified: `511f1ce20edd4266239da61443bfc93473a5433f20bfee6692a25a03073dc933`. | Each ratified pack ships alongside a `.mastery_report.json` companion file. The loader, in production mode, verifies the companion's self-seal and cross-checks its `report_sha256` against the pack's `mastery_report_sha256`. To re-ratify after editing a pack's axes, run `python scripts/ratify_identity_packs.py` (idempotent — re-running on already-current packs is a no-op). ## Authoring a new identity pack (robotics / personalization / creative tools) 1. **Author the SubjectSpec.** Use `core formation new ` to scaffold; edit to declare the pack's intent and identity axis constraints. 2. **Hand-author the candidate axes.** Use the `identity_anchor` template's expected input shape: `concepts` are axes (with `definition` = behavioral commitment), `counters` are override-attempt probes the pack must refuse. 3. **Ratify through formation.** Render → compose → compile → run → ratify. Produces a signed `MasteryReport`. 4. **Promote.** Promotion goes through `teaching/review.py`'s reviewed-apply path. The promote step writes both `.json` and `.mastery_report.json` to `packs/identity/`. 5. **Deploy.** The pack is now selectable by `--identity `. Distribute alongside your deployment's other artifacts. ### Anti-patterns - **Don't author identity packs by hand-editing `packs/identity/`.** The runtime never writes there; neither should authors. All packs flow through formation so audit trails are intact. - **Don't ship unratified packs (empty `mastery_report_sha256`) in production.** The loader's `require_ratified` flag exists to refuse them. - **Don't try to override `boundary_ids` to weaken refusal.** Boundaries are the immutable contract; if your identity pack omits expected boundaries, the runtime refuses to load it. - **Don't try to express safety constraints in an identity pack.** Safety axes belong in the (future) safety pack, always-loaded and never-replaceable. ## Known limits (read before designing around) 1. ~~**Identity does not yet visibly differentiate articulation at the realizer.**~~ **Closed by [ADR-0028](adr/ADR-0028-identity-surface-wiring.md) + [ADR-0030](adr/ADR-0030-depth-language-hedge.md) (2026-05-17).** Pack `surface_preferences` now flow into the English, Hebrew, and Koine Greek assemblers. `core chat --identity precision_first_v1 "Q"` produces a visibly different surface than the default pack on the same prompt at the same alignment, *across all three foundational languages*. Per-pack depth-language phrase overrides remain a future concern (today's depth-language hedge phrases are canonical defaults in `generate/surface.py::_DEPTH_HEDGE_PHRASES`). 2. **One pack at a time.** Multi-pack overlays (`--identity general,domain_medical`) are deferred to a follow-up ADR. 3. **No language-specific identity yet.** Packs are language-neutral. Per-language identity is a future concern. 4. **Safety axes are still in `chat/runtime.py`.** Once the safety pack ADR lands, safety boundaries will move out of `boundary_ids` and into a separately-loaded safety pack. ## Cross-reference index - Pack format spec: this doc §"Pack format (v1)". - Loader contract: this doc §"Loader contract". - Decision record: [ADR-0027](adr/ADR-0027-identity-packs.md). - Teaching-order placement: [`teaching_order.md`](teaching_order.md) §"The Five-Layer Ordering Rule" Layer 1. - Identity-divergence eval: `evals/identity_divergence/contract.md`. - The geometric identity primitives: `core/physics/identity.py` (ADR-0010 implicit). - The formation template that ratifies packs: `formation/templates/identity_anchor.py`.