core/docs/identity_packs.md
Shay 07ad3af845 feat(surface): ADR-0031 — score-decomposition surface (per-axis hedges)
Closes the 'identity hedges are generic' gap.  When IdentityCheck reports
that a specific axis is deviating AND the pack supplies an axis_hedges
entry for that axis, the assembler uses that axis's phrase instead of
ADR-0028's generic preferred_hedge_*.  The hedge text now names what is
actually at issue.

Selection: lex-smallest axis_id in (ctx.deviation_axes ∩ axis_hedges).
Deterministic; loader emits axis_hedges in lex order on axis_id.

Example surface at alignment=0.30 (strong band) under default pack:
  No deviation             → 'It seems that truth reveals reality.'
  truthfulness deviates    → 'Evidence is thin that truth reveals reality.'
  coherence deviates       → 'This does not yet cohere: truth reveals reality.'
  reverence deviates       → 'Reports suggest truth reveals reality.'

Same trajectory + truthfulness deviation, three different packs:
  default_general_v1   → 'Evidence is thin that truth reveals reality.'
  precision_first_v1   → 'The evidence does not support that truth reveals reality.'
  generosity_first_v1  → 'Truth reveals reality.'  (above generosity's strong=0.20)

Schema (additive, optional):
  surface_preferences.axis_hedges = {
    <axis_id>: { 'strong': str, 'soft': str, 'qualifier': str },
    ...
  }

Bounds: each phrase length 1–64; axis_id non-empty.  Absent block →
ADR-0028 byte-for-byte fallback.  Loader emits pairs in lex order on
axis_id for hashability + deterministic tie-break.

Files:
  core/physics/identity.py
    + class AxisHedge (frozen: strong, soft, qualifier)
    SurfacePreferences gains axis_hedges: Tuple = ()
  packs/identity/loader.py
    + _build_axis_hedges(): parse + bounds-check + emit lex-ordered tuple
  generate/surface.py
    SurfaceContext gains deviation_axes: frozenset[str] + axis_hedges tuple
    + _axis_specific_phrase(ctx): lex-smallest match or None
    _apply_hedge consults axis-specific phrase before ADR-0028 fallback
    Depth languages (he, grc) unchanged — ADR-0030 canonical phrases
  chat/runtime.py
    _build_surface_context lifts identity_score.deviation_axes and
    prefs.axis_hedges into SurfaceContext
  packs/identity/*.json
    Three v1 packs gain axis_hedges blocks (truthfulness, coherence,
    reverence — each pack uses voice consistent with its character)
  scripts/ratify_identity_packs.py (no change — idempotent)
  packs/identity/*.mastery_report.json
    Auto-refreshed.  New SHAs:
      default_general_v1   → 2ab7d469013509ba5030313ca9a609a443d0716e3ddcc5596f59858ce054f5d3
      precision_first_v1   → 78aa1e6a68a35c2c8576b6196a52d421b94f6d11e006128986902a4fd08679af
      generosity_first_v1  → 511f1ce20edd4266239da61443bfc93473a5433f20bfee6692a25a03073dc933

Tests: tests/test_identity_score_decomposition.py — 17 new tests:
  per-axis phrase selection, band gating still applies, pack swap with
  same deviation produces three different phrases, lex tie-break is
  deterministic, depth-language fallback to ADR-0030, backward compat
  with empty deviation_axes, and the contract that all three v1 packs
  ship axis_hedges for all three default-pack axes.

Suite status (all green):
  cognition 121, teaching 17, runtime 19, formation 182, smoke 67
  identity+safety+English+depth divergence 71
  score decomposition 17

Scope limits (documented in ADR-0031):
  - English-only at v1 (depth languages use canonical ADR-0030 phrases)
  - Lex tie-break is operational not semantic — pack authors can re-key
    if they need a different priority
  - No dominance-driven phrasing (Interpretation A); preserved as
    forward-compatible follow-up

Docs: ADR-0031 (Accepted) recorded; docs/identity_packs.md gains
§Axis-specific hedge phrases section and updated v1-pack SHAs; memory
'identity-packs.md' refreshed.
2026-05-17 20:16:22 -07:00

208 lines
12 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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:** [`decisions/ADR-0027-identity-packs.md`](decisions/ADR-0027-identity-packs.md), [`teaching_order.md`](teaching_order.md), [`runtime_contracts.md`](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/<pack_id>.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: `<slug>_v<major>`. |
| `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 `<pack_id>.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 164). 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](decisions/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 `<pack_id>.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 <subject_id>` 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 `<pack_id>.json` and `<pack_id>.mastery_report.json` to `packs/identity/`.
5. **Deploy.** The pack is now selectable by `--identity <pack_id>`. 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](decisions/ADR-0028-identity-surface-wiring.md) + [ADR-0030](decisions/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](decisions/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`.