Files
ai-workflow-skill/packages/orch-runtime/internal/cli/orch/verify.go
T

196 lines
6.1 KiB
Go

package orch
import (
"fmt"
"ai-workflow-skill/packages/coord-core/protocol"
"ai-workflow-skill/packages/coord-core/store"
"github.com/spf13/cobra"
)
type verifyRecordOptions struct {
runID string
taskID string
attemptNo int
checkName string
status string
summary string
body string
bodyFile string
metadataJSON string
recordedBy string
}
type verifyStatusOptions struct {
runID string
taskID string
attemptNo int
}
func newVerifyCmd(root *rootOptions) *cobra.Command {
cmd := &cobra.Command{
Use: "verify",
Short: "Verification gate commands",
Long: helpLong(
"Verification gate commands record post-implementation checks and inspect task verification state.",
"Verification gates keep orch from treating a worker's done signal as final completion before the required checks pass.",
),
Example: ` orch --db .agents/coord.db verify record --run blog_mvp_001 --task T1 --check lint --status passed --summary "lint clean"
orch --db .agents/coord.db verify status --run blog_mvp_001 --task T1`,
}
cmd.AddCommand(newVerifyRecordCmd(root))
cmd.AddCommand(newVerifyStatusCmd(root))
return cmd
}
func newVerifyRecordCmd(root *rootOptions) *cobra.Command {
opts := &verifyRecordOptions{}
cmd := &cobra.Command{
Use: "record",
Short: "Record one verification check result for a task attempt",
Long: helpLong(
"Use verify record after reconcile has moved a task into verifying, or when a prior verification failure needs an updated check result.",
"record upserts one named check result for the selected attempt, then recomputes the task gate and task status.",
),
Example: ` orch --db .agents/coord.db verify record --run blog_mvp_001 --task T1 --check lint --status passed --summary "lint clean"
orch --db .agents/coord.db verify record --run blog_mvp_001 --task T1 --check package-consumer --status failed --summary "consumer smoke failed" --body-file ./artifacts/package-consumer.txt`,
RunE: func(cmd *cobra.Command, args []string) error {
ctx := cmd.Context()
sqlDB, err := openOrchDB(ctx, root.dbPath)
if err != nil {
return err
}
defer sqlDB.Close()
body, err := resolveBodyValue(opts.body, opts.bodyFile)
if err != nil {
return err
}
result, err := store.NewOrchStore(sqlDB).RecordCheck(ctx, store.VerifyRecordInput{
RunID: opts.runID,
TaskID: opts.taskID,
AttemptNo: opts.attemptNo,
CheckName: opts.checkName,
Status: opts.status,
Summary: opts.summary,
Body: body,
MetadataJSON: opts.metadataJSON,
RecordedBy: opts.recordedBy,
})
if err != nil {
return err
}
resp := protocol.Success{
OK: true,
Command: "verify record",
Data: map[string]any{
"task": result.Task,
"attempt": result.Attempt,
"check": result.Check,
"gate": result.Gate,
},
}
if root.json {
return protocol.WriteJSON(cmd.OutOrStdout(), resp)
}
_, err = fmt.Fprintf(
cmd.OutOrStdout(),
"recorded check %s=%s for %s/%s attempt %d\n",
result.Check.CheckName,
result.Check.Status,
result.Task.RunID,
result.Task.TaskID,
result.Attempt.AttemptNo,
)
return err
},
}
cmd.Flags().StringVar(&opts.runID, "run", "", "Run ID")
cmd.Flags().StringVar(&opts.taskID, "task", "", "Task ID")
cmd.Flags().IntVar(&opts.attemptNo, "attempt", 0, "Attempt number; defaults to the latest attempt")
cmd.Flags().StringVar(&opts.checkName, "check", "", "Check name")
cmd.Flags().StringVar(&opts.status, "status", "", "Check status: passed, failed, or skipped")
cmd.Flags().StringVar(&opts.summary, "summary", "", "Short verification summary")
cmd.Flags().StringVar(&opts.body, "body", "", "Optional verification details")
cmd.Flags().StringVar(&opts.bodyFile, "body-file", "", "Optional path to a verification details file")
cmd.Flags().StringVar(&opts.metadataJSON, "metadata-json", "", "Structured metadata JSON for the check result")
cmd.Flags().StringVar(&opts.recordedBy, "recorded-by", "orch", "Recorder identity for the check result")
_ = cmd.MarkFlagRequired("run")
_ = cmd.MarkFlagRequired("task")
_ = cmd.MarkFlagRequired("check")
_ = cmd.MarkFlagRequired("status")
return cmd
}
func newVerifyStatusCmd(root *rootOptions) *cobra.Command {
opts := &verifyStatusOptions{}
cmd := &cobra.Command{
Use: "status",
Short: "Show the current verification state for one task",
Long: helpLong(
"Use verify status to inspect the task spec snapshot, the selected attempt, and the current gate state in one response.",
"Prefer this when a task is stuck in verifying or failed because of gate results.",
),
Example: ` orch --db .agents/coord.db verify status --run blog_mvp_001 --task T1
orch --db .agents/coord.db --json verify status --run blog_mvp_001 --task T1 --attempt 2`,
RunE: func(cmd *cobra.Command, args []string) error {
ctx := cmd.Context()
sqlDB, err := openOrchDB(ctx, root.dbPath)
if err != nil {
return err
}
defer sqlDB.Close()
result, err := store.NewOrchStore(sqlDB).GetVerificationStatus(ctx, store.VerificationStatusInput{
RunID: opts.runID,
TaskID: opts.taskID,
AttemptNo: opts.attemptNo,
})
if err != nil {
return err
}
resp := protocol.Success{
OK: true,
Command: "verify status",
Data: map[string]any{
"task": result.Task,
"attempt": result.Attempt,
"spec": result.Spec,
"gate": result.Gate,
},
}
if root.json {
return protocol.WriteJSON(cmd.OutOrStdout(), resp)
}
if result.Gate == nil {
_, err = fmt.Fprintf(cmd.OutOrStdout(), "%s/%s has no verification gate\n", result.Task.RunID, result.Task.TaskID)
return err
}
_, err = fmt.Fprintf(cmd.OutOrStdout(), "%s/%s verification status %s\n", result.Task.RunID, result.Task.TaskID, result.Gate.Status)
return err
},
}
cmd.Flags().StringVar(&opts.runID, "run", "", "Run ID")
cmd.Flags().StringVar(&opts.taskID, "task", "", "Task ID")
cmd.Flags().IntVar(&opts.attemptNo, "attempt", 0, "Attempt number; defaults to the latest attempt")
_ = cmd.MarkFlagRequired("run")
_ = cmd.MarkFlagRequired("task")
return cmd
}