Feat v0.3.7 package audit (#20)
All checks were successful
CI/CD / detect-changes (push) Successful in 4s
CI/CD / test-frontend (push) Successful in 6s
CI/CD / test-go-pg (push) Successful in 2m36s
CI/CD / test-sqlite (push) Successful in 2m41s
CI/CD / build-and-deploy (push) Successful in 1m13s

Co-authored-by: Jeffrey Smith <jasafpro@gmail.com>
Co-committed-by: Jeffrey Smith <jasafpro@gmail.com>
This commit was merged in pull request #20.
This commit is contained in:
2026-03-28 21:22:39 +00:00
committed by xcaliber
parent d68451fe8e
commit d91ec02dd7
47 changed files with 2067 additions and 53 deletions

View File

@@ -18,6 +18,25 @@ import (
// MaxConsecutiveAutomated is the cycle guard limit for automated stages.
const MaxConsecutiveAutomated = 10
// parseSnapshotStages handles both snapshot formats:
// - Wrapped: {"stages": [...], "workflow": {...}} (from Publish handler)
// - Legacy: [...] (from early tests)
func parseSnapshotStages(raw json.RawMessage) ([]models.WorkflowStage, error) {
// Try wrapped format first
var wrapped struct {
Stages []models.WorkflowStage `json:"stages"`
}
if err := json.Unmarshal(raw, &wrapped); err == nil && len(wrapped.Stages) > 0 {
return wrapped.Stages, nil
}
// Fallback to flat array
var stages []models.WorkflowStage
if err := json.Unmarshal(raw, &stages); err != nil {
return nil, fmt.Errorf("corrupt version snapshot: %w", err)
}
return stages, nil
}
// Engine orchestrates workflow instance lifecycle.
type Engine struct {
stores store.Stores
@@ -45,9 +64,9 @@ func (e *Engine) Start(ctx context.Context, workflowID string, initialData json.
return nil, fmt.Errorf("no published version: %w", err)
}
var stages []models.WorkflowStage
if err := json.Unmarshal(ver.Snapshot, &stages); err != nil {
return nil, fmt.Errorf("corrupt version snapshot: %w", err)
stages, err := parseSnapshotStages(ver.Snapshot)
if err != nil {
return nil, err
}
if len(stages) == 0 {
return nil, fmt.Errorf("workflow has no stages")
@@ -121,9 +140,9 @@ func (e *Engine) advanceInternal(ctx context.Context, instanceID string, stageDa
return nil, fmt.Errorf("version not found: %w", err)
}
var stages []models.WorkflowStage
if err := json.Unmarshal(ver.Snapshot, &stages); err != nil {
return nil, fmt.Errorf("corrupt snapshot: %w", err)
stages, err := parseSnapshotStages(ver.Snapshot)
if err != nil {
return nil, err
}
// Find current stage ordinal
@@ -321,9 +340,9 @@ func (e *Engine) AdvancePublic(ctx context.Context, entryToken string, stageData
if err != nil {
return nil, fmt.Errorf("version not found: %w", err)
}
var stages []models.WorkflowStage
if err := json.Unmarshal(ver.Snapshot, &stages); err != nil {
return nil, fmt.Errorf("corrupt snapshot: %w", err)
stages, err := parseSnapshotStages(ver.Snapshot)
if err != nil {
return nil, err
}
// Find current stage and validate audience
@@ -356,9 +375,9 @@ func (e *Engine) SubmitSignoff(ctx context.Context, instanceID, userID, decision
if err != nil {
return nil, fmt.Errorf("version not found: %w", err)
}
var stages []models.WorkflowStage
if err := json.Unmarshal(ver.Snapshot, &stages); err != nil {
return nil, fmt.Errorf("corrupt snapshot: %w", err)
stages, err := parseSnapshotStages(ver.Snapshot)
if err != nil {
return nil, err
}
// Find current stage
@@ -421,8 +440,8 @@ func CheckClaimRole(ctx context.Context, stores store.Stores, assignment *models
if err != nil {
return nil
}
var stages []models.WorkflowStage
if err := json.Unmarshal(ver.Snapshot, &stages); err != nil {
stages, parseErr := parseSnapshotStages(ver.Snapshot)
if parseErr != nil {
return nil
}