Story 2.12: UI-Initiated Session Configuration Collection
Epic: Epic 2 - Session Management & Async Interaction
Story Key: 2-12-ui-session-config
Created: 2026-02-02
Priority: High
Story
As a developer,
I want sessions created in OpenCode UI to collect configuration lazily based on actual needs,
So that I can start exploratory sessions with zero friction and only provide builder/Slack config when required.
Acceptance Criteria
AC1: Session Creation Without Config
Given User creates session in OpenCode UI with title “Add rate limiting”
When Session is created
Then Session is registered with minimal state (id, title, directory)
And No builder or Slack configuration is collected yet
And Session type is “exploratory”
AC2: Exploratory Work Without Prompts
Given Exploratory session is active
When User asks questions and agent reads code
Then Work proceeds without any configuration prompts
And Agent uses Read, Grep, Glob tools freely
AC3: Write Operation Triggers Builder Config
Given User attempts first write operation (Edit, Write, Bash with file modification)
When Plugin intercepts write tool execution
Then Execution is paused
And User is prompted for builder configuration
AC4: OpenCode Modal for Builder Config (When in Web UI)
Given User is in OpenCode web UI when write is attempted
When Builder config prompt is needed
Then OpenCode modal appears with fields:
- Project: [Dropdown of projects from
.ai/projectlist.mdor “Create New”] - Workspace Name: [Auto-filled:
{project-id}-{task-slug}] - Repositories: [Multi-select, pre-selected based on detected repos]
- Category: [Select, inferred from project]
And Optional section: “Setup Slack notifications now?” (unchecked by default)
AC5: Slack Form for Builder Config (When in Slack)
Given User is in Slack when write is attempted
When Builder config prompt is needed
Then Slack form appears in existing thread or DM with same fields
And Optional section: “Setup Slack notifications?” (checked by default)
AC6: Builder Initialization on Config Submission
Given Builder config is provided
When User submits form
Then Gateway invokes task builder:init BUILDER_NAME={workspace_name} REPOS={repos}
And Builder workspace is created at .builders/{workspace_name}/
And Session is moved to builder workspace
And Session type changes to “work”
And Write operation proceeds
AC7: Optional Bundled Slack Config
Given Builder config form includes optional Slack section
When User enables Slack notifications and submits
Then Both builder and Slack configs are saved
And Slack thread is created in specified channel
And Future notifications route to Slack
AC8: Going Offline Triggers Slack Config
Given User is in OpenCode web UI working
When User goes offline (presence detection: no web activity >10min)
Then Next notification triggers Slack config collection
And Slack form appears: “Where should I post updates for ‘{session.title}’?”
And Fields: Channel (inferred), Priority (medium default)
AC9: Slack Config Creates Thread
Given Slack config is provided via form
When User submits
Then Slack thread is created in chosen channel
And Pending notification is posted to thread
And Mapping session_id → thread_ts is saved to .session-state.yaml
AC10: Independent Config Collection Order
Given User creates session, goes offline, then starts building
When Both configs are eventually needed
Then Slack config collected first (when going offline)
And Builder config collected second (when attempting write in Slack)
And Both configs can be collected in either order
AC11: Project ID Inference
Given Project inference runs on session title “Add auth to OpenCode Slack”
When System matches against .ai/projectlist.md
Then Project 0012 (OpenCode Slack Integration) is suggested
And Workspace name defaults to “0012-add-auth-to-opencode-slack”
And Repositories default to [“ai-dev”] (from project metadata)
And Category defaults to “ai-dev”
AC12: Project Selection Fallback
Given Project inference cannot determine project with confidence
When Multiple projects match or none match
Then Dropdown shows all active projects
And “Create New Project” option is available
And If selected, next available project ID (e.g., 0014) is assigned
AC13: Builder Config State Persistence
Given Session with builder config exists
When Gateway restarts
Then .session-state.yaml is loaded from PVC
And Builder config (project_id, workspace_name, repos, category) is restored
And Session can resume work without re-prompting
AC14: Slack Config State Persistence
Given Session with Slack config exists
When Gateway restarts
Then Slack thread mapping is loaded from state file
And Future notifications route to correct thread
And No duplicate threads are created
AC15: Single-Form Both Configs
Given User opts to provide both configs at once
When Builder config form shows optional Slack section
Then User can check “Setup Slack notifications now”
And Single form submission provides both configs
And No second prompt occurs later
Tasks/Subtasks
Task 1: Gateway - Session Config Manager
- 1.1: Create
SessionConfigManagerclass inservices/gateway/services/session_config.py - 1.2: Implement
ensure_builder_config(session_id)method with presence detection - 1.3: Implement
ensure_slack_config(session_id)method - 1.4: Add project ID inference from
.ai/projectlist.md - 1.5: Add workspace name generation:
{project-id}-{slugified-title} - 1.6: Add repository/category inference from git remotes and paths
Task 2: Gateway - Builder Initialization
- 2.1: Create builder initialization service in
services/gateway/services/builder.py - 2.2: Implement
init_builder(workspace_name, repositories)callingtask builder:init - 2.3: Handle builder creation success/failure responses
- 2.4: Update session state with builder config and workspace path
Task 3: Gateway - Slack Form Prompts
- 3.1: Create Slack form builder in
services/gateway/services/slack_forms.py - 3.2: Implement builder config form (with optional Slack section)
- 3.3: Implement Slack config-only form
- 3.4: Handle form submissions and extract config data
- 3.5: Create Slack threads and store
thread_tsmapping
Task 4: Bridge Plugin - Write Detection
- 4.1: Add
tool.beforeExecutehook in plugin - 4.2: Detect write operations:
mcp_edit,mcp_write, bash with file modifications - 4.3: Check session for builder config before allowing write
- 4.4: Send builder config request to Gateway if missing
- 4.5: Pause tool execution until config provided
Task 5: Bridge Plugin - OpenCode Modal (Optional - depends on SDK capability)
- 5.1: Research OpenCode SDK modal/form capabilities
- 5.2: If supported: Implement builder config modal prompt
- 5.3: If not supported: Skip (use Slack form fallback)
Task 6: Gateway - Presence Detection
- 6.1: Track
last_web_activitytimestamp per user - 6.2: Update timestamp on web UI interactions (WebSocket messages from Bridge)
- 6.3: Implement presence check:
is_user_in_web_ui()(activity within 10min) - 6.4: Route config prompts to OpenCode vs Slack based on presence
Task 7: Gateway - State Persistence
- 7.1: Extend
.session-state.yamlschema withbuilder_configandslack_config - 7.2: Save builder config to state on initialization
- 7.3: Save Slack config to state on thread creation
- 7.4: Load both configs on Gateway restart
- 7.5: Restore session mappings (
session_id → thread_ts)
Task 8: Integration Testing
- 8.1: Test exploratory session (no config prompts)
- 8.2: Test write triggers builder config prompt
- 8.3: Test builder initialization and workspace creation
- 8.4: Test going offline triggers Slack config
- 8.5: Test both configs collected in different orders
- 8.6: Test project inference (match/no match)
- 8.7: Test state recovery after Gateway restart
- 8.8: Test optional bundled config submission
Dev Notes
Architecture Context
Refer to .ai/projects/ai-dev/opencode-slack-integration/architecture.md Decision 15 for complete architectural details.
Key Implementation Points
- Two Independent Configs: Builder config and Slack config are orthogonal - can be collected in any order
- Lazy Collection: Only prompt when actually needed (write for builder, offline for Slack)
- Smart Inference: Pre-fill forms with detected values from session context
- Presence-Aware Routing: Prompt in OpenCode if user present, Slack if offline/unknown
- Optional Bundling: Single form can collect both configs to minimize interruptions
Component Files
services/gateway/services/session_config.py- Config managerservices/gateway/services/builder.py- Builder initializationservices/gateway/services/slack_forms.py- Slack form buildersservices/gateway/models/session.py- Session state schema extensionsplugins/opencode-bridge/src/handlers/write-detection.ts- Write interception
State Schema Extension
session:
id: ses_123
title: "Add rate limiting"
type: exploratory | work
builder_config:
project_id: "0012"
workspace_name: "0012-add-rate-limiting"
repositories: ["ai-dev", "k8s-lab"]
category: "ai-dev"
workspace_path: ".builders/0012-add-rate-limiting"
slack_config:
channel: "#ai-dev-builds"
thread_ts: "1234567890.123456"
priority: "high"Test Data
- Sample project list: Use
.ai/projectlist.md(projects 0001-0013) - Test session titles that match/don’t match projects
- Test repo detection in various workspace structures
Dev Agent Record
Debug Log
Completion Notes
File List
Change Log
- 2026-02-02: Story created from Epic 2 Story 2.12
Status
in-progress