Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions alembic/versions/035_agent_live_sessions_ipc_attachment.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,15 @@
machinery in Phase 1. Sync-inline-ack-3s alternative deferred to a future
Backlog row (meeting-room-style live agent discussions).

NOTE on forward-reference (annotated 2026-05-19 by cueapi-secondary during
Surface 6 audit, msg_410i80bctfox): the ``delivery_mode_requested='ipc'``
value referenced above is a forward-reference. The ``messages.delivery_mode_requested``
column does NOT exist yet in cueapi-core — it lands when Surface 6
(cueapi-hosted alembic 050a equivalent) is ported. Until then, the ASYNC
fire-accept dispatcher path is dormant; no message-create code path produces
``delivery_mode_requested='ipc'``. See parity-manifest.json →
``pending_ports.surface_6_delivery_mode`` for the port sequencing.

Backwards-compat: all v2.x rows inherit ``transport='poll'`` + NULL on the
other 3 columns. Existing fire-accept dispatcher logic untouched until a row
carries ``transport='ipc'``.
Expand Down
137 changes: 137 additions & 0 deletions docs/parity-status.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
# Parity Status — cueapi-core ↔ cueapi-hosted

This document is the narrative reference for what is and isn't yet ported
between cueapi-core (this repo) and the private cueapi-hosted monorepo.
The machine-readable source-of-truth is `parity-manifest.json` at the repo
root; this doc explains the WHY + WHAT'S NEXT in prose.

Three related artifacts:

1. **`parity-manifest.json`** — file-by-file parity tracking (what's
synced, when, deviations) + the new `pending_ports` section that
tracks whole-feature ports across multiple files.
2. **`HOSTED_ONLY.md`** — open-core policy. Lists features that are
intentionally hosted-only and will NOT be ported (Stripe billing,
GDPR cascade, dashboard, cross-org messaging, multi-tenancy).
3. **`docs/parity-status.md`** (this file) — narrative reference for
pending ports + the current gap shape.

## How to read parity status

When evaluating "is feature X in cueapi-core?":

1. Check `HOSTED_ONLY.md` first — if X is listed there, it's intentionally
not ported and won't be.
2. If not in HOSTED_ONLY, check `parity-manifest.json` `oss_only_exclusions`
— file is OSS-only by design.
3. Check `parity-manifest.json` `pending_ports` — whole-feature port
pending, with port sequencing + dependencies enumerated.
4. Check `parity-manifest.json` `field_drift_annotations` — feature is
ported but specific fields diverge (often deferred for trigger or
intentional hosted-only sub-feature).
5. If feature isn't in any of the above + files are last_synced recent,
the feature is at parity. File a Backlog row if you find a gap not
yet tracked.

## Current pending ports

### Surface 6 — sender-controlled delivery mode

**Status**: audit-complete; port pending Jingim Phase 1 plan signal.

**What it does**: lets the sender pick which channel to deliver a Cue
Message through — `live` (push to recipient's attached Live session),
`bg` (background-handler-spawn), `inbox` (poll-fetchable), `webhook`
(push to recipient's webhook URL), or `auto` (server resolves based on
recipient's `preferred_contact` + cascade fallback).

**Why it matters for self-hosters**: Dock plans to integrate
cueapi-core messaging into their UI as a customer-facing primitive. The
load-bearing primitive for Dock's Phase 1 rebuild is `delivery_mode:
"live"` (sender-controlled push), which replaces their current 4-second
polling pattern with substrate-side push delivery.

**What's already in cueapi-core** (the foundations):

- Event-emit primitive (PR #71, alembic 028)
- Messaging emission wiring (PR #72, alembic 029)
- Long-poll mode (PR #73)
- `agent_live_sessions` schema (PR #67, alembic 026)
- Heartbeat + live-claim attestation endpoints (PR #70)
- `agent_live_sessions` IPC attachment columns (PR #91, alembic 035)
- Per-message `send_at` (PR #77, alembic 030)
- Agent roster + `last_seen_at` (PR #80, alembic 031)

**What's NOT yet in cueapi-core** (the gap):

- `MessageCreate.delivery_mode` field on the wire schema (sender input)
- `messages.delivery_mode_requested` + `messages.effective_delivery_mode`
columns (sender input + server-resolved channel)
- `agents.delivery_capabilities` + `agents.preferred_contact` columns
(recipient capability declaration + auto-resolution preference)
- `agents.live_cue_id` + `agents.bg_cue_id` columns (bridge targets for
push delivery)
- Delivery-mode resolver in `message_service.create_message` (capability
check + cascade fallback)
- Bridge logic that fires the recipient's `live_cue_id` / `bg_cue_id`
when `delivery_mode=live` / `delivery_mode=bg`

The load-bearing private migration is `alembic/versions/050a_messages_delivery_mode.py`
on cueapi-hosted, with follow-on `067_messages_queue_effective_mode.py`
extending the CHECK constraint to include a `queue` value for the
`live_fallback_mode='queue'` case.

**Port sequencing** (4 steps; each step is its own PR):

1. **Audit-PR** (this PR): expand `parity-manifest.json` with the
Surface 6 row + clarifying comment on alembic 035's forward-reference
+ this narrative doc.
2. **Substrate migration port**: port alembic 050a + 067 → cueapi-core
(next available migration numbers). 4 agent columns + 2 message
columns + CHECK constraint. Tests: schema-invariant patterns matching
existing PR #87 / #91 style.
3. **`MessageCreate.delivery_mode` field + resolver logic**: wire-shape
addition + capability-check + auto-cascade-fallback. Tests:
per-mode happy paths + cascade fallback + invalid-mode rejection.
4. **Bridge logic**: live mode → fire `agents.live_cue_id`; bg mode →
fire `agents.bg_cue_id`. Effective-mode resolution surfaces on
`MessageResponse`. Tests: bridge fires correct cue + claim-miss
cascade to bg.

Steps 2-4 are held pending Jingim's Phase 1 plan signal — substantive
timing depends on Dock's Phase 1 rebuild sketch. The 4-PR cut shape is
locked; only the start-timing is open.

**Tier shape**: single-tenant self-hosted (Fly + Neon Postgres + Upstash
Redis). The 4 port-PRs are tenant-agnostic; no multi-tenancy logic
needs to ride along.

**Cross-org gating is OUT OF SCOPE**: cueapi-hosted's `cross_org_*_enabled`
toggles + cross-org walk-trio (PRs #881-#889 on hosted) stay
hosted-only per the open-core policy. The Surface 6 port is intra-tenant
only.

### Other pending ports

See `parity-manifest.json` `field_drift_annotations` for field-level
divergence on already-ported files (e.g., Execution + Cue schema fields
deferred for trigger). Whole-feature ports beyond Surface 6 are tracked
on the agent-team-project-tracker Backlog; this doc lists the
narrative-worthy ones with documented sequencing.

## Audit methodology

Audits happen periodically (typical cadence: post-cueapi-hosted-feature
ship + every 2-4 weeks for the broader gap inventory). The full-audit
date is in `parity-manifest.json` `last_full_audit`. Audits cross-check
every file in `app/**/*.py` + `alembic/**/*.py` against the private
counterpart at audit time.

When you find a feature you want in cueapi-core that isn't here:

1. File a Backlog row on the agent-team-project-tracker
2. Cross-reference the cueapi-hosted PR(s) that introduced it
3. Add it to `parity-manifest.json` `pending_ports` with the coverage
matrix (load-bearing PRs + migrations + target OSS files)
4. Sequence the port-PRs (substrate migration → wire-shape → routing →
tests typically; adjust for the feature's surface)
109 changes: 109 additions & 0 deletions parity-manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,115 @@
}
}
},
"pending_ports": {
"comment": "Whole-feature ports from private cueapi/cueapi that are NOT YET in cueapi-core. Each entry enumerates the load-bearing private artifacts (PRs + migrations + files) and the target OSS files that would change when the port lands. Distinct from oss_only_exclusions (whole files only in OSS) and field_drift_annotations (single-field divergence on already-ported files). Backlog rows for these ports live on the agent-team-project-tracker; the matrix below is the SoT for what each port touches. Reviewed 2026-05-19 by cueapi-secondary during Surface 6 audit (msg_410i80bctfox; KICKOFF cue msg_sq6h2fcwseiu).",
"surface_6_delivery_mode": {
"summary": "Sender-controlled push delivery for the messaging primitive: MessageCreate.delivery_mode = 'live' | 'bg' | 'inbox' | 'webhook' | 'auto' with substrate-side capability check + cascade fallback + bridge to recipient's live_cue_id / bg_cue_id. Load-bearing primitive for Dock's Phase 1 rebuild per Jingim's directional read (msg_vzs6a1t5oii5 2026-05-19 ~01:43Z).",
"tier": "single-tenant-self-hosted",
"status": "audit-complete; port pending Jingim Phase 1 plan signal",
"private_origin": {
"load_bearing_prs": [
{
"pr": "cueapi/cueapi#635 family (Surface 6 productization)",
"context": "Backlog row cmot4owm7 (substrate work) + cmot5alrh (bridge work). Adds sender-controlled delivery_mode field + capability declaration on agents + bridge logic in message_service."
}
],
"load_bearing_migrations": [
{
"path": "alembic/versions/050a_messages_delivery_mode.py",
"ddl": "agents.delivery_capabilities (JSONB default '[\"inbox\"]') + agents.preferred_contact (TEXT default 'inbox') + agents.live_cue_id (VARCHAR(20) FK cues.id ON DELETE SET NULL) + agents.bg_cue_id (same shape) + messages.delivery_mode_requested (TEXT nullable) + messages.effective_delivery_mode (TEXT nullable, CHECK in 'live'|'bg'|'inbox'|'webhook'); 'auto' is a sender-input value only (server resolves before write)."
},
{
"path": "alembic/versions/067_messages_queue_effective_mode.py",
"ddl": "Extends valid_effective_delivery_mode CHECK constraint to include 'queue' value. Used when sender targets Live but recipient's Live session has no fresh heartbeat AND sender's live_fallback_mode='queue' (vs 'bg'). Stays delivery_state='queued' + effective_delivery_mode='queue'; poll-fetchable once Live session attaches."
}
]
},
"target_oss_files": {
"models": [
{
"path": "app/models/agent.py",
"change": "Add 4 columns: delivery_capabilities (JSONB), preferred_contact (String(16) default 'inbox'), live_cue_id (String(20) FK cues.id ON DELETE SET NULL), bg_cue_id (String(20) FK cues.id ON DELETE SET NULL)."
},
{
"path": "app/models/message.py",
"change": "Add 2 columns: delivery_mode_requested (Text nullable), effective_delivery_mode (Text nullable). Add CHECK constraint valid_effective_delivery_mode."
}
],
"schemas": [
{
"path": "app/schemas/message.py",
"change": "Add MessageCreate.delivery_mode: Optional[Literal['live', 'bg', 'inbox', 'webhook', 'auto']]. Add MessageResponse.delivery_mode_requested + MessageResponse.effective_delivery_mode."
},
{
"path": "app/schemas/agent.py",
"change": "Add AgentResponse.delivery_capabilities (list) + AgentResponse.preferred_contact (str). Expose for the GET /v1/agents/{ref} discovery surface."
}
],
"services": [
{
"path": "app/services/message_service.py",
"change": "Add delivery-mode resolver in create_message: capability check + cascade fallback (auto resolves via preferred_contact). Add bridge logic to fire recipient's live_cue_id when delivery_mode=live (claim-window-aware; cascade to bg on claim miss per migration 067 'queue' state). Add bridge logic to fire recipient's bg_cue_id when delivery_mode=bg."
},
{
"path": "app/services/agent_service.py",
"change": "Allow PATCH /v1/agents/{ref} to set delivery_capabilities + preferred_contact + live_cue_id + bg_cue_id."
}
],
"alembic": [
{
"path": "TBD: alembic/versions/036_messages_delivery_mode.py (or next available)",
"ddl": "Port of cueapi-hosted 050a (load-bearing migration above). All 6 columns from the private migration; CHECK constraint enum from 067 (include 'queue' value)."
}
]
},
"deferred_in_audit": [
{
"feature": "live_fallback_mode (sender chooses queue vs bg on Live-miss)",
"private_origin": "alembic 067_messages_queue_effective_mode.py (queue state) + service-layer resolver",
"note": "Optional; ship Surface 6 minimal first (live + bg + inbox + webhook + auto resolver). live_fallback_mode is a sender-side preference for handling claim-miss — Dock's Phase 1 may not need it. Defer to Step 4 follow-up OR fold into Step 2 if substrate work is small."
},
{
"feature": "cross-org delivery_mode gating (cross_org_cue_task_enabled etc.)",
"private_origin": "cueapi/cueapi PR #881-#889 (cross-org walk-trio)",
"note": "Per Jingim's tier-shape lock (single-tenant self-hosted), cross-org is OUT OF SCOPE for the OSS port. Cross-org gating + walk-trio stays hosted-only until self-hosters signal need."
}
],
"ports_already_landed_in_oss_for_surface_6_foundations": [
"alembic 028 + app/models/event.py + app/services/events_service.py — event-emit primitive (PR #71, ports private #731 PR-1b)",
"alembic 029 — messaging emission columns (PR #72, ports private #775 PR-2a)",
"alembic 030 — message send_at (PR #77, ports private #623)",
"alembic 031 — agents.last_seen_at + roster endpoint (PR #80, ports private #630)",
"alembic 033 — subscriptions.inline_body (PR #84, ports private #791)",
"alembic 034 — subscriptions.last_acked_event_id (PR #85, ports private #793)",
"alembic 026 — agent_live_sessions schema (PR #67)",
"alembic 027 — executions live-claim attestation (PR #70)",
"alembic 035 — agent_live_sessions IPC attachment (PR #91)"
],
"forward_references_in_oss_pending_surface_6": [
{
"path": "alembic/versions/035_agent_live_sessions_ipc_attachment.py",
"note": "Docstring mentions `delivery_mode_requested='ipc'` (line 42 'returns immediately with delivery_mode_requested='ipc'') as a forward-reference to behavior the (future) async fire-accept dispatcher will exhibit. The column itself is NOT added by 035 (which only touches agent_live_sessions); it lands when Surface 6's substrate migration ports. NOT a bug — the ASYNC dispatcher path won't fire until Surface 6 is complete (no message-create path produces delivery_mode_requested='ipc' until then). Verified 2026-05-19 by cueapi-secondary during Step 1 audit."
}
],
"sequencing_recommendation": [
"Step 1 (audit-PR, this PR): expand parity-manifest with this Surface 6 row + docs/parity-status.md narrative + 035 docstring clarifying comment.",
"Step 2 (substrate migration port): port 050a + 067 -> cueapi-core 036 + 037 (or next available numbers). 4 agent columns + 2 message columns + CHECK constraint.",
"Step 3 (MessageCreate.delivery_mode + resolver): wire-shape field + capability-check + auto-cascade-fallback logic in message_service.",
"Step 4 (bridge logic): live mode -> fire agents.live_cue_id; bg mode -> fire agents.bg_cue_id. Effective-mode resolution surfaces on MessageResponse."
],
"audit_metadata": {
"audit_date": "2026-05-19",
"audited_by": "cueapi-secondary",
"kickoff_cue": "msg_sq6h2fcwseiu",
"scope_narrow_cue": "msg_u7x0f649gjro",
"greenlight_cue": "msg_7enby3btpl6v",
"dock_workspace": "https://trydock.ai/mike/cueapi-core-parity-for-dock",
"jingim_directional_signal": "msg_vzs6a1t5oii5",
"tier_shape": "single-tenant-self-hosted (Fly + Neon Postgres + Upstash Redis)"
}
}
},
"files": {
"alembic": [
{
Expand Down
Loading