Tasks: Slack Bot AG-UI Migration
Input: Design documents from docs/docs/specs/100-slack-agui-migration/
Prerequisites: plan.md (required), spec.md (required), research.md, data-model.md, contracts/sse-events.md
Tests: Included β spec requires A2A tests be replaced with AG-UI tests (Phase 6 in plan.md).
Organization: Tasks are grouped by user story. Stories US1 and US2 share foundational work (Phases 1β2) and can proceed to their specific logic in parallel after that. US3βUS6 build on the foundation and can also proceed independently.
Format: [ID] [P?] [Story] Descriptionβ
- [P]: Can run in parallel (different files, no dependencies)
- [Story]: Which user story this task belongs to (e.g., US1, US2, US3)
- Include exact file paths in descriptions
Path Conventionsβ
- Slack bot source:
ai_platform_engineering/integrations/slack_bot/ - Tests:
ai_platform_engineering/integrations/slack_bot/tests/ - Docker:
docker-compose.dev.yaml,docker-compose.yaml(repo root) - Spec docs:
docs/docs/specs/100-slack-agui-migration/
Phase 1: Foundation β Client & Configβ
Purpose: Rewrite SSE client for AG-UI endpoints, update config models, delete A2A client
Commit: feat(slack-bot): rewrite SSE client for AG-UI endpoints
- T001 [P] [US1] Rewrite
sse_client.pyβ updateSSEClientto targetPOST /api/v1/chat/stream/start?protocol=aguiwithChatRequestbody (message,conversation_id,agent_id,trace_id); replacerequestswithhttpxstreaming; keepSSEEventTypeenum,SSEEventdataclass,_get_headers()withX-Client-Source: slack-botand OAuth2 Bearer token inai_platform_engineering/integrations/slack_bot/sse_client.py - T002 [P] [US1] Add
SSEEventfields β addoutcome: str | None,interrupt: dict | None,steps: list | None,snapshot: dict | Nonefields to theSSEEventdataclass inai_platform_engineering/integrations/slack_bot/sse_client.py - T003 [US2] Add
invoke()method toSSEClientβPOST /api/v1/chat/invokewithChatRequestbody, returns parsed JSON dict inai_platform_engineering/integrations/slack_bot/sse_client.py - T004 [US3] Add
resume_stream()method toSSEClientβPOST /api/v1/chat/stream/resume?protocol=aguiwithResumeRequestbody (agent_id,conversation_id,form_data,trace_id), returns SSE stream inai_platform_engineering/integrations/slack_bot/sse_client.py - T005 [US1] Add
thread_ts_to_conversation_id()helper β deterministic UUID v5 fromthread_tsusingSLACK_NAMESPACE = uuid5(NAMESPACE_URL, "slack.caipe.io")inai_platform_engineering/integrations/slack_bot/sse_client.py - T006 [P] [US4] Update
ChannelConfigβ addagent_id: Optional[str] = Nonefield inai_platform_engineering/integrations/slack_bot/utils/config_models.py - T007 [P] [US4] Update
GlobalDefaultsβ adddefault_agent_id: Optional[str] = Nonefield with validation warning whenai_enabled=Trueand noagent_idis set inai_platform_engineering/integrations/slack_bot/utils/config_models.py - T008 [US6] Delete
a2a_client.pyβ removeai_platform_engineering/integrations/slack_bot/a2a_client.pyentirely
Checkpoint: SSE client targets AG-UI endpoints with three methods (stream, invoke, resume). Config models support agent_id. A2A client deleted.
Phase 2: Streaming Handlerβ
Purpose: Replace A2A streaming handler with AG-UI event mapping, delete A2A event parser
Commit: feat(slack-bot): replace A2A streaming handler with AG-UI event mapping
- T009 [US1] Rewrite
stream_response()inai.pyβ replacestream_a2a_response()with AG-UI event mapping:TEXT_MESSAGE_STARTβchat_startStream,TEXT_MESSAGE_CONTENTβStreamBuffer.append,TEXT_MESSAGE_ENDβ flush,TOOL_CALL_START/ENDβ task_update chunks,RUN_FINISHED(success)βchat_stopStreamwith feedback/footer,RUN_FINISHED(interrupt)β delegate to HITL handler,RUN_ERRORβ error blocks,CUSTOM(NAMESPACE_CONTEXT)β track subagent,CUSTOM(WARNING)β log warning inai_platform_engineering/integrations/slack_bot/utils/ai.py - T010 [US2] Implement
invoke_response()inai.pyβ callsse_client.invoke(), postcontentviachat_postMessagewith feedback blocks and footer, handle errors with{"retry_needed": True}inai_platform_engineering/integrations/slack_bot/utils/ai.py - T011 [US1] Keep and verify unchanged functions β confirm
StreamBuffer,_build_footer_text(),_build_stream_final_blocks(),_post_final_response(),_check_overthink_skip(),RETRY_PROMPT_PREFIXremain unchanged inai_platform_engineering/integrations/slack_bot/utils/ai.py - T012 [US1] Update
handle_ai_alert_processing()β update to use new SSE client andagent_idparameter instead of A2A client inai_platform_engineering/integrations/slack_bot/utils/ai.py - T013 [US6] Delete
event_parser.pyβ removeai_platform_engineering/integrations/slack_bot/utils/event_parser.pyentirely
Checkpoint: ai.py provides stream_response() and invoke_response() using AG-UI events. A2A event parser deleted. StreamBuffer and helper functions preserved.
Phase 3: User Story 3 β HITL Support (Priority: P2)β
Goal: Update HITL handler to parse AG-UI interrupt format and resume via dynamic agents
Commit: feat(slack-bot): update HITL handler for AG-UI interrupt format
Independent Test: Trigger an agent workflow that requires human input, verify the form renders in Slack, submit a response, confirm the agent resumes.
Implementationβ
- T014 [US3] Implement
parse_agui_interrupt()β parseSSEEventwithtype=RUN_FINISHEDandoutcome="interrupt", extractid,reason,payload.prompt,payload.fields,payload.agentfrominterruptdict, map AG-UI field types to Slack Block Kit (textβplain_text_input,selectβstatic_select,multiselectβmulti_static_select,booleanβbutton pair,number/url/emailβplain_text_input), returnHITLFormwithform_id=interrupt.idinai_platform_engineering/integrations/slack_bot/utils/hitl_handler.py - T015 [US3] Update
HITLCallbackHandler.__init__()β change constructor to__init__(self, sse_client)(removesession_managerparam), storesse_clientfor resume calls inai_platform_engineering/integrations/slack_bot/utils/hitl_handler.py - T016 [US3] Update
HITLCallbackHandler.handle_interaction()β extractconversation_id,agent_id,interrupt_idfrom action value JSON; on Approve: collect form values β JSON string βsse_client.resume_stream(); on Reject:sse_client.resume_stream()with rejection message inai_platform_engineering/integrations/slack_bot/utils/hitl_handler.py - T017 [US3] Store resume context in HITL form β embed
conversation_id,agent_idin button actionvalueJSON so resume handler can extract them inai_platform_engineering/integrations/slack_bot/utils/hitl_handler.py
Checkpoint: HITL interrupt events from AG-UI are parsed, rendered as Slack forms, and submissions resume the agent via dynamic agents.
Phase 4: User Story 1 + 2 + 4 β App.py Rewiring (Priority: P1/P2)β
Goal: Rewire app.py event handlers for dynamic agents, simplify session manager to deterministic UUIDs
Commit: refactor(slack-bot): rewire app.py for dynamic agents
Independent Test: Send a real user message and a bot user message; verify streaming and invoke paths work end-to-end. Verify two channels route to different agents.
Implementationβ
- T018 [US1] Update
app.pyinitialization β replaceSUPERVISOR_SSE_URL/CAIPE_URLwithDYNAMIC_AGENTS_URLenv var; createSSEClient(DYNAMIC_AGENTS_URL, timeout=300, auth_client=auth_client); update health check toGET {DYNAMIC_AGENTS_URL}/healthz; updateHITLCallbackHandler(sse_client)withoutsession_managerparam; remove A2A-specific imports inai_platform_engineering/integrations/slack_bot/app.py - T019 [US1] Update
handle_mentionhandler β getagent_idfromchannel_config.agent_id(fallback toconfig.defaults.default_agent_id), generateconversation_idviathread_ts_to_conversation_id(thread_ts), branch on user type: real user βai.stream_response(), bot user βai.invoke_response()inai_platform_engineering/integrations/slack_bot/app.py - T020 [US1] Update
handle_dm_messagehandler β sameagent_id/conversation_idpattern ashandle_mention, branch on user type inai_platform_engineering/integrations/slack_bot/app.py - T021 [P] [US1] Update
handle_qanda_messagehandler β sameagent_id/conversation_idpattern, branch on user type inai_platform_engineering/integrations/slack_bot/app.py - T022 [US3] Update
handle_hitl_actionhandler β callhitl_handler.handle_interaction(), process resume SSE stream withstream_response()in the original thread inai_platform_engineering/integrations/slack_bot/app.py - T023 [P] [US1] Update feedback, retry, and alert handlers β pass
agent_idandconversation_idto updated function signatures inai_platform_engineering/integrations/slack_bot/app.py - T024 [US1] Simplify
session_manager.pyβ replaceget_context_id(thread_ts)with deterministicthread_ts_to_conversation_id(thread_ts)(no API call); removeset_context_id(); remove supervisor API calls; removesupervisor_urlandauth_clientconstructor params; keep TTL caches (_user_info_cache,_skipped_cache,_escalated_threads); remove_context_cacheand_trace_cacheinai_platform_engineering/integrations/slack_bot/utils/session_manager.py
Checkpoint: All event handlers use dynamic agents. Session manager is stateless for conversation IDs. Streaming, invoke, HITL, and channel routing all wired up.
Phase 5: User Story 5 + Docker/Config (Priority: P3)β
Goal: Update Docker dependencies, environment variables, and version bump
Commit: chore(slack-bot): update Docker config for dynamic agents
Independent Test: docker compose -f docker-compose.dev.yaml config validates without errors; slack-bot service depends on dynamic-agents.
Implementationβ
- T025 [P] [US5] Update
docker-compose.dev.yamlβ changeslack-bot.depends_onfromcaipe-supervisortodynamic-agents; replaceCAIPE_URLwithDYNAMIC_AGENTS_URL=http://dynamic-agents:8100; removeSUPERVISOR_SSE_URL; keepCAIPE_UI_URLindocker-compose.dev.yaml - T026 [P] [US5] Update
docker-compose.yamlβ same dependency and env var changes as dev; update image tag to0.4.0indocker-compose.yaml - T027 [P] [US5] Version bump
pyproject.tomlβ change version from0.3.0to0.4.0inai_platform_engineering/integrations/slack_bot/pyproject.toml
Checkpoint: Docker configs target dynamic agents. Version is 0.4.0. Slack conversations remain isolated from web UI by using deterministic UUID v5 IDs that are not queried by the web UI.
Phase 6: Testsβ
Goal: Replace A2A tests with AG-UI tests, update existing test fixtures
Commit: test(slack-bot): replace A2A tests with AG-UI tests
Independent Test: uv run pytest tests/ -v passes with zero failures.
Delete A2A Testsβ
- T028 [P] [US6] Delete
test_a2a_client.pyβ removeai_platform_engineering/integrations/slack_bot/tests/test_a2a_client.py - T029 [P] [US6] Delete
test_a2a_streaming.pyβ removeai_platform_engineering/integrations/slack_bot/tests/test_a2a_streaming.py - T030 [P] [US6] Delete A2A test data β remove
ai_platform_engineering/integrations/slack_bot/tests/test_data/a2a_heavy_search.jsonandai_platform_engineering/integrations/slack_bot/tests/test_data/a2a_jira_ticket_creation.json
New AG-UI Testsβ
- T031 [P] [US1] Create
test_sse_client.pyβ test SSEClient init,stream_chat()endpoint/params,resume_stream(),invoke(),thread_ts_to_conversation_id()determinism, auth headers inai_platform_engineering/integrations/slack_bot/tests/test_sse_client.py - T032 [P] [US1] Create
test_streaming.pyβ AG-UI event stream replay tests: text streaming, tool calls, plan steps, error recovery, HITL interrupt delegation inai_platform_engineering/integrations/slack_bot/tests/test_streaming.py - T033 [P] [US3] Create
test_hitl_handler.pyβ testparse_agui_interrupt(), field type mapping (text, select, multiselect, boolean, number), form rendering, resume submission, rejection handling inai_platform_engineering/integrations/slack_bot/tests/test_hitl_handler.py
Update Existing Testsβ
- T034 [P] [US1] Update
conftest.pyβ update env vars (DYNAMIC_AGENTS_URLinstead ofCAIPE_URL), addagent_idto channel config fixture, update SSEClient fixture inai_platform_engineering/integrations/slack_bot/tests/conftest.py - T035 [P] [US4] Update
test_config.pyβ addagent_idvalidation tests, defaultdefault_agent_idfallback, warning whenai_enabled=Truewith noagent_idinai_platform_engineering/integrations/slack_bot/tests/test_config.py - T036 [P] [US1] Update
test_ai.pyβ update forstream_response()/invoke_response()signatures, AG-UI event fixtures inai_platform_engineering/integrations/slack_bot/tests/test_ai.py - T037 [P] [US1] Update
test_ai_plan_streaming.pyβ update SSE event fixtures from A2A to AG-UI format inai_platform_engineering/integrations/slack_bot/tests/test_ai_plan_streaming.py - T038 [P] [US1] Update
test_error_recovery.pyβ update for new handler signature and AG-UI error events (RUN_ERROR) inai_platform_engineering/integrations/slack_bot/tests/test_error_recovery.py - T039 [P] [US5] Update
test_metadata_leak_e2e.pyβ update SSE event fixtures to AG-UI format, verify no metadata leaks inai_platform_engineering/integrations/slack_bot/tests/test_metadata_leak_e2e.py - T040 [P] [US1] Update
test_mongodb_session.pyβ update for deterministic UUID (no API mocking needed, testthread_ts_to_conversation_id()) inai_platform_engineering/integrations/slack_bot/tests/test_mongodb_session.py
Checkpoint: All tests pass. Zero A2A test references remain. AG-UI streaming, invoke, HITL, config, and error recovery are covered.
Phase 7: Polish & Cross-Cutting Concernsβ
Purpose: Final verification and cleanup across all user stories
- T041 Run
uv run ruff checkon all modified files and fix any linting issues inai_platform_engineering/integrations/slack_bot/ - T042 Run
uv run pytest tests/ -vand confirm zero failures inai_platform_engineering/integrations/slack_bot/tests/ - T043 Verify A2A code removal β confirm zero references to
a2a_client,A2AClient,send_message_stream,event_parserin Slack bot codebase - T044 Run quickstart.md validation scenarios (Scenarios 1β8) against a running environment
- T045 Update
docs/docs/specs/100-slack-agui-migration/status from Draft to Complete
Dependencies & Execution Orderβ
Phase Dependenciesβ
- Phase 1 (Foundation): No dependencies β can start immediately
- Phase 2 (Streaming Handler): Depends on Phase 1 (T001βT005 for SSEClient methods)
- Phase 3 (HITL): Depends on Phase 1 (T004 for
resume_stream()) and Phase 2 (T009 forstream_response()HITL delegation) - Phase 4 (App.py Rewiring): Depends on Phases 1β3 (all client/handler functions must exist)
- Phase 5 (Docker/Config): No code dependencies β can run in parallel with Phases 2β4
- Phase 6 (Tests): Depends on Phases 1β4 (tests exercise the new code)
- Phase 7 (Polish): Depends on all prior phases
User Story Dependenciesβ
- US1 (Streaming): Foundation (Phase 1) + Streaming Handler (Phase 2) + App.py Rewiring (Phase 4) β core path
- US2 (Invoke): Foundation (Phase 1, T003) + Streaming Handler (Phase 2, T010) + App.py Rewiring (Phase 4, T019βT020) β can be implemented alongside US1
- US3 (HITL): Foundation (Phase 1, T004) + HITL Handler (Phase 3) + App.py Rewiring (Phase 4, T022) β depends on US1 streaming being functional
- US4 (Routing): Foundation (Phase 1, T006βT007) + App.py Rewiring (Phase 4, T019βT021) β config changes are independent; wiring depends on US1
- US5 (Isolation): Docker/Config (Phase 5) β independent of code changes; relies on deterministic UUIDs from T005
- US6 (A2A Removal): Deletions in Phase 1 (T008), Phase 2 (T013), Phase 6 (T028βT030) β can happen as soon as replacement code is in place
Within Each Phaseβ
- Tasks marked [P] within the same phase can run in parallel
- Tasks without [P] must run sequentially within their phase
- T001 and T002 are parallel (both modify
sse_client.pybut different sections) - T006 and T007 are parallel with T001 (different files)
- T009 depends on T001βT005 (uses new SSEClient)
- T010 depends on T003 (uses
invoke())
Parallel Opportunitiesβ
- Phase 1: T001+T002 (sse_client.py), T006+T007 (config_models.py), T008 (delete) β all three groups can run in parallel
- Phase 5: T025+T026+T027 β all three are independent files
- Phase 6: All delete tasks (T028βT030), all new tests (T031βT033), all update tasks (T034βT040) β can run in parallel within each group
- Cross-phase: Phase 5 (Docker) can run in parallel with Phases 2β4 (code changes)
Parallel Example: Phase 1β
# Launch config and client tasks in parallel (different files):
Task: "Rewrite SSEClient for AG-UI endpoints in sse_client.py"
Task: "Add agent_id to ChannelConfig in config_models.py"
Task: "Add default_agent_id to GlobalDefaults in config_models.py"
# Then sequentially (same file, depends on T001):
Task: "Add invoke() method to SSEClient in sse_client.py"
Task: "Add resume_stream() method to SSEClient in sse_client.py"
Task: "Add thread_ts_to_conversation_id() helper in sse_client.py"
# Independent (can run anytime):
Task: "Delete a2a_client.py"
Parallel Example: Phase 6β
# Launch all delete tasks in parallel:
Task: "Delete test_a2a_client.py"
Task: "Delete test_a2a_streaming.py"
Task: "Delete A2A test data files"
# Launch all new test creation in parallel:
Task: "Create test_sse_client.py"
Task: "Create test_streaming.py"
Task: "Create test_hitl_handler.py"
# Launch all test updates in parallel:
Task: "Update conftest.py"
Task: "Update test_config.py"
Task: "Update test_ai.py"
Task: "Update test_ai_plan_streaming.py"
Task: "Update test_error_recovery.py"
Task: "Update test_metadata_leak_e2e.py"
Task: "Update test_mongodb_session.py"
Implementation Strategyβ
MVP First (US1 + US2 β P1 Stories)β
- Complete Phase 1: Foundation (T001βT008)
- Complete Phase 2: Streaming Handler (T009βT013)
- Complete Phase 4: App.py Rewiring β US1/US2 tasks only (T018βT021, T023βT024)
- STOP and VALIDATE: Test streaming (Scenario 1) and invoke (Scenario 2) end-to-end
- Deploy to dev and verify
Incremental Deliveryβ
- Complete Phases 1β2 + Phase 4 (US1/US2 tasks) β MVP functional
- Add Phase 3 + Phase 4 T022 (HITL) β Test Scenario 4 β HITL works
- Add Phase 5 (Docker) β Test Scenario 5 (routing) β Config updated
- Add Phase 6 (Tests) β
uv run pytestpasses β Full test coverage - Phase 7 (Polish) β Run quickstart validation β Release ready
Single Developer Strategyβ
Recommended order for a single developer:
- Phase 1 (Foundation) β one commit
- Phase 2 (Streaming Handler) β one commit
- Phase 3 (HITL) β one commit
- Phase 4 (App.py Rewiring) β one commit
- Phase 5 (Docker/Config) β one commit
- Phase 6 (Tests) β one commit
- Phase 7 (Polish) β no commit (verification only)
Each phase = one conventional commit per plan.md.
Notesβ
- [P] tasks = different files, no dependencies
- [Story] label maps task to specific user story for traceability
- Each user story should be independently completable and testable
- Commit after each phase (6 commits total per plan.md)
- Stop at any checkpoint to validate independently
- Avoid: vague tasks, same file conflicts, cross-story dependencies that break independence
- All file paths are relative to repository root