Skip to content

fix(channel): process idle event before JWT reconnect (cherry-pick from dev/staging #59)#61

Merged
madtank merged 1 commit intomainfrom
orion/channel-idle-reconnect-to-main
Apr 17, 2026
Merged

fix(channel): process idle event before JWT reconnect (cherry-pick from dev/staging #59)#61
madtank merged 1 commit intomainfrom
orion/channel-idle-reconnect-to-main

Conversation

@madtank
Copy link
Copy Markdown
Member

@madtank madtank commented Apr 17, 2026

Summary

Cherry-picks the channel idle-reconnect fix (dev/staging PR #59, commit babbda9) onto main so it ships in the next release. Urgent for task 24646c02.

What changed

  • ax_cli/commands/channel.py: process the SSE event that wakes an idle stream BEFORE breaking for JWT refresh.
  • channel/README.md, specs/LISTENER-001/spec.md: presence-receipt contract docs.
  • tests/test_channel.py: regression test.

Why not a dev/staging → main roll-up?

Divergence between main and dev/staging requires a larger merge commit / conflict resolution. Cherry-picking just the urgent fix is safer for the reminder-lifecycle task. A separate dev/staging → main PR will catch up the remaining commits (docs/credentials/context).

Test plan

  • pytest tests/test_channel.py — 13 passed
  • After merge: confirm installed axctl on orion host sees reconnect_after_event in ax_cli/commands/channel.py; run timed reminder test.

Authored by anvil; cherry-picked by orion.

…presence receipts docs (#59)

* feat(alerts): ax alerts CLI — Activity Stream alerts + reminders MVP (#53)

* feat(credentials): audit active agent PAT hygiene

* docs(context): expose preview cache command

* fix(context): make fetched uploads render as artifacts

* docs(skill): add ax tool cadence

* feat(alerts): ax alerts CLI — Activity Stream alerts + task reminders MVP

First-slice MVP for task dfef4c92 (Activity Stream alerts and reminders).
Thin wrapper over POST /api/v1/messages using the existing metadata.alert
path — zero backend schema changes, no scheduler dependency.

Commands:
- ax alerts send — fire an alert/reminder with severity, target, source_task,
  due_at, remind_at, evidence. Emits metadata.ui.cards[0].type="alert" so
  the frontend's AlertCardBody renders it (verified against AxMessageWidgets).
- ax alerts reminder — shortcut for --kind reminder (source_task required).
- ax alerts ack/resolve/state — post a state-change REPLY (backend PATCH
  only accepts content, not metadata, so state transitions become first-class
  stream events referencing the parent alert via parent_message_id).

Reminder cards stay compact: no mcp_app widget, no task-board initial_data.
The card's resource_uri points at the linked task so it's clickable.

Dogfooded against next.paxai.app:
- msg 233089a7 — alert send, card type=alert, target+source_task set.
- msg 23c50444 — reminder send, kind=task_reminder, resource_uri set.

Related: 65c76d9b (CLI alert metadata), 0dacbc1e (task reminders), ebd63283
(activity stream taxonomy). Scheduler (68656c16) deferred to slice 2.

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

* docs(alerts): clarify recipient-acks-not-firer + link backend follow-up

Help text for ack/resolve/state now documents:
- the "recipient acks, not firer" boundary (backend refuses self-replies)
- task 247f7bf0 as the follow-up that enables in-place state transitions

Per aX feedback on ax-cli#53.

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

* feat(alerts): task-aware targeting + snooze state, task=source-of-truth design

Refinement per ChatGPT 2026-04-15 on dfef4c92 / 0dacbc1e:
tasks are canonical reminder/workflow objects; alerts are Activity Stream
events generated from (or linked to) tasks.

Changes:
- Add `snoozed` to allowed states + `ax alerts snooze <id>` command.
  Scheduler (68656c16) will re-fire at remind_at / next cadence.
- When `--source-task` is set and `--target` is omitted, auto-resolve
  target from task.assignee_id → task.creator_id. Explicit --target still
  wins for escalation. Displays "target: orion (from task assignee)" so
  users see when auto-resolution fired.
- Module docstring now states the "task = source of truth" design rule
  and points at 0dacbc1e / 68656c16 / 34bfbf6b for the scheduler-driven
  follow-up (recurring / SLA / stale-task nudges).

4 new tests (13 total for alerts, 199 full suite). Dogfooded on prod:
msg 18bb003a — reminder auto-targeted orion from task dfef4c92 assignee.

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

* style: format alerts command

* feat(alerts): timestamp sanity guard + response_required default for reminders

Per ChatGPT coordination update on ax-cli#53 / b911ea19:

- Reject any --remind-at / --due-at before 2020 with a clear error that
  names the likely root cause (runner with broken/frozen clock). Real
  case caught in msg b9fb15b6 where a remind_at landed as 2000-01-01.
- Reject malformed ISO-8601 with a typed message instead of letting
  garbage flow into the alert metadata.
- Default response_required=true for --kind reminder (they're work
  nudges — recipient is expected to ack/snooze). --kind alert stays
  opt-in via --response-required.

3 new tests (16 total alerts, 202 full suite). ruff format + check clean.

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

* feat(channel): publish processing status from claude bridge

* fix(channel): run claude bridge through axctl

* fix(channel): delegate package start to axctl

* test(channel): add headless processing smoke

* fix(channel): align Claude notification payload

* feat(cli): surface agent processing status while waiting

* style(cli): format dogfood scheduler files

---------

Co-authored-by: anvil <anvil@ax-platform.com>
Co-authored-by: orion <orion@paxai.app>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* draft(spec): SEND-TO-AGENT-001 CLI contract for sending tasks and alerts (#55)

Task b38a7475: users need an intuitive way to send a task or alert to an
agent as a real request with structured context. Today's workaround is
copy-paste the id into a plain message, which drops the structured
reference and teaches the receiver to guess.

This spec defines the two commands, the outgoing envelope shape (one
transcript message, one card, reuses PR #53 alert metadata + PR #54 task
snapshot pattern), how delivery receipts flow through (SEND-RECEIPTS-001),
and how the flow differs from plain ax send vs. passive app signals.

Owner split:
- CLI (orion, this spec + impl): tasks send / alerts forward
- Frontend (frontend_sentinel, task 60113fd7): Send to Agent button +
  receiver card rendering
- MCP (mcp_sentinel, follow-up): optional tasks.send / alerts.send MCP tool
- Backend: no schema changes, plain messages POST accepts the envelope

Composes with PR #107 (availability-escalation), PR #108 (delivery-
receipts), PR #54 (task snapshot embedding), PR #53 (ax alerts MVP).

Task: b38a7475-8bf6-445e-bd6e-1845222885dd

Co-authored-by: anvil <anvil@ax-platform.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(alerts): embed task snapshot in reminder alert metadata (#54) (#57)

Task e55be7c8: task reminder alert cards today truncate and surface raw
implementation fields, with no Open Task action even though the payload
includes source_task_id. This commit does the CLI-side half — enrich the
reminder envelope with a compact task snapshot (id, title, priority,
status, assignee_id, assignee_name, creator_id, deadline) so the frontend
can render task context inline without a second round-trip.

Changes
- alerts.py: extract _agent_name_for + _fetch_task_snapshot helpers
  (shared by _resolve_target_from_task).
- alerts.py: _build_alert_metadata grows a task_snapshot kwarg; when
  present, embeds at alert.task and card_payload.task.
- reminders.py: _fire_policy fetches the snapshot once per fire and passes
  it to _build_alert_metadata.
- tests: positive case (snapshot embedded with all fields) + negative case
  (fetch failure still fires reminder with source_task_id fallback).

Frontend half (task e55be7c8 assignee frontend_sentinel) renders the task
block on the alert card + wires an Open Task button to resource_uri
ui://tasks/{source_task_id}. The MCP widget ui://tasks/detail already
exists; the button is a simple MCP app link.

Validation
- PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 pytest tests/test_alerts_commands.py tests/test_reminders_commands.py
- Result: 21 passed (19 existing + 2 new)

Task: e55be7c8-4758-434c-bea9-1a35b27a769a

Co-authored-by: anvil <anvil@ax-platform.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(reminders): lifecycle-aware source task check (#58)

Tasks: e032bc49 (urgent) + f00e36ac (high).

Before firing a task-backed reminder policy, fetch the source task and
classify its lifecycle:

- Terminal (completed/closed/done/cancelled/canceled/archived/resolved,
  or completed_at set) → disable the policy + emit a skipped-result.
  No message is sent, fired_count does NOT advance.
- Pending review (status=pending_review, tags contains pending_review,
  requirements.pending_review truthy, or requirements.review_owner*
  set) → reroute to review_owner → creator_fallback, prefix the reason
  with [pending review]. Do not wake the assignee.
- Active → unchanged default (assignee / creator fallback).

Adds _task_lifecycle() in alerts.py; reminders.py wires it through
_fire_policy and the run loop (skipped results do not advance).

Docs: docs/reminder-lifecycle.md with the full state table, routing
priority, envelope changes, and backward-compat notes.

Tests: 3 new — terminal skip+disable, pending_review → review_owner,
pending_review fallback → creator. 220 total pass.

Co-authored-by: anvil <anvil@ax-platform.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs(channel): define CLI presence receipts

* fix(channel): process idle event before reconnect

---------

Co-authored-by: anvil <anvil@ax-platform.com>
Co-authored-by: orion <orion@paxai.app>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@madtank madtank merged commit 641f1ea into main Apr 17, 2026
6 checks passed
@madtank madtank deleted the orion/channel-idle-reconnect-to-main branch April 17, 2026 02:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant