Add spec-aware orch tasks and verification gates

This commit is contained in:
2026-03-23 14:05:10 +08:00
parent 4d8c90eb26
commit 9f9b66330c
22 changed files with 1696 additions and 55 deletions
@@ -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
}