Changeset 0.35.0 (#209)

Co-authored-by: Jeffrey Smith <jasafpro@gmail.com>
Co-committed-by: Jeffrey Smith <jasafpro@gmail.com>
This commit is contained in:
2026-03-20 09:59:53 +00:00
committed by xcaliber
parent d16bb93177
commit bf8082e69f
37 changed files with 2324 additions and 129 deletions

View File

@@ -563,12 +563,14 @@ func (s *ChannelStore) CountAll(ctx context.Context) (int, error) {
// ── Workflow instance state (v0.29.0-cs3) ───────────────────────────────
func (s *ChannelStore) SetWorkflowInstance(ctx context.Context, channelID, workflowID string, version int, stageData json.RawMessage, status string) error {
now := time.Now().UTC().Format(timeFmt)
_, err := DB.ExecContext(ctx, `
UPDATE channels
SET workflow_id = ?, workflow_version = ?, current_stage = 0,
stage_data = ?, workflow_status = ?, last_activity_at = ?
stage_data = ?, workflow_status = ?, last_activity_at = ?,
stage_entered_at = ?
WHERE id = ?
`, workflowID, version, stageData, status, time.Now().UTC().Format(timeFmt), channelID)
`, workflowID, version, stageData, status, now, now, channelID)
return err
}
@@ -578,10 +580,10 @@ func (s *ChannelStore) GetWorkflowStatus(ctx context.Context, channelID string)
err := DB.QueryRowContext(ctx, `
SELECT workflow_id, workflow_version, current_stage,
COALESCE(stage_data, '{}'), COALESCE(workflow_status, 'active'),
last_activity_at
last_activity_at, stage_entered_at
FROM channels WHERE id = ? AND type = 'workflow'
`, channelID).Scan(&ws.WorkflowID, &ws.WorkflowVersion,
&ws.CurrentStage, &stageData, &ws.Status, &ws.LastActivityAt)
&ws.CurrentStage, &stageData, &ws.Status, &ws.LastActivityAt, &ws.StageEnteredAt)
if err == sql.ErrNoRows {
return nil, nil
}
@@ -593,11 +595,13 @@ func (s *ChannelStore) GetWorkflowStatus(ctx context.Context, channelID string)
}
func (s *ChannelStore) AdvanceWorkflowStage(ctx context.Context, channelID string, nextStage int, stageData json.RawMessage) error {
now := time.Now().UTC().Format(timeFmt)
_, err := DB.ExecContext(ctx, `
UPDATE channels
SET current_stage = ?, stage_data = ?, last_activity_at = ?
SET current_stage = ?, stage_data = ?, last_activity_at = ?,
stage_entered_at = ?
WHERE id = ?
`, nextStage, stageData, time.Now().UTC().Format(timeFmt), channelID)
`, nextStage, stageData, now, now, channelID)
return err
}
@@ -612,9 +616,11 @@ func (s *ChannelStore) CompleteWorkflow(ctx context.Context, channelID string, f
}
func (s *ChannelStore) RejectWorkflowToStage(ctx context.Context, channelID string, stage int) error {
now := time.Now().UTC().Format(timeFmt)
_, err := DB.ExecContext(ctx, `
UPDATE channels SET current_stage = ?, last_activity_at = ? WHERE id = ?
`, stage, time.Now().UTC().Format(timeFmt), channelID)
UPDATE channels SET current_stage = ?, last_activity_at = ?,
stage_entered_at = ? WHERE id = ?
`, stage, now, now, channelID)
return err
}
@@ -866,6 +872,30 @@ func (s *ChannelStore) MergeSettings(ctx context.Context, channelID string, sett
return err
}
// ── Monitoring (v0.35.0) ────────────────────
func (s *ChannelStore) ListByType(ctx context.Context, channelType string) ([]models.Channel, error) {
rows, err := DB.QueryContext(ctx, `
SELECT id, user_id, title, COALESCE(description, ''), type,
team_id, created_at
FROM channels WHERE type = ?
ORDER BY created_at DESC`, channelType)
if err != nil {
return nil, err
}
defer rows.Close()
var result []models.Channel
for rows.Next() {
var ch models.Channel
if err := rows.Scan(&ch.ID, &ch.UserID, &ch.Title, &ch.Description,
&ch.Type, &ch.TeamID, st(&ch.CreatedAt)); err != nil {
return nil, err
}
result = append(result, ch)
}
return result, rows.Err()
}
// ── CS7b helpers ────────────────────────────
func scanChannelListItems(rows *sql.Rows) ([]store.ChannelListItem, error) {

View File

@@ -160,11 +160,11 @@ func (s *WorkflowStore) CreateStage(ctx context.Context, st *models.WorkflowStag
_, err := DB.ExecContext(ctx, `
INSERT INTO workflow_stages (id, workflow_id, ordinal, name, persona_id, assignment_team_id,
form_template, stage_mode, history_mode, auto_transition, transition_rules,
surface_pkg_id, created_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
surface_pkg_id, sla_seconds, created_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
st.ID, st.WorkflowID, st.Ordinal, st.Name, st.PersonaID, st.AssignmentTeamID,
formTpl, stageMode, st.HistoryMode, boolToInt(st.AutoTransition), transRules,
st.SurfacePkgID, st.CreatedAt.Format(time.RFC3339))
st.SurfacePkgID, st.SLASeconds, st.CreatedAt.Format(time.RFC3339))
return err
}
@@ -172,7 +172,7 @@ func (s *WorkflowStore) ListStages(ctx context.Context, workflowID string) ([]mo
rows, err := DB.QueryContext(ctx, `
SELECT id, workflow_id, ordinal, name, persona_id, assignment_team_id,
form_template, stage_mode, history_mode, auto_transition, transition_rules,
surface_pkg_id, created_at
surface_pkg_id, sla_seconds, created_at
FROM workflow_stages WHERE workflow_id = ?
ORDER BY ordinal ASC`, workflowID)
if err != nil {
@@ -187,7 +187,7 @@ func (s *WorkflowStore) ListStages(ctx context.Context, workflowID string) ([]mo
if err := rows.Scan(&stg.ID, &stg.WorkflowID, &stg.Ordinal, &stg.Name,
&stg.PersonaID, &stg.AssignmentTeamID, &formTpl, &stg.StageMode,
&stg.HistoryMode, &autoTrans, &transRules,
&stg.SurfacePkgID, st(&stg.CreatedAt)); err != nil {
&stg.SurfacePkgID, &stg.SLASeconds, st(&stg.CreatedAt)); err != nil {
return nil, err
}
stg.FormTemplate = json.RawMessage(formTpl)
@@ -209,11 +209,11 @@ func (s *WorkflowStore) UpdateStage(ctx context.Context, st *models.WorkflowStag
UPDATE workflow_stages
SET ordinal = ?, name = ?, persona_id = ?, assignment_team_id = ?,
form_template = ?, stage_mode = ?, history_mode = ?, auto_transition = ?,
transition_rules = ?, surface_pkg_id = ?
transition_rules = ?, surface_pkg_id = ?, sla_seconds = ?
WHERE id = ?`,
st.Ordinal, st.Name, st.PersonaID, st.AssignmentTeamID,
formTpl, stageMode, st.HistoryMode, boolToInt(st.AutoTransition), transRules,
st.SurfacePkgID, st.ID)
st.SurfacePkgID, st.SLASeconds, st.ID)
return err
}
@@ -504,3 +504,44 @@ func scanAssignments(rows *sql.Rows) ([]store.WorkflowAssignment, error) {
}
return result, rows.Err()
}
// ── Review Comments (v0.35.0) ───────────────────────────
func (s *WorkflowStore) GetAssignmentByID(ctx context.Context, id string) (*store.WorkflowAssignment, error) {
var a store.WorkflowAssignment
var rc string
var claimedAt, completedAt *time.Time
err := DB.QueryRowContext(ctx, `
SELECT id, channel_id, stage, team_id, assigned_to, status,
COALESCE(review_comments, '[]'), created_at, claimed_at, completed_at
FROM workflow_assignments WHERE id = ?
`, id).Scan(&a.ID, &a.ChannelID, &a.Stage, &a.TeamID, &a.AssignedTo, &a.Status,
&rc, st(&a.CreatedAt), stN(&claimedAt), stN(&completedAt))
if err != nil {
return nil, err
}
a.ReviewComments = json.RawMessage(rc)
a.ClaimedAt = claimedAt
a.CompletedAt = completedAt
return &a, nil
}
func (s *WorkflowStore) AddReviewComment(ctx context.Context, assignmentID string, comment store.ReviewComment) error {
commentJSON, err := json.Marshal(comment)
if err != nil {
return err
}
// SQLite: read, append, write back
var existing string
err = DB.QueryRowContext(ctx, `SELECT COALESCE(review_comments, '[]') FROM workflow_assignments WHERE id = ?`, assignmentID).Scan(&existing)
if err != nil {
return err
}
var arr []json.RawMessage
_ = json.Unmarshal([]byte(existing), &arr)
arr = append(arr, json.RawMessage(commentJSON))
updated, _ := json.Marshal(arr)
_, err = DB.ExecContext(ctx, `UPDATE workflow_assignments SET review_comments = ? WHERE id = ?`, string(updated), assignmentID)
return err
}