Add spec-aware orch tasks and verification gates
This commit is contained in:
@@ -1,7 +1,11 @@
|
||||
package orch
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"ai-workflow-skill/packages/coord-core/protocol"
|
||||
"ai-workflow-skill/packages/coord-core/store"
|
||||
@@ -17,6 +21,13 @@ type taskAddOptions struct {
|
||||
defaultTo string
|
||||
acceptanceJSON string
|
||||
priority string
|
||||
specFile string
|
||||
specSHA string
|
||||
checkProfile string
|
||||
requiredChecks []string
|
||||
allowedPaths []string
|
||||
blockedPaths []string
|
||||
metadataJSON string
|
||||
}
|
||||
|
||||
func newTaskCmd(root *rootOptions) *cobra.Command {
|
||||
@@ -42,10 +53,11 @@ func newTaskAddCmd(root *rootOptions) *cobra.Command {
|
||||
Short: "Add a task to a run",
|
||||
Long: helpLong(
|
||||
"Use task add to register one schedulable task inside a run.",
|
||||
"Tasks may include a default worker target, priority, and optional acceptance JSON that downstream tooling can inspect.",
|
||||
"Tasks may include a default worker target, priority, optional acceptance JSON, and a task-spec snapshot with verification policy.",
|
||||
"A task must belong to an existing run before it can become ready or be dispatched.",
|
||||
),
|
||||
Example: ` orch --db .agents/coord.db task add --run blog_mvp_001 --task T1 --title "Implement backend" --summary "Ship the first API slice" --default-to backend-worker --priority high`,
|
||||
Example: ` orch --db .agents/coord.db task add --run blog_mvp_001 --task T1 --title "Implement backend" --summary "Ship the first API slice" --default-to backend-worker --priority high
|
||||
orch --db .agents/coord.db task add --run blog_mvp_001 --task T2 --title "Polish release flow" --spec-file ./tasks/t2.md --check-profile cadence_component --required-check lint --required-check test:e2e`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
ctx := cmd.Context()
|
||||
|
||||
@@ -55,6 +67,11 @@ func newTaskAddCmd(root *rootOptions) *cobra.Command {
|
||||
}
|
||||
defer sqlDB.Close()
|
||||
|
||||
specBody, computedSpecSHA, err := loadTaskSpecSnapshot(opts.specFile, opts.specSHA)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
task, err := store.NewOrchStore(sqlDB).AddTask(ctx, store.AddTaskInput{
|
||||
RunID: opts.runID,
|
||||
TaskID: opts.taskID,
|
||||
@@ -63,6 +80,14 @@ func newTaskAddCmd(root *rootOptions) *cobra.Command {
|
||||
DefaultTo: opts.defaultTo,
|
||||
AcceptanceJSON: opts.acceptanceJSON,
|
||||
Priority: opts.priority,
|
||||
SpecFile: strings.TrimSpace(opts.specFile),
|
||||
SpecSHA: computedSpecSHA,
|
||||
SpecBody: specBody,
|
||||
CheckProfile: strings.TrimSpace(opts.checkProfile),
|
||||
RequiredChecks: opts.requiredChecks,
|
||||
AllowedPaths: opts.allowedPaths,
|
||||
BlockedPaths: opts.blockedPaths,
|
||||
MetadataJSON: opts.metadataJSON,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -91,9 +116,40 @@ func newTaskAddCmd(root *rootOptions) *cobra.Command {
|
||||
cmd.Flags().StringVar(&opts.defaultTo, "default-to", "", "Default worker agent")
|
||||
cmd.Flags().StringVar(&opts.acceptanceJSON, "acceptance-json", "", "Acceptance criteria JSON")
|
||||
cmd.Flags().StringVar(&opts.priority, "priority", "normal", "Task priority")
|
||||
cmd.Flags().StringVar(&opts.specFile, "spec-file", "", "Path to the task spec file to snapshot")
|
||||
cmd.Flags().StringVar(&opts.specSHA, "spec-sha", "", "Optional expected SHA256 for --spec-file")
|
||||
cmd.Flags().StringVar(&opts.checkProfile, "check-profile", "", "Verification check profile name")
|
||||
cmd.Flags().StringSliceVar(&opts.requiredChecks, "required-check", nil, "Required verification check name; repeat for multiple checks")
|
||||
cmd.Flags().StringSliceVar(&opts.allowedPaths, "allowed-path", nil, "Allowed path prefix for task scope; repeat for multiple paths")
|
||||
cmd.Flags().StringSliceVar(&opts.blockedPaths, "blocked-path", nil, "Blocked path prefix for task scope; repeat for multiple paths")
|
||||
cmd.Flags().StringVar(&opts.metadataJSON, "metadata-json", "", "Structured metadata JSON for task policy")
|
||||
_ = cmd.MarkFlagRequired("run")
|
||||
_ = cmd.MarkFlagRequired("task")
|
||||
_ = cmd.MarkFlagRequired("title")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func loadTaskSpecSnapshot(specFile, expectedSHA string) (string, string, error) {
|
||||
specFile = strings.TrimSpace(specFile)
|
||||
expectedSHA = strings.TrimSpace(expectedSHA)
|
||||
if specFile == "" {
|
||||
if expectedSHA != "" {
|
||||
return "", "", fmt.Errorf("%w: spec-sha requires spec-file", store.ErrInvalidInput)
|
||||
}
|
||||
return "", "", nil
|
||||
}
|
||||
|
||||
body, err := os.ReadFile(specFile)
|
||||
if err != nil {
|
||||
return "", "", protocol.InvalidInput("failed to read spec-file", err)
|
||||
}
|
||||
|
||||
sum := sha256.Sum256(body)
|
||||
computed := hex.EncodeToString(sum[:])
|
||||
if expectedSHA != "" && !strings.EqualFold(expectedSHA, computed) {
|
||||
return "", "", fmt.Errorf("%w: spec-sha does not match spec-file contents", store.ErrInvalidInput)
|
||||
}
|
||||
|
||||
return string(body), computed, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user