Document: cr-inbound-seam-minimal-slice-v0_1
Version: v0.1
Date: 2026-06-29
Type: Phase change request — engine (one route, one auth dep, one token table, one event kind, one MemoryObject). Frontend: none new (reuses the held-assertion card).
Consumer: Claude Code (executor).
Branch base: engine main @ d32c99b.
Origin: The minimal testable inbound slice (loomworks-scoping-note-inbound-seam-minimal-slice-v0_2), grounded by Step 0 against engine d32c99b.
Reads with: the bidirectional-substrate investigation (the inbound half) and the source-provider investigation (the outbound half, sequenced after).
The smallest real thing that proves the inbound seam, and that you can test with one curl. An external system POSTs a JSON fact at an engagement, authenticated with a per-engagement inbound token. The fact lands in that engagement's Memory as a held assertion — a proposed fact, FORAY-attested, waiting for the Operator to commit or discard it. You test it by POSTing, then opening the Memory room and seeing the held card appear. It accepts data only and can do nothing but propose a fact, so the bright line (data, not commands) holds by construction — a held assertion can't even enter the record without the Operator committing it.
The scoping note got three things wrong about the real engine; Step 0 found them and the CR uses the real model:
wasAttributedTo, wasGeneratedBy) — there is no source enum to set to inbound_seam. Origin is carried by event_kind + actor_kind. So "this came from the inbound seam" is a new event_kind="inbound_event_received" plus the inbound actor's kind — not a provenance string.inbound_event_received kind is needed (free-form column + one anchor-priority line; no migration).
POST /engagements/{id}/inbound — authenticated by a per-engagement inbound token (a dedicated machine principal, auth A2). An external JSON fact arrives → drafts a held assertion in the engagement's Memory → FORAY-attested automatically at the write seam → visible as a held card in the Memory room. The Operator commits or discards it. Curl-testable.
append_event (memory/events.py:127) — the single memory-event write path; FORAY fires inside it automatically at the reserved seam (_foray_reserved_emit, events.py:244). Zero FORAY code to write.submit_manual_content (uploads.py:785) — the near-exact template: external JSON body → typed MemoryObject with Provenance → append_event. Copy this shape.assertions_drafted); it surfaces in the existing Memory room with no new UI./engagements/{engagement_id}/..., bare literal path, get_db_session + an auth dep).MemoryObject subclass for the external-fact shape + register it in OBJECT_TYPE_REGISTRY (memory/registry.py).event_kind inbound_event_received + one line in _ANCHOR_PRIORITY (foray/anchor_priority.py:31). No migration (event_kind is free-form String(64)).engagement_api_keys shape but storing a hash like the contributor-token mechanism (contributors/tokens.py). Plus an auth dep modeled on the bearer branch of get_resolved_actor (deps.py:915), yielding an actor whose kind marks it inbound/machine (mapped to contributor or agent so no actor_kind CHECK migration is needed).contributedBy. The card won't label "inbound" yet (the assertion card carries only contributedBy.{kind,name}) — distinguishing inbound-vs-other in the UI is deferred rendering work, not this slice.Per-step commits, suite green at each, halt-before-push. Engine only.
Dedicated per-engagement inbound-token table (labeled, sha256-hashed, compared not decrypted). Mint/verify helpers modeled on contributors/tokens.py. An auth dep modeled on the get_resolved_actor bearer branch, resolving the token scoped to the engagement and yielding an inbound/machine actor (kind ∈ {contributor, agent} — no CHECK migration).
Suite expectation: a valid inbound token resolves to the inbound actor scoped to its engagement; an invalid/wrong-engagement token is rejected.
The external-fact MemoryObject subclass + OBJECT_TYPE_REGISTRY registration; the inbound_event_received event kind + its _ANCHOR_PRIORITY line.
Suite expectation: the object serializes/deserializes through the registry; the event kind is recognized.
POST /engagements/{id}/inbound: the inbound auth dep + get_db_session → build the external-fact MemoryObject with Provenance → draft a held assertion from it (the upload pathway's assertions_drafted shape) → append_event(... event_kind="inbound_event_received" ...) (FORAY fires automatically).
Suite expectation: a POSTed JSON fact drafts a held assertion in the target engagement; the event is written and attested (the reserved seam fires); a malformed UUID 422s; a missing/invalid token is rejected.
A real POST against the running engine:
curl -X POST https://<dev>/engagements/<id>/inbound \
-H "Authorization: Bearer <inbound-token>" \
-H "Content-Type: application/json" \
-d '{"fact": "a coaching client completed their 360 assessment, score 72"}'
Then open the engagement's Memory room → a held card carrying that fact appears, FORAY-attested. The Operator commits or discards it.
This is the acceptance — not a headless-only check. The slice exists to be observed: POST, look at Memory, see the held card. (The card won't say "inbound" yet — deferred.)
The inbound half of the bidirectional thesis, walking: external system → inbound seam → governance band (FORAY + provenance via actor/event-kind) → Memory (as a reviewable held assertion), bright line holding by construction. With that confidence, the outbound (provider) half — where the thread began — gets scoped next, against a proven governed-transit pattern.
DUNIN7 — Done In Seven LLC — Miami, Florida Change Request — Inbound seam, minimal observable slice — v0.1 — 2026-06-29 POST /engagements/{id}/inbound, per-engagement inbound token (dedicated table, auth A2). External JSON fact → drafts a HELD ASSERTION in Memory (Fork A, observable — not a raw invisible event) → FORAY-attested automatically via append_event's reserved seam → visible as a held card, Operator commits/discards. Bright line by construction (held assertion, no path to action). Thin: reuses append_event + the submit_manual_content template + the held-assertion card; net-new is small (an inbound-token table, an event kind, a MemoryObject, the route). Step 0 corrected three scoping premises (no provenance-source field → origin = event_kind + actor_kind; new inbound_event_received kind; no Memory event-list → it's an assertion list, so draft an assertion). Acceptance is the curl test, observed in Memory. Base engine d32c99b.