Changeset 0.26.0 (#165)
This commit is contained in:
394
docs/DESIGN-0.26.0.md
Normal file
394
docs/DESIGN-0.26.0.md
Normal file
@@ -0,0 +1,394 @@
|
||||
# DESIGN — v0.26.0: Workflow Engine
|
||||
|
||||
**Status:** Shipped (v0.26.0–v0.26.5, 15 changesets)
|
||||
**Branch:** `0.26.0`
|
||||
**Scope:** Team-owned staged processes, AI intake, human assignment, visitor experience
|
||||
**Depends on:** Dynamic surfaces (v0.25.0), multi-participant channels (v0.23.x),
|
||||
anonymous identity (v0.24.3), permissions (v0.24.2)
|
||||
|
||||
---
|
||||
|
||||
## Phasing
|
||||
|
||||
Six sub-releases. Each is shippable and testable independently.
|
||||
Deferred tech debt from v0.21–v0.25 absorbed into v0.26.0 (phase 0).
|
||||
|
||||
```
|
||||
v0.26.0 Foundation: cleanup + context-aware tool system
|
||||
│
|
||||
v0.26.1 Workflow definitions + versioning (schema + CRUD)
|
||||
│
|
||||
v0.26.2 Workflow instances + stage transitions (runtime)
|
||||
│
|
||||
v0.26.3 Visitor experience (surfaces + branded chat)
|
||||
│
|
||||
v0.26.4 AI intake + assignment queue
|
||||
│
|
||||
v0.26.5 Team collaboration + workflow builder UI
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## v0.26.0 — Foundation
|
||||
|
||||
Cleanup debt, then build the tool infrastructure that workflows need.
|
||||
|
||||
### Deferred Cleanup (absorb from v0.21–v0.25)
|
||||
|
||||
**Session cleanup job**
|
||||
- Background goroutine: delete `session_participants` older than N days
|
||||
with no associated messages
|
||||
- Configurable via `SESSION_EXPIRY_DAYS` env var (default: 30)
|
||||
- Runs on startup + every 6h
|
||||
- Closes v0.24.3 deferred item
|
||||
|
||||
**Stale code TODOs**
|
||||
- `surfaces.go:108-109` — surface delete: clean up static assets + templates
|
||||
from `SURFACE_ASSET_DIR/{id}/` and `SURFACE_TEMPLATE_DIR/{id}/`
|
||||
- `completion.go:164` — remove misleading TODO comment (pricing IS populated
|
||||
in `recordUsage()` at line ~1996)
|
||||
- `intrinsic.go:19` — remove stale "TODO: 0.9.2" comment (shipped in v0.22.0)
|
||||
|
||||
**Roadmap housekeeping**
|
||||
- Mark shipped items in "Technical Debt + Deferred Items" section:
|
||||
- Rate limit tracking (v0.22.4)
|
||||
- Auto-disable policy (v0.22.4)
|
||||
- Tool health recording (v0.22.4)
|
||||
- PDF export via pandoc (v0.22.4)
|
||||
- Persona tool grant enforcement (v0.25.0)
|
||||
- Clean up stale deferred-into version markers
|
||||
|
||||
### Context-Aware Tool System
|
||||
|
||||
_Absorbed from v0.25.0 roadmap. Prerequisite for workflow tool scoping._
|
||||
|
||||
**`ToolContext` struct** (`tools/types.go`)
|
||||
- Fields: `ChannelType`, `WorkspaceID`, `WorkflowID`, `TeamID`,
|
||||
`PersonaID`, `IsVisitor`
|
||||
- Populated from channel record in completion handler before tool execution
|
||||
|
||||
**`Require` predicates**
|
||||
- `Availability() Require` method on `Tool` interface
|
||||
- `BaseTool` embed: defaults to `AlwaysAvailable` (backward compat)
|
||||
- Built-in predicates: `RequireWorkspace`, `RequireWorkflow`,
|
||||
`RequireTeam`, `DenyVisitor`, `All()`
|
||||
- `tools.AvailableFor(tctx, disabled)` replaces `AllDefinitionsFiltered()`
|
||||
|
||||
**Self-declaring tools**
|
||||
- Workspace tools: `RequireWorkspace + DenyVisitor`
|
||||
- Git tools: `RequireWorkspace + DenyVisitor`
|
||||
- `workspace_create`: available only when no workspace bound
|
||||
- Memory/notes tools: `DenyVisitor`
|
||||
- Eliminates `WorkspaceToolNames()` / `GitToolNames()` manual suppression
|
||||
|
||||
**`ExecutionContext` extension**
|
||||
- Add `WorkflowID`, `TeamID` to existing `ExecutionContext`
|
||||
- Populated from channel record in completion handler
|
||||
|
||||
**Persona tool grant enforcement hardening**
|
||||
- v0.25.0 shipped the second-pass allowlist in completion.go:917
|
||||
- For workflows: version snapshot must include persona tool grants
|
||||
at snapshot time (frozen for running instances)
|
||||
|
||||
### Route Namespaces
|
||||
|
||||
Two distinct namespaces. No collision.
|
||||
|
||||
**`/s/:slug` — Extension Surfaces (application extensions)**
|
||||
- Single-page, self-contained sub-applications
|
||||
- Examples: custom dashboard, kanban board, form builder
|
||||
- One template, one JS entry point, scoped CSS
|
||||
- Registered via surface manifest (v0.25.0 `surface_registry`)
|
||||
|
||||
**`/w/:scope/:slug` — Workflows (business logic automation)**
|
||||
- `:scope` is `team-slug` or `global` — workflows belong to a team or
|
||||
are org-wide. Prevents slug collisions across teams.
|
||||
- Multi-page: landing, visitor chat, team review, admin tracking —
|
||||
different views depending on who is accessing (visitor vs. team
|
||||
member vs. admin)
|
||||
- Involves customers and team members, possibly spanning multiple teams
|
||||
- Purpose-built Go templates per view, not a single-page pattern
|
||||
|
||||
Both use Go template rendering. They share `base.html` but have
|
||||
independent template trees and JS entry points.
|
||||
|
||||
### Migration
|
||||
|
||||
- 023_v0260_foundation.sql: session cleanup additions (if schema changes
|
||||
needed), no new tables in this phase
|
||||
|
||||
---
|
||||
|
||||
## v0.26.1 — Workflow Definitions + Versioning
|
||||
|
||||
Schema and CRUD for defining workflows. No runtime yet.
|
||||
|
||||
### Tables
|
||||
|
||||
**`workflows`**
|
||||
- `id`, `team_id` (nullable — NULL = global), `name`,
|
||||
`slug` (unique within scope: `UNIQUE(team_id, slug)` with
|
||||
partial index for global), `description`
|
||||
- `branding` (JSONB: accent color, logo URL, tagline)
|
||||
- `entry_mode` (enum: `public_link`, `team_only`)
|
||||
- `is_active`, `version` (auto-increment on edit)
|
||||
- `on_complete` (JSONB, nullable — v0.27.0 chaining hook, NULL for now.
|
||||
Future schema: `{"action": "start_workflow", "target_slug": "...",
|
||||
"data_map": {...}}`. Column exists early so the table doesn't need
|
||||
migration when chaining lands.)
|
||||
- `retention` (JSONB — `{"mode": "archive"|"delete", "delete_after_days": N}`.
|
||||
Default: `{"mode": "archive"}`)
|
||||
- `created_by`, `created_at`, `updated_at`
|
||||
|
||||
**`workflow_stages`**
|
||||
- `id`, `workflow_id`, `ordinal`, `name`
|
||||
- `persona_id` (FK — persona drives this stage)
|
||||
- `assignment_team_id` (FK — which team handles human review)
|
||||
- `form_template` (JSONB — fields the persona should collect)
|
||||
- `history_mode` (enum: `full`, `summary`, `fresh` — default `full`)
|
||||
- `auto_transition` (bool — advance automatically when form complete)
|
||||
- `transition_rules` (JSONB — conditions, round-robin config)
|
||||
|
||||
**`workflow_versions`**
|
||||
- `id`, `workflow_id`, `version_number`
|
||||
- `snapshot` (JSONB — full serialized definition + stages + tool grants)
|
||||
- `created_at`
|
||||
|
||||
### API
|
||||
|
||||
- Team-admin CRUD: create/edit/delete workflows and stages
|
||||
- Reorder stages (PATCH ordinal)
|
||||
- `workflow.create` permission required
|
||||
- Publish action: snapshot current definition → `workflow_versions`
|
||||
- Slug validation: lowercase, alphanumeric + hyphens, unique within scope
|
||||
(same slug can exist under different teams). URL resolves as
|
||||
`/w/team-slug/workflow-slug` or `/w/global/workflow-slug`.
|
||||
|
||||
### Design Decisions (to flesh out)
|
||||
|
||||
- Branding schema: minimal (accent + logo + tagline) or extensible JSONB?
|
||||
- Stage persona: must be team-scoped persona? Or any accessible persona?
|
||||
|
||||
### Migration
|
||||
|
||||
- 023_v0261_workflows.sql: `workflows`, `workflow_stages`, `workflow_versions`
|
||||
|
||||
---
|
||||
|
||||
## v0.26.2 — Workflow Instances + Stage Transitions
|
||||
|
||||
Channels become workflow runtime containers.
|
||||
|
||||
### Channel Extensions
|
||||
|
||||
- `workflow_id`, `workflow_version` columns on `channels`
|
||||
(populated on workflow channel creation)
|
||||
- `current_stage` (ordinal), `stage_data` (JSONB)
|
||||
- `workflow_status` enum: `active`, `completed`, `stale`, `cancelled`
|
||||
- `last_activity_at` tracking
|
||||
|
||||
### Entry Point
|
||||
|
||||
- Public link: `/w/:scope/:slug` → creates workflow channel, adds anonymous
|
||||
session participant (v0.24.3), binds stage 1 persona, auto-creates
|
||||
workspace. `:scope` is team slug or `global`.
|
||||
- Team-only: same flow but requires authenticated user
|
||||
|
||||
### Stage Transitions
|
||||
|
||||
- AI-triggered: `workflow_advance` tool (see v0.26.4)
|
||||
- Human-triggered: button in channel header
|
||||
- Rejection: return to previous stage with reason text
|
||||
- On transition: swap active persona, notify assignment team,
|
||||
update channel metadata, create channel-scoped note with collected data
|
||||
|
||||
**History mode** (per-stage, configurable in `workflow_stages`):
|
||||
- `full` — new persona sees complete conversation history with a system
|
||||
boundary message (same pattern as @mention context boundaries, v0.23.0).
|
||||
Simplest to implement, best for continuity-sensitive workflows.
|
||||
- `summary` — utility-role summarization of prior stages injected as a
|
||||
system message. New persona starts with context but not raw history.
|
||||
- `fresh` — new persona sees nothing from prior stages. Clean slate.
|
||||
Simplest for independent review stages.
|
||||
|
||||
Default: `full`. Stored as `history_mode` enum on `workflow_stages`.
|
||||
|
||||
### Staleness Sweep
|
||||
|
||||
- Background goroutine (like health accumulator pattern)
|
||||
- Marks idle instances as `stale` after configurable threshold
|
||||
- Stale UX: visitor sees "Continue or Start Over"
|
||||
(start over = new instance on latest version)
|
||||
|
||||
### Migration
|
||||
|
||||
- Adds columns to `channels`, or separate `workflow_instances` table
|
||||
(TBD — inline columns simpler, separate table cleaner)
|
||||
|
||||
---
|
||||
|
||||
## v0.26.3 — Visitor Experience
|
||||
|
||||
Purpose-built surfaces for anonymous workflow participants.
|
||||
|
||||
### Landing Page
|
||||
|
||||
- `GET /w/:scope/:slug` — branded page with persona avatar, description, "Start"
|
||||
- Go template: `workflow-landing.html`
|
||||
- Reads `workflow.branding` JSONB for accent color, logo, tagline
|
||||
- System dark/light mode (no theme toggle — visitors don't have prefs)
|
||||
|
||||
### Visitor Chat
|
||||
|
||||
- `GET /w/:scope/:slug/c/:channelId` — bubble chat (NOT full ChatPane)
|
||||
- `workflow-chat.js`: lightweight WebSocket client
|
||||
- Message send/receive
|
||||
- Markdown rendering (marked.js + DOMPurify, already available)
|
||||
- Typing indicator
|
||||
- Stage transition animation (visual feedback on advance)
|
||||
- Scoped: no sidebar, no settings, no navigation
|
||||
- Session auth via `AuthOrSession` middleware (v0.24.3)
|
||||
|
||||
### Tool Scoping
|
||||
|
||||
- `DenyVisitor` predicates prevent visitors from accessing workspace,
|
||||
git, memory, notes tools
|
||||
- Persona tool grants further restrict per-stage
|
||||
|
||||
### Design Decisions (to flesh out)
|
||||
|
||||
- Visitor can upload files? (attachments to workflow channel)
|
||||
- Visitor can see previous stage history? (probably not — each stage
|
||||
is a clean persona conversation)
|
||||
- Mobile-first layout for visitor surfaces
|
||||
|
||||
---
|
||||
|
||||
## v0.26.4 — AI Intake + Assignment Queue
|
||||
|
||||
The AI does the data collection. Humans review and act.
|
||||
|
||||
### `workflow_advance` Tool
|
||||
|
||||
- `RequireWorkflow` availability predicate
|
||||
- Input: collected form data (JSONB matching `form_template`)
|
||||
- Validates all required fields present
|
||||
- Triggers stage transition (same path as human-triggered)
|
||||
- Creates channel-scoped note with structured form response
|
||||
|
||||
### Form Template → System Prompt
|
||||
|
||||
- Stage `form_template` JSONB injected into persona system prompt:
|
||||
"Collect the following information from the visitor: [field list]"
|
||||
- Persona conducts conversational intake
|
||||
- When all fields gathered → calls `workflow_advance` with data
|
||||
- Rejection: persona sees rejection reason in conversation, re-collects
|
||||
|
||||
### Assignment Queue
|
||||
|
||||
**`workflow_assignments` table**
|
||||
- `id`, `channel_id`, `stage` (ordinal), `team_id`
|
||||
- `assigned_to` (nullable user_id), `status` (unassigned/claimed/completed)
|
||||
- `created_at`, `claimed_at`, `completed_at`
|
||||
|
||||
**Claim model**
|
||||
- Unassigned workflow channels visible to all team members
|
||||
- Optimistic lock: `UPDATE ... WHERE status = 'unassigned'`
|
||||
- Round-robin auto-assignment (optional, per-stage `transition_rules`)
|
||||
|
||||
**Notifications**
|
||||
- New workflow assignment → notification to team (existing infra)
|
||||
- WebSocket push to online team members
|
||||
- Claim confirmation notification to claimer
|
||||
|
||||
### Migration
|
||||
|
||||
- `workflow_assignments` table
|
||||
|
||||
---
|
||||
|
||||
## v0.26.5 — Team Collaboration + Workflow Builder UI
|
||||
|
||||
Frontend surfaces for building and managing workflows.
|
||||
|
||||
### Team Member View
|
||||
|
||||
- Assigned member sees full history (AI intake + artifacts + notes)
|
||||
- Persona remains active — assists both visitor and team member
|
||||
- Member can: advance stage, reject (with reason), add notes,
|
||||
invoke tools, reassign, escalate
|
||||
|
||||
### Workflow Builder
|
||||
|
||||
- Team admin panel section
|
||||
- Stage list editor: add/remove/reorder stages
|
||||
- Per-stage config: persona picker, assignment team, form template editor
|
||||
- Branding editor: accent color, logo upload, tagline
|
||||
- Publish button (creates version snapshot)
|
||||
- Read-only tool grants display per stage persona
|
||||
|
||||
### Channel Header (Workflow Mode)
|
||||
|
||||
- Workflow stage indicator (step N of M, stage name)
|
||||
- Advance / Reject buttons (permission-gated)
|
||||
- Assignment info (who claimed, when)
|
||||
- History mode indicator (full/summary/fresh)
|
||||
|
||||
### Queue UI
|
||||
|
||||
Prototype both approaches in v0.26.5, decide based on usage:
|
||||
|
||||
**Option A: Sidebar section** for team members — shows unassigned workflow
|
||||
channels for user's teams, count badge, click opens in main pane.
|
||||
Low friction, always visible.
|
||||
|
||||
**Option B: Dedicated surface** (`/workflows` or team-admin view) for
|
||||
tracking and management — full table with filters, assignment history,
|
||||
metrics. Better for high-volume operations.
|
||||
|
||||
Likely outcome: both. Sidebar for team members (day-to-day claims),
|
||||
dedicated surface for team admins (tracking, reporting, bulk ops).
|
||||
|
||||
---
|
||||
|
||||
## Resolved Decisions
|
||||
|
||||
1. **Workflow channel lifecycle.** Configurable per-workflow retention
|
||||
policy. Default: archive (read-only, `ai_mode='off'`, no new messages).
|
||||
Optional: auto-delete after N days. Stored as `retention` JSONB on
|
||||
`workflows` table.
|
||||
|
||||
2. **Multi-stage persona conversations.** Three modes, configurable
|
||||
per-stage via `history_mode` enum on `workflow_stages`:
|
||||
- `full` — complete history + boundary message (default, simplest)
|
||||
- `summary` — utility-role summarization injected as system message
|
||||
- `fresh` — clean slate, no prior context
|
||||
First and third are simplest to implement; ship those first,
|
||||
`summary` can land as a follow-up.
|
||||
|
||||
3. **Visitor re-entry.** Resume if session cookie matches and instance
|
||||
is `active`. Otherwise start new on latest version.
|
||||
|
||||
4. **Workflow-to-workflow chaining.** Deferred to v0.27.0 (tasks/agents).
|
||||
`on_complete` JSONB column on `workflows` table ships as nullable in
|
||||
v0.26.1 migration so the schema is pre-wired. v0.27.0 populates it
|
||||
and adds the trigger logic. No migration needed when chaining lands.
|
||||
|
||||
5. **Route namespaces.** `/s/:slug` for extension surfaces (single-page
|
||||
sub-apps). `/w/:scope/:slug` for workflows (multi-page, role-dependent
|
||||
views). No collision. See "Route Namespaces" in v0.26.0.
|
||||
|
||||
---
|
||||
|
||||
## Cross-Cutting Concerns
|
||||
|
||||
**Testing strategy:** Integration tests per phase. v0.26.0 tool context
|
||||
tests. v0.26.1 workflow CRUD + versioning tests. v0.26.2 stage transition
|
||||
tests. v0.26.4 assignment claim concurrency test.
|
||||
|
||||
**Migration numbering:** Single migration per phase (023, 024, ...) or
|
||||
consolidated? Leaning toward: one per phase, squash at tag time if needed.
|
||||
|
||||
**Backward compat:** Non-workflow channels unaffected. `ToolContext` with
|
||||
`BaseTool` defaults means existing tools work without changes. Workflow
|
||||
columns on `channels` are nullable.
|
||||
Reference in New Issue
Block a user