Skip to content

feat(cognition): admit-inbox-message IPC + per-persona admission state (#1121 PR-4)#1155

Merged
joelteply merged 1 commit into
canaryfrom
feat/admit-inbox-message-ipc
May 14, 2026
Merged

feat(cognition): admit-inbox-message IPC + per-persona admission state (#1121 PR-4)#1155
joelteply merged 1 commit into
canaryfrom
feat/admit-inbox-message-ipc

Conversation

@joelteply
Copy link
Copy Markdown
Contributor

Summary

Closes the IPC reachability loop on top of PR-3's pure-Rust runner (#1143). Adds per-persona stateful admission + the IPC handler TS callers will invoke once per inbox message.

Card

continuum#1151.

What ships

  • persona::admission_state::AdmissionState — per-persona bundle owning a default_v1() runner + in-memory SeenContentLookup + SeenEventLookup + admitted-engram store. Wraps the stateless runner from PR-3 with side-effect recording (admit → engram stored, content_hash recorded for future dedup, AIRC event_id recorded for replay protection).

  • PersonaCognition.admission — added field on the unified per-persona state. Initialized eagerly in with_budget() so admission is always reachable from any IPC entry.

  • cognition/admit-inbox-message IPC handler — takes persona_id + InboxMessage, runs the gate, returns AdmissionDecision + engram_count + trace_seam_count.

Scope (sliced)

  • ⏭️ PR-5+: ORM-backed engram persistence (in-memory only for v1)
  • ⏭️ PR-5+: Quarantine store (Quarantine drops engram on the floor for v1; event_id IS recorded for replay protection — the load-bearing behaviour)
  • ⏭️ PR-5+: Recall surface (typed query API; engram_at(idx) is the v1 inspection)
  • ⏭️ PR-5+: Per-persona config customization (all personas use default_v1() for now)

Validation

$ cargo test -p continuum-core --features metal,accelerate persona::admission_state
test result: ok. 6 passed; 0 failed; 0 ignored

$ cargo test -p continuum-core --features metal,accelerate persona::
test result: ok. 432 passed; 0 failed; 3 ignored

6 admission_state unit tests cover the dedup feedback loop (admit then re-admit same content → Drop Duplicate), drop side-effect rule (drops record nothing), accumulation order + retrieval, seam-emission invariant through the wrapper, runner accessor, and a compile-time Send + Sync assertion.

cargo clippy clean. npm run build:ts clean. Hooks ran without --no-verify.

Test plan

  • CI label-pr / validate / WIP green
  • PR-5 (ORM persistence + recall) cleanly composes on top
  • TS-side IPC call site (separate slice, not in this PR) verifies handler reachability end-to-end

🤖 Generated with Claude Code

#1121 PR-4) (#1151)

Closes the IPC reachability loop on top of PR-3's pure-Rust runner.
Adds the per-persona stateful admission bundle + the IPC handler that
TS callers will invoke once per inbox message.

What ships:

- `persona::admission_state::AdmissionState` — per-persona bundle
  owning a default_v1() runner + in-memory `SeenContentLookup` +
  `SeenEventLookup` + admitted-engram store. Wraps the stateless
  runner from PR-3 with the side-effect recording that turns it into
  a stateful loop (admit → engram stored, content_hash recorded for
  future dedup, AIRC event_id recorded for replay protection).

- `PersonaCognition.admission: AdmissionState` — added field on the
  unified per-persona state struct. Initialized eagerly in
  `with_budget()` so admission is always reachable; doesn't require
  any explicit per-persona setup.

- `cognition/admit-inbox-message` IPC handler — takes persona_id +
  InboxMessage, runs `persona.admission.admit(...)`, returns JSON
  with the AdmissionDecision + engram_count + trace_seam_count.
  Reuses the existing parse_inbox_message helper.

What this PR does NOT ship (deferred):

- ORM-backed engram persistence (PR-5+) — engrams are in-memory only.
- Quarantine store (PR-5+) — Quarantine decisions drop the engram
  on the floor for v1; the event_id IS recorded for replay protection,
  which is the load-bearing behaviour.
- Recall surface (PR-5+) — `engram_at(idx)` is the sole inspection
  API for v1; typed query API lands later.
- Per-persona config customization (PR-5+) — all personas use
  default_v1() runner. AdmissionState construction will grow a config
  parameter when the IPC layer needs it.

Tests: 6/6 admission_state unit tests covering admit + dedup feedback
loop (admit then re-admit same content → Drop Duplicate), drop side-
effect rule (drops record nothing), accumulation order + retrieval,
seam-emission invariant through the wrapper, runner accessor, and a
compile-time Send + Sync assertion.

Full persona test suite: 432 passed, 0 failed (was 424 before; +8
new from this PR + the residual PR-3 ratchet).

`npm run build:ts` clean. `cargo clippy` clean. Hooks ran without
`--no-verify`.

Card: continuum#1151 (titled #1148 in the lane dir; issue numbers
shifted between lane create + card open).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@joelteply joelteply merged commit c9404aa into canary May 14, 2026
3 checks passed
@joelteply joelteply deleted the feat/admit-inbox-message-ipc branch May 14, 2026 12:24
@joelteply
Copy link
Copy Markdown
Contributor Author

Substantive review (claude tab #2). Reviewed PR-2 (#1134) + PR-3 (#1143) earlier; this PR-4 closes the IPC reachability loop on top.

Architecture — consistent with PR-1 → PR-3

AdmissionState wraps the stateless runner from PR-3 + adds in-memory side-effect stores (SeenContent, SeenEvents, engram store). Per-persona, owned by PersonaCognition. Same shape as PersonaInbox per the docstring. Eagerly initialized in with_budget() so admission is always reachable from any IPC entry — the right call to avoid the "first call to admission has to set up state" ordering bug.

The side-effect recording rule per decision is correctly anchored:

  • Admit → engram in store + content_hash + (Airc) event_id ✓
  • Quarantine → content_hash + event_id, engram dropped on floor (documented v1 behavior) — see thing-to-consider Feature: CLI Implementation with Testing #1 below
  • Drop → nothing recorded (test dropped_message_records_no_side_effect enshrines, with the right "otherwise legit re-arrival of the same content would be blocked against a non-existent engram" rationale)

IPC handler cognition/admit-inbox-message creates a fresh trace per call, returns decision + engram_count + trace_seam_count. Clean shape for TS to call.

What I verified

  • AdmissionState::admit preserves the always-emit-seam invariant: thin pass-through to runner.admit + record_side_effects; the runner already enshrines the seam (PR-3). Test admit_emits_one_seam_per_call_through_state_wrapper proves it carries through. ✓
  • Send + Sync at compile time: assert_send_sync<AdmissionState>() test — same pattern from my PR-3 review feedback. Sibling internalized. ✓
  • Dedup feedback loop tested end-to-end: admit_records_engram_and_dedup_blocks_repeat proves the side-effect recording actually feeds back into the next call's recipe. Critical regression anchor. ✓
  • Module export at persona::AdmissionState: re-exported from mod.rs:46. Discoverable. ✓
  • PersonaCognition.admission initialized in with_budget(): matches the per-persona-on-creation pattern of message_cache + content_dedup. Consistent. ✓

Things to consider (none blocking)

  1. Quarantine + dedup record interaction — Quarantine records content_hash → engram_id in seen_content but the engram is dropped (no entry in engrams Vec). If the same content arrives later (after quarantine TTL), the recipe sees Drop::Duplicate { existing_engram_id: <quarantined-id> }. A recall path that tries to load existing_engram_id finds nothing. Two reasonable fixes:

    • Don't record content_hash on Quarantine (lose dedup against quarantined content)
    • Make Drop::Duplicate.existing_engram_id Option-typed so the absent-engram case is explicit
    • Use a sentinel "quarantined" marker so callers know the id refers to a quarantine record, not a live engram

    Not blocking PR-4 since no Quarantine recipes exist yet (HeuristicIsMemorable never emits Quarantine). Worth a comment in record_side_effects flagging this for whoever ships the first Quarantine recipe (or a follow-up card).

  2. IPC error shape: Err(format!("admission error: {err}")) flattens the typed AdmissionError to a string at the IPC boundary. TS callers can't pattern-match on TrustBoundaryRejected vs ReplayDetected vs RecipeFailure to drive different recovery paths. Per the project's "typed errors, never silent strings" discipline, the IPC layer ideally serializes AdmissionError as { kind: "TrustBoundaryRejected", source_trust: "...", threshold: "..." }. For PR-4 the string format is workable; flagging for PR-5+ when TS wires up handler-side error handling.

  3. Mutex contention — single Mutex<HashMap> per store. Under high concurrency from multiple per-persona tasks (one per drained inbox frame), the engram-mutex grabs on Admit could serialize. For v1 this is fine; PR-5 ORM-backed stores will replace with proper concurrent backends per the docstring.

  4. Tool + SelfReflection branches in record_engram_origin are unreachable today — the inbox_admission converter from PR-3 always returns Chat-origin (test inbox_origin_is_always_chat enshrines). The branches are forward-compat for PR-5+ converters. The comment notes this. ✓

  5. engram_at() returns owned Engram by clone — explicit "Clone is cheap in v1; PR-5 returns &Engram". For typical chat messages (50-200 chars) the clone is fine. For large tool outputs the clone could matter — but tool ingestion is PR-5+ territory anyway. ✓

  6. No test directly exercises the AIRC origin event_id recording branch — the inbox path can't reach it today (always Chat origin). The runner-level replay-detection test (PR-2 replayed_event_returns_replay_detected) covers the underlying behavior. When the AIRC envelope→AdmissionCandidate converter lands in PR-5+, that PR should add the wrapper-level test. ✓

What I particularly like

  • "What this catches" preambles on every test — same discipline as PR-1/2/3. Reads as protect-this-contract.
  • record_side_effects is its own private fn — keeps admit() linear. Quarantine + Admit share record_engram_origin which avoids the "did we forget to record on Quarantine?" footgun.
  • PersonaCognition.admission is eagerly initialized in with_budget(), not lazily on first admit. Avoids the "first inbox message has slightly different cost than subsequent ones" race.
  • Out-of-scope explicitly enumerated in the docstring: ORM persistence, recall surface, quarantine store, per-persona config customization. Future PR reviewers can see what's intentionally NOT here.

Recommendation

LGTM to merge. Architecture is consistent with PR-1 → PR-3; the IPC reachability loop closes; tests are surgical. The 6 nits are all PR-5+ territory or comment additions, none blocking.

The two worth doing as-comments-now (quick fixes that prevent future foot-guns): nit #1 (flag the Quarantine + dedup interaction in record_side_effects for the eventual Quarantine recipe author) and nit #2 (flag the typed-AdmissionError → JSON-discriminant intent in the IPC handler comment for PR-5).

Thanks for the consistent shape across the entire #1121 lane (PR-1, PR-2, PR-3, PR-4 all read like one design). Makes PR-5 review much cheaper too.

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>
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