refactor(monorepo): extract coord-core
This commit is contained in:
+1
-1
@@ -12,7 +12,7 @@ import (
|
||||
"time"
|
||||
|
||||
"ai-workflow-skill/internal/app"
|
||||
"ai-workflow-skill/internal/db"
|
||||
"ai-workflow-skill/packages/coord-core/db"
|
||||
"ai-workflow-skill/internal/httpapi"
|
||||
)
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ As of now:
|
||||
- the first real Phase 2 read-only operator UI is now implemented in `apps/web`, including routed runs list, run detail, blocked queue, and thread timeline views backed by the existing `orchd` HTTP API, plus Tailwind v4 consumer wiring so the source-owned Cadence UI components render correctly in the app
|
||||
- a repository-level skill workspace monorepo migration plan now exists under `docs/skill-workspace-monorepo.md`, defining the target split between runtime packages under `packages/`, agent-facing skill bundles under `skills/`, support apps under `apps/`, and package-based skill packaging flows
|
||||
- the first migration phase for the skill workspace monorepo is now complete: root `go.work` exists, `pnpm-workspace.yaml` now discovers `packages/*`, empty runtime module roots now exist under `packages/`, and a declarative `scripts/skill-bundles.json` plus `scripts/package_skill_runtimes.sh` scaffold now define package-oriented skill bundle metadata from the repo root
|
||||
- `packages/coord-core` now exists as the first real extracted runtime package, containing shared coordination DB/schema, protocol, and store code, and the active coordination runtimes now import `coord-core` instead of root `internal/db`, `internal/store`, and `internal/protocol`
|
||||
- a repo-local `scripts/package_skill_clis.sh` packaging flow now builds bundled skill CLI assets for `inbox`, `orch`, and `council-review`
|
||||
- `orch` now implements `run init/show`, `task add`, `dep add`, `ready`, `dispatch`, `reconcile`, `wait`, `blocked`, `answer`, `retry`, `reassign`, `cancel`, `cleanup`, and `status`
|
||||
- `orch` can create runs, gate tasks through dependencies, dispatch work through `inbox`, reconcile worker thread state back into task state, answer blocked tasks, retry or reassign work, cancel tasks or runs, clean attempt worktrees, and create per-attempt Git worktrees during strict dispatch
|
||||
@@ -495,10 +496,14 @@ Completed so far:
|
||||
- initial module roots now exist for `packages/coord-core`, `packages/inbox-runtime`, `packages/orch-runtime`, `packages/orchd-runtime`, and `packages/repo-memory-runtime`
|
||||
- `scripts/skill-bundles.json` now records the first package-oriented skill bundle metadata, including the future `repo-memory` runtime mapping
|
||||
- `scripts/package_skill_runtimes.sh` now provides a declarative bundle plan/validate scaffold that targets package paths rather than hardcoded root runtime paths
|
||||
- `packages/coord-core/db` now owns the shared SQLite open, pragmas, migrations, and coordination schema files
|
||||
- `packages/coord-core/protocol` now owns the shared JSON and CLI error helpers used across the coordination stack
|
||||
- `packages/coord-core/store` now owns the shared inbox, orch, and council store logic plus its coordination-domain tests
|
||||
- root coordination runtimes under `cmd/`, `internal/cli/`, `internal/app/`, `internal/httpapi/`, and `internal/query/` now import `coord-core` instead of depending on root `internal/db`, `internal/store`, or root `internal/protocol`
|
||||
- `go test ./...` still passes for the root module, and `go test ./...` passes inside `packages/coord-core`
|
||||
|
||||
Remaining:
|
||||
|
||||
- extract shared coordination code into `packages/coord-core`
|
||||
- extract `inbox`, `orch`, and `orchd` into package-owned runtimes
|
||||
- import `repo-memory` as its own runtime package and add the corresponding skill bundle
|
||||
- graduate the bundle scaffold into the primary packaging flow once package-owned runtime entrypoints exist
|
||||
@@ -509,12 +514,12 @@ If a new agent is taking over now, the next concrete step should be:
|
||||
|
||||
1. treat `Milestone 9: Web Product Phase 2 Read-Only Operator UI` as complete for the initial operator surface and do not expand web feature scope further until the workspace split is decided package-by-package
|
||||
2. treat the Phase 1 workspace bootstrap for `Milestone 10` as complete and keep the new `go.work`, `packages/`, and declarative bundle metadata as the baseline for all further migration steps
|
||||
3. extract the shared coordination kernel into `packages/coord-core` before moving `inbox`, `orch`, or `orchd` into package-owned runtimes
|
||||
3. treat the shared coordination kernel extraction into `packages/coord-core` as complete and move `inbox` plus `orch` into package-owned runtimes next
|
||||
4. keep the authored skill forward-test plans under `docs/tests/*-skill/` synchronized as runtime ownership moves from root paths to package paths
|
||||
5. keep the legacy hardcoded packaging flow working temporarily, but evolve the new declarative bundle scaffold into the primary packaging path before adding `repo-memory`
|
||||
6. import `repo-memory` only after the package-based runtime and skill packaging pattern exists
|
||||
|
||||
The inbox implementation and its human-readable test-plan set are already in place, `orch` supports the main scheduler loop plus the complete council start/wait/tally/report workflow, the web product now has its first real operator-facing read surfaces, and the repository has finished the first workspace-bootstrap phase of the skill monorepo migration, so the next step should be package extraction rather than continuing to accrete new root-owned runtimes.
|
||||
The inbox implementation and its human-readable test-plan set are already in place, `orch` supports the main scheduler loop plus the complete council start/wait/tally/report workflow, the web product now has its first real operator-facing read surfaces, and the repository has completed both the workspace bootstrap and the shared coordination-kernel extraction phases of the skill monorepo migration, so the next step should be runtime extraction rather than continuing to accrete new root-owned runtimes.
|
||||
|
||||
## Recommended Driver Choices
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
|
||||
- [x] create or adopt an active execution roadmap for the migration workstream
|
||||
- [x] Phase 1: bootstrap `go.work`, expanded workspace manifests, package roots, and declarative skill bundle metadata
|
||||
- [ ] Phase 2: extract shared coordination code into `packages/coord-core`
|
||||
- [x] Phase 2: extract shared coordination code into `packages/coord-core`
|
||||
- [ ] Phase 3: extract `inbox-runtime` and `orch-runtime`
|
||||
- [ ] Phase 4: extract `orchd-runtime`
|
||||
- [ ] Phase 5: import `repo-memory-runtime` and add `skills/repo-memory`
|
||||
@@ -54,4 +54,4 @@
|
||||
|
||||
## Next Step
|
||||
|
||||
- start Phase 2 by extracting the shared coordination kernel into `packages/coord-core`, using the new workspace and bundle metadata scaffold as the stable base for subsequent package moves
|
||||
- start Phase 3 by moving `inbox` and `orch` into package-owned runtimes on top of the now-shared `packages/coord-core` kernel
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ import (
|
||||
"database/sql"
|
||||
|
||||
"ai-workflow-skill/internal/query"
|
||||
"ai-workflow-skill/internal/store"
|
||||
"ai-workflow-skill/packages/coord-core/store"
|
||||
)
|
||||
|
||||
type WebService struct {
|
||||
|
||||
@@ -3,8 +3,8 @@ package inbox
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"ai-workflow-skill/internal/protocol"
|
||||
"ai-workflow-skill/internal/store"
|
||||
"ai-workflow-skill/packages/coord-core/protocol"
|
||||
"ai-workflow-skill/packages/coord-core/store"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -3,7 +3,7 @@ package inbox
|
||||
import (
|
||||
"os"
|
||||
|
||||
"ai-workflow-skill/internal/protocol"
|
||||
"ai-workflow-skill/packages/coord-core/protocol"
|
||||
)
|
||||
|
||||
func resolveBodyValue(body, bodyFile string) (string, error) {
|
||||
|
||||
@@ -3,8 +3,8 @@ package inbox
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"ai-workflow-skill/internal/protocol"
|
||||
"ai-workflow-skill/internal/store"
|
||||
"ai-workflow-skill/packages/coord-core/protocol"
|
||||
"ai-workflow-skill/packages/coord-core/store"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -4,8 +4,8 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"ai-workflow-skill/internal/protocol"
|
||||
"ai-workflow-skill/internal/store"
|
||||
"ai-workflow-skill/packages/coord-core/protocol"
|
||||
"ai-workflow-skill/packages/coord-core/store"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"context"
|
||||
"database/sql"
|
||||
|
||||
"ai-workflow-skill/internal/db"
|
||||
"ai-workflow-skill/packages/coord-core/db"
|
||||
)
|
||||
|
||||
func openInboxDB(ctx context.Context, dbPath string) (*sql.DB, error) {
|
||||
|
||||
@@ -3,8 +3,8 @@ package inbox
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"ai-workflow-skill/internal/protocol"
|
||||
"ai-workflow-skill/internal/store"
|
||||
"ai-workflow-skill/packages/coord-core/protocol"
|
||||
"ai-workflow-skill/packages/coord-core/store"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -6,8 +6,8 @@ import (
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"ai-workflow-skill/internal/protocol"
|
||||
"ai-workflow-skill/internal/store"
|
||||
"ai-workflow-skill/packages/coord-core/protocol"
|
||||
"ai-workflow-skill/packages/coord-core/store"
|
||||
)
|
||||
|
||||
func Execute(args []string, stdout, stderr io.Writer) int {
|
||||
|
||||
@@ -4,8 +4,8 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"ai-workflow-skill/internal/protocol"
|
||||
"ai-workflow-skill/internal/store"
|
||||
"ai-workflow-skill/packages/coord-core/protocol"
|
||||
"ai-workflow-skill/packages/coord-core/store"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -3,7 +3,7 @@ package inbox
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"ai-workflow-skill/internal/protocol"
|
||||
"ai-workflow-skill/packages/coord-core/protocol"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -3,8 +3,8 @@ package inbox
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"ai-workflow-skill/internal/protocol"
|
||||
"ai-workflow-skill/internal/store"
|
||||
"ai-workflow-skill/packages/coord-core/protocol"
|
||||
"ai-workflow-skill/packages/coord-core/store"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -3,8 +3,8 @@ package inbox
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"ai-workflow-skill/internal/protocol"
|
||||
"ai-workflow-skill/internal/store"
|
||||
"ai-workflow-skill/packages/coord-core/protocol"
|
||||
"ai-workflow-skill/packages/coord-core/store"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -3,8 +3,8 @@ package inbox
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"ai-workflow-skill/internal/protocol"
|
||||
"ai-workflow-skill/internal/store"
|
||||
"ai-workflow-skill/packages/coord-core/protocol"
|
||||
"ai-workflow-skill/packages/coord-core/store"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -3,8 +3,8 @@ package inbox
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"ai-workflow-skill/internal/protocol"
|
||||
"ai-workflow-skill/internal/store"
|
||||
"ai-workflow-skill/packages/coord-core/protocol"
|
||||
"ai-workflow-skill/packages/coord-core/store"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -3,8 +3,8 @@ package inbox
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"ai-workflow-skill/internal/protocol"
|
||||
"ai-workflow-skill/internal/store"
|
||||
"ai-workflow-skill/packages/coord-core/protocol"
|
||||
"ai-workflow-skill/packages/coord-core/store"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -3,8 +3,8 @@ package inbox
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"ai-workflow-skill/internal/protocol"
|
||||
"ai-workflow-skill/internal/store"
|
||||
"ai-workflow-skill/packages/coord-core/protocol"
|
||||
"ai-workflow-skill/packages/coord-core/store"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -4,8 +4,8 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"ai-workflow-skill/internal/protocol"
|
||||
"ai-workflow-skill/internal/store"
|
||||
"ai-workflow-skill/packages/coord-core/protocol"
|
||||
"ai-workflow-skill/packages/coord-core/store"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -4,8 +4,8 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"ai-workflow-skill/internal/protocol"
|
||||
"ai-workflow-skill/internal/store"
|
||||
"ai-workflow-skill/packages/coord-core/protocol"
|
||||
"ai-workflow-skill/packages/coord-core/store"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -3,8 +3,8 @@ package orch
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"ai-workflow-skill/internal/protocol"
|
||||
"ai-workflow-skill/internal/store"
|
||||
"ai-workflow-skill/packages/coord-core/protocol"
|
||||
"ai-workflow-skill/packages/coord-core/store"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -3,8 +3,8 @@ package orch
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"ai-workflow-skill/internal/protocol"
|
||||
"ai-workflow-skill/internal/store"
|
||||
"ai-workflow-skill/packages/coord-core/protocol"
|
||||
"ai-workflow-skill/packages/coord-core/store"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -3,7 +3,7 @@ package orch
|
||||
import (
|
||||
"os"
|
||||
|
||||
"ai-workflow-skill/internal/protocol"
|
||||
"ai-workflow-skill/packages/coord-core/protocol"
|
||||
)
|
||||
|
||||
func resolveBodyValue(body, bodyFile string) (string, error) {
|
||||
|
||||
@@ -3,8 +3,8 @@ package orch
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"ai-workflow-skill/internal/protocol"
|
||||
"ai-workflow-skill/internal/store"
|
||||
"ai-workflow-skill/packages/coord-core/protocol"
|
||||
"ai-workflow-skill/packages/coord-core/store"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -3,8 +3,8 @@ package orch
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"ai-workflow-skill/internal/protocol"
|
||||
"ai-workflow-skill/internal/store"
|
||||
"ai-workflow-skill/packages/coord-core/protocol"
|
||||
"ai-workflow-skill/packages/coord-core/store"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -6,8 +6,8 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"ai-workflow-skill/internal/protocol"
|
||||
"ai-workflow-skill/internal/store"
|
||||
"ai-workflow-skill/packages/coord-core/protocol"
|
||||
"ai-workflow-skill/packages/coord-core/store"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -3,8 +3,8 @@ package orch
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"ai-workflow-skill/internal/protocol"
|
||||
"ai-workflow-skill/internal/store"
|
||||
"ai-workflow-skill/packages/coord-core/protocol"
|
||||
"ai-workflow-skill/packages/coord-core/store"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -3,8 +3,8 @@ package orch
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"ai-workflow-skill/internal/protocol"
|
||||
"ai-workflow-skill/internal/store"
|
||||
"ai-workflow-skill/packages/coord-core/protocol"
|
||||
"ai-workflow-skill/packages/coord-core/store"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -4,8 +4,8 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"ai-workflow-skill/internal/protocol"
|
||||
"ai-workflow-skill/internal/store"
|
||||
"ai-workflow-skill/packages/coord-core/protocol"
|
||||
"ai-workflow-skill/packages/coord-core/store"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"context"
|
||||
"database/sql"
|
||||
|
||||
"ai-workflow-skill/internal/db"
|
||||
"ai-workflow-skill/packages/coord-core/db"
|
||||
)
|
||||
|
||||
func openOrchDB(ctx context.Context, dbPath string) (*sql.DB, error) {
|
||||
|
||||
@@ -3,8 +3,8 @@ package orch
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"ai-workflow-skill/internal/protocol"
|
||||
"ai-workflow-skill/internal/store"
|
||||
"ai-workflow-skill/packages/coord-core/protocol"
|
||||
"ai-workflow-skill/packages/coord-core/store"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -3,8 +3,8 @@ package orch
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"ai-workflow-skill/internal/protocol"
|
||||
"ai-workflow-skill/internal/store"
|
||||
"ai-workflow-skill/packages/coord-core/protocol"
|
||||
"ai-workflow-skill/packages/coord-core/store"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -6,8 +6,8 @@ import (
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"ai-workflow-skill/internal/protocol"
|
||||
"ai-workflow-skill/internal/store"
|
||||
"ai-workflow-skill/packages/coord-core/protocol"
|
||||
"ai-workflow-skill/packages/coord-core/store"
|
||||
)
|
||||
|
||||
func Execute(args []string, stdout, stderr io.Writer) int {
|
||||
|
||||
@@ -3,8 +3,8 @@ package orch
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"ai-workflow-skill/internal/protocol"
|
||||
"ai-workflow-skill/internal/store"
|
||||
"ai-workflow-skill/packages/coord-core/protocol"
|
||||
"ai-workflow-skill/packages/coord-core/store"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -3,8 +3,8 @@ package orch
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"ai-workflow-skill/internal/protocol"
|
||||
"ai-workflow-skill/internal/store"
|
||||
"ai-workflow-skill/packages/coord-core/protocol"
|
||||
"ai-workflow-skill/packages/coord-core/store"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -3,8 +3,8 @@ package orch
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"ai-workflow-skill/internal/protocol"
|
||||
"ai-workflow-skill/internal/store"
|
||||
"ai-workflow-skill/packages/coord-core/protocol"
|
||||
"ai-workflow-skill/packages/coord-core/store"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -3,8 +3,8 @@ package orch
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"ai-workflow-skill/internal/protocol"
|
||||
"ai-workflow-skill/internal/store"
|
||||
"ai-workflow-skill/packages/coord-core/protocol"
|
||||
"ai-workflow-skill/packages/coord-core/store"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -3,8 +3,8 @@ package orch
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"ai-workflow-skill/internal/protocol"
|
||||
"ai-workflow-skill/internal/store"
|
||||
"ai-workflow-skill/packages/coord-core/protocol"
|
||||
"ai-workflow-skill/packages/coord-core/store"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -3,8 +3,8 @@ package orch
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"ai-workflow-skill/internal/protocol"
|
||||
"ai-workflow-skill/internal/store"
|
||||
"ai-workflow-skill/packages/coord-core/protocol"
|
||||
"ai-workflow-skill/packages/coord-core/store"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -3,8 +3,8 @@ package orch
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"ai-workflow-skill/internal/protocol"
|
||||
"ai-workflow-skill/internal/store"
|
||||
"ai-workflow-skill/packages/coord-core/protocol"
|
||||
"ai-workflow-skill/packages/coord-core/store"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -5,8 +5,8 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"ai-workflow-skill/internal/protocol"
|
||||
"ai-workflow-skill/internal/store"
|
||||
"ai-workflow-skill/packages/coord-core/protocol"
|
||||
"ai-workflow-skill/packages/coord-core/store"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -9,8 +9,8 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"ai-workflow-skill/internal/protocol"
|
||||
"ai-workflow-skill/internal/store"
|
||||
"ai-workflow-skill/packages/coord-core/protocol"
|
||||
"ai-workflow-skill/packages/coord-core/store"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"ai-workflow-skill/internal/store"
|
||||
"ai-workflow-skill/packages/coord-core/store"
|
||||
)
|
||||
|
||||
type errorEnvelope struct {
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"github.com/go-chi/chi/v5/middleware"
|
||||
|
||||
"ai-workflow-skill/internal/query"
|
||||
"ai-workflow-skill/internal/store"
|
||||
"ai-workflow-skill/packages/coord-core/store"
|
||||
)
|
||||
|
||||
type readService interface {
|
||||
|
||||
@@ -10,8 +10,8 @@ import (
|
||||
"time"
|
||||
|
||||
"ai-workflow-skill/internal/app"
|
||||
dbpkg "ai-workflow-skill/internal/db"
|
||||
"ai-workflow-skill/internal/store"
|
||||
dbpkg "ai-workflow-skill/packages/coord-core/db"
|
||||
"ai-workflow-skill/packages/coord-core/store"
|
||||
)
|
||||
|
||||
func TestRouterExposesReadOnlyWebEndpoints(t *testing.T) {
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"ai-workflow-skill/internal/store"
|
||||
"ai-workflow-skill/packages/coord-core/store"
|
||||
)
|
||||
|
||||
type ReadService struct {
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"embed"
|
||||
"fmt"
|
||||
"sort"
|
||||
)
|
||||
|
||||
//go:embed schema/*.sql
|
||||
var schemaFS embed.FS
|
||||
|
||||
func ApplyMigrations(ctx context.Context, db *sql.DB) error {
|
||||
files, err := schemaFS.ReadDir("schema")
|
||||
if err != nil {
|
||||
return fmt.Errorf("read embedded schema directory: %w", err)
|
||||
}
|
||||
|
||||
names := make([]string, 0, len(files))
|
||||
for _, file := range files {
|
||||
if file.IsDir() {
|
||||
continue
|
||||
}
|
||||
names = append(names, file.Name())
|
||||
}
|
||||
sort.Strings(names)
|
||||
|
||||
tx, err := db.BeginTx(ctx, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("begin schema transaction: %w", err)
|
||||
}
|
||||
defer tx.Rollback()
|
||||
|
||||
for _, name := range names {
|
||||
content, err := schemaFS.ReadFile("schema/" + name)
|
||||
if err != nil {
|
||||
return fmt.Errorf("read embedded schema file %q: %w", name, err)
|
||||
}
|
||||
if _, err := tx.ExecContext(ctx, string(content)); err != nil {
|
||||
return fmt.Errorf("apply schema file %q: %w", name, err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := tx.Commit(); err != nil {
|
||||
return fmt.Errorf("commit schema transaction: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
_ "modernc.org/sqlite"
|
||||
)
|
||||
|
||||
func Open(ctx context.Context, dbPath string) (*sql.DB, error) {
|
||||
if err := ensureParentDir(dbPath); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
db, err := sql.Open("sqlite", dbPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("open sqlite database: %w", err)
|
||||
}
|
||||
|
||||
if err := applyPragmas(ctx, db); err != nil {
|
||||
_ = db.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return db, nil
|
||||
}
|
||||
|
||||
func ensureParentDir(dbPath string) error {
|
||||
parent := filepath.Dir(dbPath)
|
||||
if parent == "." || parent == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
return os.MkdirAll(parent, 0o755)
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func applyPragmas(ctx context.Context, db *sql.DB) error {
|
||||
pragmas := []string{
|
||||
"PRAGMA foreign_keys = ON;",
|
||||
"PRAGMA journal_mode = WAL;",
|
||||
"PRAGMA busy_timeout = 5000;",
|
||||
}
|
||||
|
||||
for _, pragma := range pragmas {
|
||||
if _, err := db.ExecContext(ctx, pragma); err != nil {
|
||||
return fmt.Errorf("apply pragma %q: %w", pragma, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
CREATE TABLE IF NOT EXISTS threads (
|
||||
thread_id TEXT PRIMARY KEY,
|
||||
run_id TEXT NOT NULL,
|
||||
task_id TEXT NOT NULL,
|
||||
subject TEXT NOT NULL,
|
||||
created_by TEXT NOT NULL,
|
||||
assigned_to TEXT NOT NULL,
|
||||
status TEXT NOT NULL,
|
||||
priority TEXT NOT NULL DEFAULT 'normal',
|
||||
latest_message_id TEXT,
|
||||
created_at TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS messages (
|
||||
message_id TEXT PRIMARY KEY,
|
||||
thread_id TEXT NOT NULL,
|
||||
from_agent TEXT NOT NULL,
|
||||
to_agent TEXT NOT NULL,
|
||||
kind TEXT NOT NULL,
|
||||
summary TEXT NOT NULL,
|
||||
body TEXT NOT NULL DEFAULT '',
|
||||
payload_json TEXT NOT NULL DEFAULT '{}',
|
||||
created_at TEXT NOT NULL,
|
||||
FOREIGN KEY(thread_id) REFERENCES threads(thread_id)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS leases (
|
||||
thread_id TEXT PRIMARY KEY,
|
||||
agent_id TEXT NOT NULL,
|
||||
lease_token TEXT NOT NULL,
|
||||
claimed_at TEXT NOT NULL,
|
||||
expires_at TEXT NOT NULL,
|
||||
released_at TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS artifacts (
|
||||
artifact_id TEXT PRIMARY KEY,
|
||||
message_id TEXT NOT NULL,
|
||||
path TEXT NOT NULL,
|
||||
kind TEXT NOT NULL,
|
||||
metadata_json TEXT NOT NULL DEFAULT '{}',
|
||||
created_at TEXT NOT NULL,
|
||||
FOREIGN KEY(message_id) REFERENCES messages(message_id)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_threads_status_assigned
|
||||
ON threads(status, assigned_to, updated_at);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_messages_thread_created
|
||||
ON messages(thread_id, created_at);
|
||||
@@ -0,0 +1,52 @@
|
||||
CREATE TABLE IF NOT EXISTS runs (
|
||||
run_id TEXT PRIMARY KEY,
|
||||
goal TEXT NOT NULL,
|
||||
summary TEXT NOT NULL DEFAULT '',
|
||||
status TEXT NOT NULL DEFAULT 'active',
|
||||
created_at TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS tasks (
|
||||
run_id TEXT NOT NULL,
|
||||
task_id TEXT NOT NULL,
|
||||
title TEXT NOT NULL,
|
||||
summary TEXT NOT NULL DEFAULT '',
|
||||
status TEXT NOT NULL,
|
||||
default_to TEXT,
|
||||
priority TEXT NOT NULL DEFAULT 'normal',
|
||||
acceptance_json TEXT NOT NULL DEFAULT '[]',
|
||||
latest_attempt_no INTEGER,
|
||||
created_at TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL,
|
||||
PRIMARY KEY (run_id, task_id),
|
||||
FOREIGN KEY(run_id) REFERENCES runs(run_id)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS task_dependencies (
|
||||
run_id TEXT NOT NULL,
|
||||
task_id TEXT NOT NULL,
|
||||
depends_on_task_id TEXT NOT NULL,
|
||||
PRIMARY KEY (run_id, task_id, depends_on_task_id)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS task_attempts (
|
||||
run_id TEXT NOT NULL,
|
||||
task_id TEXT NOT NULL,
|
||||
attempt_no INTEGER NOT NULL,
|
||||
assigned_to TEXT NOT NULL,
|
||||
thread_id TEXT NOT NULL,
|
||||
base_ref TEXT,
|
||||
base_commit TEXT,
|
||||
branch_name TEXT,
|
||||
worktree_path TEXT,
|
||||
workspace_status TEXT,
|
||||
result_commit TEXT,
|
||||
status TEXT NOT NULL,
|
||||
created_at TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL,
|
||||
PRIMARY KEY (run_id, task_id, attempt_no)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_tasks_run_status
|
||||
ON tasks(run_id, status, priority, updated_at);
|
||||
@@ -0,0 +1,18 @@
|
||||
CREATE TABLE IF NOT EXISTS events (
|
||||
event_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
run_id TEXT NOT NULL,
|
||||
task_id TEXT NOT NULL,
|
||||
thread_id TEXT,
|
||||
source TEXT NOT NULL,
|
||||
event_type TEXT NOT NULL,
|
||||
message_id TEXT,
|
||||
summary TEXT NOT NULL DEFAULT '',
|
||||
payload_json TEXT NOT NULL DEFAULT '{}',
|
||||
created_at TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_events_run_event
|
||||
ON events(run_id, event_id);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_events_thread_event
|
||||
ON events(thread_id, event_id);
|
||||
@@ -0,0 +1,45 @@
|
||||
CREATE TABLE IF NOT EXISTS council_runs (
|
||||
run_id TEXT PRIMARY KEY,
|
||||
mode TEXT NOT NULL,
|
||||
target_type TEXT NOT NULL,
|
||||
output_mode TEXT NOT NULL,
|
||||
only_unanimous INTEGER NOT NULL DEFAULT 0,
|
||||
created_at TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS council_reviewers (
|
||||
run_id TEXT NOT NULL,
|
||||
reviewer_role TEXT NOT NULL,
|
||||
task_id TEXT NOT NULL,
|
||||
status TEXT NOT NULL,
|
||||
PRIMARY KEY (run_id, reviewer_role)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS council_findings (
|
||||
run_id TEXT NOT NULL,
|
||||
reviewer_role TEXT NOT NULL,
|
||||
finding_id TEXT NOT NULL,
|
||||
title TEXT NOT NULL,
|
||||
summary TEXT NOT NULL,
|
||||
proposal TEXT NOT NULL,
|
||||
rationale TEXT NOT NULL,
|
||||
confidence TEXT NOT NULL,
|
||||
tags_json TEXT NOT NULL DEFAULT '[]',
|
||||
target_refs_json TEXT NOT NULL DEFAULT '{}',
|
||||
PRIMARY KEY (run_id, reviewer_role, finding_id)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS council_groups (
|
||||
run_id TEXT NOT NULL,
|
||||
group_id TEXT NOT NULL,
|
||||
proposal TEXT NOT NULL,
|
||||
bucket TEXT NOT NULL,
|
||||
support_count INTEGER NOT NULL,
|
||||
supporters_json TEXT NOT NULL DEFAULT '[]',
|
||||
dissenters_json TEXT NOT NULL DEFAULT '[]',
|
||||
rationale_summary TEXT NOT NULL DEFAULT '',
|
||||
tags_json TEXT NOT NULL DEFAULT '[]',
|
||||
source_finding_ids_json TEXT NOT NULL DEFAULT '[]',
|
||||
PRIMARY KEY (run_id, group_id)
|
||||
);
|
||||
@@ -0,0 +1,12 @@
|
||||
CREATE TABLE IF NOT EXISTS thread_reads (
|
||||
thread_id TEXT NOT NULL,
|
||||
agent_id TEXT NOT NULL,
|
||||
last_read_message_id TEXT NOT NULL,
|
||||
last_read_at TEXT NOT NULL,
|
||||
PRIMARY KEY(thread_id, agent_id),
|
||||
FOREIGN KEY(thread_id) REFERENCES threads(thread_id),
|
||||
FOREIGN KEY(last_read_message_id) REFERENCES messages(message_id)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_thread_reads_agent
|
||||
ON thread_reads(agent_id, last_read_at);
|
||||
@@ -0,0 +1,9 @@
|
||||
CREATE TABLE IF NOT EXISTS council_inputs (
|
||||
run_id TEXT PRIMARY KEY,
|
||||
prompt TEXT NOT NULL DEFAULT '',
|
||||
target_file TEXT NOT NULL DEFAULT '',
|
||||
repo_path TEXT NOT NULL DEFAULT '',
|
||||
target_task_id TEXT NOT NULL DEFAULT '',
|
||||
created_at TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL
|
||||
);
|
||||
@@ -0,0 +1,8 @@
|
||||
CREATE TABLE IF NOT EXISTS council_reports (
|
||||
run_id TEXT PRIMARY KEY,
|
||||
show_json TEXT NOT NULL DEFAULT '[]',
|
||||
summary_json TEXT NOT NULL DEFAULT '{}',
|
||||
markdown_path TEXT NOT NULL DEFAULT '',
|
||||
created_at TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL
|
||||
);
|
||||
@@ -1,3 +1,8 @@
|
||||
module ai-workflow-skill/packages/coord-core
|
||||
|
||||
go 1.26
|
||||
|
||||
require (
|
||||
github.com/google/uuid v1.6.0
|
||||
modernc.org/sqlite v1.40.1
|
||||
)
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
package protocol
|
||||
|
||||
type CLIError struct {
|
||||
Code string
|
||||
ExitCode int
|
||||
Message string
|
||||
Err error
|
||||
}
|
||||
|
||||
func (e *CLIError) Error() string {
|
||||
return e.Message
|
||||
}
|
||||
|
||||
func (e *CLIError) Unwrap() error {
|
||||
return e.Err
|
||||
}
|
||||
|
||||
func NewCLIError(code string, exitCode int, message string, err error) error {
|
||||
return &CLIError{
|
||||
Code: code,
|
||||
ExitCode: exitCode,
|
||||
Message: message,
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
|
||||
func InvalidInput(message string, err error) error {
|
||||
return NewCLIError("invalid_input", 30, message, err)
|
||||
}
|
||||
|
||||
func NoMatchingWork(message string) error {
|
||||
return NewCLIError("no_matching_work", 10, message, nil)
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
)
|
||||
|
||||
type Success struct {
|
||||
OK bool `json:"ok"`
|
||||
Command string `json:"command"`
|
||||
Data map[string]any `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
type Error struct {
|
||||
OK bool `json:"ok"`
|
||||
Error ErrorPayload `json:"error"`
|
||||
}
|
||||
|
||||
type ErrorPayload struct {
|
||||
Code string `json:"code"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
func WriteJSON(w io.Writer, v any) error {
|
||||
enc := json.NewEncoder(w)
|
||||
enc.SetIndent("", " ")
|
||||
return enc.Encode(v)
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,3 @@
|
||||
package store
|
||||
|
||||
// Package store contains higher-level database access helpers.
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,107 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
dbpkg "ai-workflow-skill/packages/coord-core/db"
|
||||
)
|
||||
|
||||
func TestClaimThreadReturnsLeaseConflictAfterBusyWrite(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
||||
defer cancel()
|
||||
|
||||
dbPath := filepath.Join(t.TempDir(), "coord.db")
|
||||
|
||||
sqlDB, err := dbpkg.Open(ctx, dbPath)
|
||||
if err != nil {
|
||||
t.Fatalf("open base db: %v", err)
|
||||
}
|
||||
defer sqlDB.Close()
|
||||
|
||||
if err := dbpkg.ApplyMigrations(ctx, sqlDB); err != nil {
|
||||
t.Fatalf("apply migrations: %v", err)
|
||||
}
|
||||
|
||||
baseStore := NewInboxStore(sqlDB)
|
||||
thread, _, err := baseStore.Send(ctx, SendInput{
|
||||
FromAgent: "leader",
|
||||
ToAgent: "worker-a",
|
||||
Subject: "race claim",
|
||||
Summary: "race claim",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("seed thread: %v", err)
|
||||
}
|
||||
|
||||
lockerDB, err := dbpkg.Open(ctx, dbPath)
|
||||
if err != nil {
|
||||
t.Fatalf("open locker db: %v", err)
|
||||
}
|
||||
defer lockerDB.Close()
|
||||
|
||||
lockTx, err := lockerDB.BeginTx(ctx, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("begin locker tx: %v", err)
|
||||
}
|
||||
|
||||
now := nowUTC()
|
||||
if _, err := lockTx.ExecContext(
|
||||
ctx,
|
||||
`INSERT INTO leases (
|
||||
thread_id, agent_id, lease_token, claimed_at, expires_at, released_at
|
||||
) VALUES (?, ?, ?, ?, ?, NULL)`,
|
||||
thread.ThreadID,
|
||||
"worker-a",
|
||||
"lease_locked",
|
||||
formatTime(now),
|
||||
formatTime(now.Add(5*time.Minute)),
|
||||
); err != nil {
|
||||
t.Fatalf("seed active lease in tx: %v", err)
|
||||
}
|
||||
|
||||
if _, err := lockTx.ExecContext(
|
||||
ctx,
|
||||
`UPDATE threads
|
||||
SET status = ?, assigned_to = ?, latest_message_id = ?, updated_at = ?
|
||||
WHERE thread_id = ?`,
|
||||
"claimed",
|
||||
"worker-a",
|
||||
"msg_locked",
|
||||
formatTime(now),
|
||||
thread.ThreadID,
|
||||
); err != nil {
|
||||
t.Fatalf("seed claimed thread in tx: %v", err)
|
||||
}
|
||||
|
||||
commitDone := make(chan error, 1)
|
||||
go func() {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
commitDone <- lockTx.Commit()
|
||||
}()
|
||||
|
||||
claimDB, err := dbpkg.Open(ctx, dbPath)
|
||||
if err != nil {
|
||||
t.Fatalf("open claim db: %v", err)
|
||||
}
|
||||
defer claimDB.Close()
|
||||
|
||||
claimStore := NewInboxStore(claimDB)
|
||||
_, err = claimStore.ClaimThread(ctx, ClaimInput{
|
||||
ThreadID: thread.ThreadID,
|
||||
Agent: "worker-b",
|
||||
LeaseSeconds: 300,
|
||||
})
|
||||
if !errors.Is(err, ErrLeaseConflict) {
|
||||
t.Fatalf("expected lease conflict after busy retry, got %v", err)
|
||||
}
|
||||
|
||||
if err := <-commitDone; err != nil {
|
||||
t.Fatalf("commit locker tx: %v", err)
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user