Skip to content

feat(persona): typed Engram + admission membrane types (#1121 PR-1)#1129

Merged
joelteply merged 2 commits into
canaryfrom
feat/persona-engram-types
May 13, 2026
Merged

feat(persona): typed Engram + admission membrane types (#1121 PR-1)#1129
joelteply merged 2 commits into
canaryfrom
feat/persona-engram-types

Conversation

@joelteply
Copy link
Copy Markdown
Contributor

Summary

PR-1 of the AIRC inbox → cognition-admission → engram-storage bridge described in #1121 and elaborated in today's airc design discussion (Joel + Codex + claude tab #1).

Pure value types only — NO Recipe impl, NO admission gate logic, NO PersonaInbox wiring, NO ORM persistence. Subsequent PRs layer those over these types.

What ships

Type Purpose
`Engram` The storable memorable cognition unit (id, kind, content, origin, recall_keys, admitted_at_ms, trust_state_at_admission, admission_trace_id)
`EngramKind` Episodic / Semantic / Procedural / SelfReflection — biological-memory analogs as single discriminator
`EngramOrigin` Variant-typed provenance: `Airc(AircMessageRef)` / `Chat(ChatMessageRef)` / `Tool(ToolInvocationRef)` / `SelfReflection { parent_engram_id }`
`AircMessageRef` Protocol-compatible AIRC reference (transport, room_id, message_id, sender_id, timestamps, content_hash, signature, proof_refs, schema_version, optional client_name)
`ChatMessageRef` + `ToolInvocationRef` Sibling reference types for non-AIRC origins
`AdmissionDecision` Three terminal outcomes: `Admit` / `Drop` / `Quarantine` (forensic-not-destructive)
`AdmissionDropReason` Typed reasons for intentional rejection: NotMemorable / PolicyDeniedAdmission / Duplicate
`AdmissionError` thiserror typed failure modes for the admission machinery itself: EnvelopeVerificationFailed / TrustBoundaryRejected / ReplayDetected / RecipeFailure / UnsupportedSchemaVersion
`TrustState` Source policy/trust tier (Untrusted / Authenticated / Knocker / ApprovedPeer / IntragridMember / SocMember / SelfTrust) — ordered for `source_trust >= threshold` comparisons

Joel's protocol-not-client rule

`AircMessageRef` is a protocol-compatible reference, not a binding to the airc CLI. Per Joel 2026-05-13 (relayed by Codex): Continuum accepts AIRC data by proof/contract, not client identity. Any producer emitting valid envelopes (correct `transport=airc`, `signature`, `sender_id`, etc.) is acceptable. `client_name` is optional and informational only — never load-bearing for trust decisions. Tests verify that None and non-airc-CLI client_names are both accepted at the type level.

`TrustState` similarly models policy/trust of the SOURCE, not which implementation produced the data.

Cross-references

Test plan

  • 20/20 tests pass: serde roundtrips for every type + discriminator-tag verification + thiserror Display + ordering checks + optional/non-airc-CLI client_name acceptance + 10 ts-rs export_bindings tests
  • All 10 ts-rs TypeScript files materialize under `src/shared/generated/persona/` (Engram.ts, EngramKind.ts, EngramOrigin.ts, AircMessageRef.ts, ChatMessageRef.ts, ToolInvocationRef.ts, AdmissionDecision.ts, AdmissionDropReason.ts, AdmissionError.ts, TrustState.ts)
  • Workspace-style: u64 epoch ms timestamps + `#[ts(type = "string")]` Uuid fields (matches PersonaInboxFrame, cognition_io patterns)
  • Secrets audit clean (no personal/network identifiers)
  • Module wired into `persona/mod.rs` with all type re-exports
  • Plan + scope acked on AIRC by Codex (multiple coordination rounds, scope refined per Joel's protocol-not-client + TrustState-models-policy-not-brand rules)

Deferred to follow-up PRs

  • PR-2: AircEvent envelope + IsMemorable Recipe impl + admission gate logic (the cognition that produces these types' values)
  • PR-3: PersonaInbox / PersonaInboxFrame wiring (the integration)
  • PR-4: Engram ORM persistence path
  • PR-5: Recall surface (engrams → RAG context)

🤖 Generated with Claude Code

PR-1 of the AIRC inbox → cognition-admission → engram-storage bridge
described in continuum#1121 and elaborated in today's airc design
discussion (Joel + Codex + claude tab #1).

Pure value types only — NO Recipe impl, NO admission gate logic, NO
PersonaInbox wiring, NO ORM persistence path. Subsequent PRs layer
those over these types.

Adds:
- Engram { id, kind, content, origin, recall_keys, admitted_at_ms,
  trust_state_at_admission, admission_trace_id } — the storable unit
- EngramKind { Episodic, Semantic, Procedural, SelfReflection } —
  biological-memory analogs as a single discriminator (vs separate
  types per kind, which composes badly)
- EngramOrigin enum { Airc(AircMessageRef), Chat(ChatMessageRef),
  Tool(ToolInvocationRef), SelfReflection { parent_engram_id } } —
  variant-typed provenance so each origin's identity primitive is
  type-system-enforced
- AircMessageRef — protocol-compatible reference (transport=airc,
  room_id, message_id, sender_id, sent_at_ms, received_at_ms,
  content_hash, signature, proof_refs, schema_version, client_name).
  Per Joel 2026-05-13: continuum accepts AIRC data by proof/contract,
  NOT by client identity. Official airc CLI is not privileged;
  client_name is informational only and never load-bearing for trust
  decisions. Any producer emitting valid envelopes is acceptable.
- ChatMessageRef + ToolInvocationRef — sibling reference types
- AdmissionDecision { Admit, Drop, Quarantine } — three terminal
  outcomes from the admission gate. Quarantine is forensic-not-
  destructive (per cognitive-immune-model #1122 §3.8) — preserves
  candidate without admitting to live recall surface
- AdmissionDropReason { NotMemorable, PolicyDeniedAdmission,
  Duplicate } — typed reasons (categorized intentional rejection)
- AdmissionError { EnvelopeVerificationFailed, TrustBoundaryRejected,
  ReplayDetected, RecipeFailure, UnsupportedSchemaVersion } —
  thiserror typed failure modes for the admission machinery itself.
  Per Joel's no-fallback rule and the no-try/catch-in-execute
  discipline: errors are returned not swallowed. Same shape as
  NoLocalModelLoadable (#1089) and NoMultimodalBase (#1074).
- TrustState { Untrusted, Authenticated, Knocker, ApprovedPeer,
  IntragridMember, SocMember, SelfTrust } — models policy/trust of
  source, NOT implementation brand (per Joel 2026-05-13). Ordered
  with PartialOrd so admission gates can compare
  source_trust >= threshold directly.

Convention notes:
- Uuid fields use #[ts(type = "string")] — matches existing pattern
  in cognition_io.rs / channel_items.rs
- Timestamps are u64 epoch ms with #[ts(type = "number")] — matches
  existing PersonaInboxFrame.oldest_timestamp pattern. Workspace
  chrono crate doesn't have serde feature enabled by default and
  the persona modules use the u64-epoch shape consistently
- All types ship with #[derive(TS)] + export_to ../../../shared/generated/persona/<TypeName>.ts
- ts-rs export triggered via explicit export_bindings_<typename> tests
  per the gpu/memory_manager.rs pattern

Validation:
- 20/20 tests pass: serde roundtrips for every type, discriminator-
  tag verification for tagged enums, thiserror Display + serde
  paths, TrustState ordering for threshold comparison, optional
  client_name (None + non-airc-CLI value both accepted), all 10
  ts-rs export_bindings tests
- 10 generated TypeScript files materialize under
  src/shared/generated/persona/ (Engram.ts, EngramKind.ts,
  EngramOrigin.ts, AircMessageRef.ts, ChatMessageRef.ts,
  ToolInvocationRef.ts, AdmissionDecision.ts, AdmissionDropReason.ts,
  AdmissionError.ts, TrustState.ts)

Deferred to follow-up PRs:
- PR-2: AircEvent envelope + IsMemorable Recipe impl + admission gate
  logic (the cognition that produces these types' values)
- PR-3: PersonaInbox / PersonaInboxFrame wiring (the integration)
- PR-4: Engram ORM persistence path
- PR-5: Recall surface (engrams → RAG context)

Pairs with cognitive-immune-model (#1122) — the storage substrate
those defenses operate over. Pairs with forge-alloy proof contracts
(#1119) — same typed-Rust-with-ts-rs-export discipline applied to
the runtime cognition layer.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@joelteply joelteply merged commit f6c25bf into canary May 13, 2026
3 checks passed
@joelteply joelteply deleted the feat/persona-engram-types branch May 13, 2026 21:18
joelteply added a commit that referenced this pull request May 13, 2026
* feat(persona): IsMemorable Recipe + admission gate (#1121 PR-2) (#1133)

Layers the admission policy machinery over the storage-shape types
shipped in PR-1 (#1129). Splits cleanly into two responsibilities:

- Gate (structural) — `AdmissionGate::admit()` runs prereqs that are
  independent of any specific persona's policy: envelope structure
  verification, trust-tier threshold check, replay protection.
  Failures here return typed `AdmissionError` variants, never silent
  drops. Always emits a `SEAM_ADMISSION` trace entry so forensics see
  the gate ran (matches `recorder.rs`'s always-call-record_turn
  discipline).

- Recipe (policy) — `IsMemorable` trait. Implementations decide whether
  a candidate that *passed* the structural prereqs should be admitted,
  dropped, or quarantined. Different personas plug in different recipes
  (a fuzzy/agent persona uses permissive `HeuristicIsMemorable`; a SOC
  governance persona will use a strict policy-driven recipe in PR-3+).

Ships the v1 default `HeuristicIsMemorable`: dedup → length → noise
phrase → admit. No quarantine outcome (binary on inputs); the first
quarantine-emitting recipe will be a similarity-based one in a later PR.

`AdmissionConfig::permissive_v1()` (Authenticated threshold, 24h
quarantine TTL) and `AdmissionConfig::strict_v1()` (IntragridMember
threshold, 1h TTL) cover the two starting positions.

20/20 unit tests cover: every `AdmissionError` path (envelope, trust,
replay, recipe failure, schema version), heuristic policy decisions
(short / noise / duplicate / admit-with-provenance), trace seam
emission invariant (every path emits exactly one SEAM_ADMISSION),
recipe-error/quarantine propagation, config preset thresholds, and
ts-rs binding generation for the two TS-exported types
(AdmissionCandidate, AdmissionConfig).

Pure value layer; no PersonaInbox wiring, no ORM persistence, no AIRC
event-converter — those land in PR-3+ on top of this gate. Pairs with:
- persona::engram (storage-shape types from #1129)
- persona::trace (SEAM_ADMISSION constant added here)
- docs/grid/COGNITIVE-IMMUNE-MODEL.md (defense posture this gate
  participates in: apoptosis-cheaper-than-corruption, B-cell anergy,
  forensic-not-destructive)

Card: continuum#1133. Parent design: continuum#1121.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(admission): pre-normalize noise phrases + symmetric envelope tests

Folds in two review nits from claude-tab-2 on continuum#1134:

1. **Pre-normalize noise phrases** at construction time (lowercased +
   trimmed) so `IsMemorable::evaluate` doesn't re-lowercase 8 phrases
   per call. Heuristic recipes are the per-message hot path; this was
   wasted work. Adds `HeuristicIsMemorable::with_noise_phrases<I, S>(
   min_len, phrases)` constructor that does the normalization once;
   `default_v1()` routes through it.

2. **Symmetric envelope-empty-field tests**. Coverage previously had
   only `empty_signature_returns_envelope_verification_failed`; the
   `content_hash` and `schema_version` empty-field branches in
   `verify_airc_envelope` were uncovered. Asymmetric coverage lets one
   of the three regress silently. Adds the matching two tests.

Tests: 22/22 persona::admission pass (was 20). No behavior change for
admit/drop decisions; same envelope structural failures, same trust /
replay / recipe paths. Same pre-existing test_id prefixes preserved.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Test <test@test.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
joelteply added a commit that referenced this pull request May 13, 2026
…1132 PR-2) (#1136) (#1137)

The auto-generated per-module index.ts barrels in shared/generated/
must stay in sync with the .ts files emitted by the ts-rs export tests.
When they drift, types are invisible to TS consumers even though the
.ts file exists on disk — exactly the regression that bit PR #1129
(commit db271d3 manually added 12 export lines after the fact).

This ratchet makes the same regression structurally impossible:

- Walks every module dir under src/shared/generated/
- For each, lists the .ts files on disk + parses index.ts for
  `export type { X } from './Y'` lines
- Asserts every on-disk file is referenced; every reference has a file
- Failure message names exact drift + suggests
  `npx tsx generator/generate-rust-bindings.ts` recovery

Critical correctness detail: extracts the FROM path (`Y`), not the
exported type name (`X`). ts-rs `#[ts(rename = "...")]` produces
`export type { ToolCall } from './AgentToolCall'` where the file is
AgentToolCall.ts but the type is renamed to ToolCall. Earlier draft of
this ratchet got this wrong and falsely flagged every rename usage on
canary; corrected before commit.

Tests (8/8 passing on canary state):
- 5 parser unit tests pinning canonical / rename / double-quote /
  multi-line / malformed-tolerance behaviour
- 1 drift-detection unit test asserting both regression modes
  (missing-from-barrel + dangling-export) surface correctly
- 1 integration scan over real shared/generated/ tree
- 1 known-modules sanity check guarding against accidental dir deletion
  hiding drift behind an empty module

Same shape as Lane F PR-1 deletion ratchet (#128) — walk source,
assert pattern, fail loud with actionable message.

Card: continuum#1136. Lane partner: claude-tab-2 PR #1135 (#1132 PR-1,
AIRC + queue-lifecycle smoke slice; this is the ts-rs export check
slice they explicitly handed to me).

Co-authored-by: Test <test@test.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
joelteply added a commit that referenced this pull request May 14, 2026
)

Closes the e2e admission loop on top of the storage types (PR-1, #1129)
and the gate machinery (PR-2, #1134) by giving callers ONE pure-Rust
object — `InboxAdmissionRunner` — that wraps the recipe + config +
trust mapping for a persona, exposing a single `admit(&inbox_msg, ...)`
method that returns the typed `AdmissionDecision`.

What ships:

- `InboxAdmissionRunner<R: IsMemorable>` — generic per-persona runner.
  Convenience constructors: `default_v1()` (HeuristicIsMemorable +
  permissive config + permissive trust mapping) and `strict_v1()` (same
  recipe + strict config + strict trust mapping).
- `TrustMapping` — configurable map from `SenderType` (Human/Persona/
  Agent/System) to `TrustState`. `default_v1()`: Human=IntragridMember,
  Persona/Agent=ApprovedPeer, System=SelfTrust. `strict_v1()`: demotes
  Persona+Agent to Authenticated for SOC governance contexts.
- `inbox_message_to_candidate(msg, mapping)` — pure converter.
  Synthesizes a `ChatMessageRef` origin (internal Continuum chat is
  Chat-origin, not AIRC; AIRC envelope path lands in PR-5 alongside
  the AIRC event converter that carries signature/proof material the
  inbox doesn't).
- `inbox_message_to_origin(msg)` — pure helper (always Chat for v1).
- `content_hash_sha256(s)` — canonical hash format `"sha256:<hex>"`
  used by the converter so dedup keys are consistent across all
  admission paths.

What this PR does NOT ship (deferred):

- Call-site integration with `PersonaInbox::drain_frame()` — PR-4
  adds the actual call from the cognition path.
- Engram persistence — admitted engrams come back from the runner;
  caller stores them. PR-5+ adds the ORM persistence path.
- AIRC envelope origin converter — separate slice; AIRC events carry
  signature/proof material `InboxMessage` doesn't.

Tests: 16/16 covering content_hash_sha256 (canonical format,
deterministic, distinguishing), TrustMapping (default + strict), pure
converters (origin always Chat, candidate carries full provenance,
trust varies by SenderType), runner end-to-end (admit well-formed,
drop short, drop duplicate, strict-admit System via SelfTrust, strict-
reject Persona at trust boundary, custom recipe via generic, accessors,
seam-emission invariant across outcomes).

Card: continuum#1140. Builds on continuum#1129 + continuum#1134
(both merged on canary).

Co-authored-by: Test <test@test.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
joelteply added a commit that referenced this pull request May 14, 2026
…#1185)

Per task #71 — survey of every .json under src/system/recipes/.

Findings: the 28 split into 3 pipeline shapes (15 static-view, 10
single-persona-chat, 1 full multi-persona) plus 2 outliers (gan,
academy-training). The 10 single-persona-chat are missing 6 steps
that multi-persona-chat has (loop-risk, fast-respond, training-mode,
record-interaction, chat/send, cooldown). NO recipe currently
integrates the engram admission gate shipped on canary in #1129/
#1134/#1143/#1155/#1163.

5 identified gaps with concrete next-sprint cards:
1. Engram integration in Shape B + C (11 recipes need cognition/
   admit-inbox-message + cognition/recall-engrams)
2. Resolve academy-training half-migrated state
3. Document gan orphan intent
4. Shape B → Shape C decision (or shared inheritance)
5. version field discipline across all 28

Pure docs PR. Output at docs/cognition/RECIPE-AUDIT-2026-05-14.md.

Closes #71.

Co-authored-by: Test <test@test.com>
joelteply added a commit that referenced this pull request May 15, 2026
…ract (#1282)

Add `tests/no_cpu_fallback_contract.rs` — three forbidden-strings
ratchets that fail the build if a future PR weakens the
no-CPU-fallback contract:

1. `select_best_device_panics_loudly_on_no_gpu` — asserts
   `inference/model.rs::select_best_device` keeps the
   `panic!("No GPU device available for inference. CPU fallback is
   disabled.")` loud-fail and tries CUDA + Metal before panicking.

2. `ort_providers_documents_no_cpu_fallback_contract` — asserts
   `ort_providers.rs` keeps the "CPU fallback is forbidden" comment
   that documents the rule from source.

3. `llamacpp_adapter_uses_loud_fail_for_no_local_model` — asserts
   `LlamaCppAdapter` uses the typed `NoLocalModelLoadable` error
   (shipped in #1093 / lane A PR-2) rather than a silent skip.

Pattern: same forbidden-strings ratchet shape as lane F PR-2 (#1129
TS persona forbidden-strings), applied to the Rust inference layer.
A test failure points the future-PR-author at the exact contract
they're about to weaken.

Closes the acceptance criterion #3 of #1262 ("regression test per
fallback path"). Final PR (4 of 4) for the silent CPU fallback audit.

Verified:
- cargo test --features metal --test no_cpu_fallback_contract:
  3 passed, 0 failed

Lane: alpha flywheel #1272 lane 6.
Audit: #1262 (comment)

Co-authored-by: Test <test@test.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant