Repository navigation
Skein-SpecSync Alignment: Priority-Driven Dispatch #47
Description
Skein-SpecSync Alignment: Priority-Driven Dispatch
Goal: Make Skein respect OpenSpec prioritization so humans can direct focus ("work on feature X") and agents automatically manage workflow state.
Problem
Skein currently ignores specsync's priority and stage system. It treats all changes equally, picking work arbitrarily rather than by human-set priority. This creates two problems:
- No focus: Can't tell Skein "focus on the critical security fix" — it might work on unrelated refactors
- No state tracking: Agents can't update workflow state as they work; stages stay empty, priorities unused
- Parallel systems: specsync metadata and Skein's queue are disconnected; humans have to manage both
Result: specsync's rich workflow model sits unused while Skein makes uninformed work decisions.
Solution Overview
Integrate specsync's priority and stage system deeply into Skein:
- Dispatcher reads specsync state → sorts backlog by priority before assigning work
- Agents write specsync state → automatically update stage as work progresses (active → in-review → complete)
- CLI priority commands affect dispatch →
specsync set-priorityimmediately changes what Skein picks next - Human-focused queuing →
skein focus <change>sets it highest priority, all others lower - Board reconciliation automatic → Skein syncs to GitHub Projects without manual intervention
This makes specsync the single source of truth for work prioritization and state, with Skein as the agent orchestrator.
Detailed Specification
1. Priority Dispatch (Dispatcher Integration)
Current: Skein's supervisor picks next change arbitrarily from queue.
Desired: Before assigning to an agent, sort by specsync priority.
Changes to skein queue and Dispatcher Logic
// Pseudo-code for new dispatch decision
func nextChangeToAssign() Change {
// Load all changes with specsync metadata
changes := loadOpenSpecChanges()
// Filter by stage: only backlog, or include active if no backlog
available := filterByStage(changes, []Stage{backlog, active})
// Sort by priority (descending), then by created (ascending)
sort.Slice(available, func(i, j int) bool {
pri_i := available[i].Priority ?? 0
pri_j := available[j].Priority ?? 0
if pri_i != pri_j {
return pri_i > pri_j // Higher priority first
}
return available[i].Created.Before(available[j].Created) // Older first if tied
})
// Skip blocked/archived/in-review
for _, c := range available {
if canAssign(c) {
return c
}
}
return nil
}New Metadata in Queue Display
skein queue output should show:
SLUG STATUS PRI TASKS STAGE P_SOURCE MODIFIED
feature-auth-rewrite pending 95 5/8 backlog manual Jul 15 22:00
bugfix-critical-crash pending 80 2/2 backlog manual Jul 15 21:50
refactor-db-layer pending 50 12/18 backlog derived Jul 14 10:00
nice-to-have-feature pending 20 0/5 backlog default Jul 10 05:00
Where P_SOURCE is: manual (set via CLI), derived (from tasks.md), default (fallback = 0).
2. State Management by Agents (Agent Integration)
Current: Agents work but never call specsync set-stage.
Desired: Agents automatically update specsync state as they work.
Agent Lifecycle Hooks
# In .skein/config.yaml or agent templates
agent_hooks:
on_assign:
# When agent starts working on a change
- specsync set-stage $(SLUG) active
on_progress:
# When agent makes substantive progress (commit, passing test)
- specsync set-stage $(SLUG) active # idempotent, ensures active
on_submit_review:
# When agent opens PR or submits for review
- specsync set-stage $(SLUG) in-review
on_complete:
# When all tasks checked and validated
- specsync set-stage $(SLUG) complete
on_block:
# When agent hits external dependency
- specsync set-stage $(SLUG) blocked
# Agent adds reason to tasks.md:
# "- [ ] waiting for @human to review design doc"Agent Skills
Create new skills agents can call:
skein skill add specsync-workflow
# Agents can now call in their routines:
# /specsync-workflow status → get current priority/stage
# /specsync-workflow focus-next → find and report next high-priority change
# /specsync-workflow mark-blocked → set blocked + reason3. Human Prioritization Commands
Desired: Simple CLI to express focus.
New/Extended Commands
# Set raw priority (1-100)
specsync set-priority feature-x 90
# Human-friendly "focus" command
skein focus feature-x
# → Sets priority to 99, all others relative to this one stay same but lower
# → Forces stage to backlog (ready to pick up)
# → Logs decision to audit trail
# Unfocus (clear priority)
skein unfocus feature-x
# → Resets to default (0) or derived priority
# Block a change (waiting on something)
skein block feature-x "waiting for design review"
# → Calls specsync set-stage feature-x blocked
# → Adds reason to tasks.md
# → Skein won't pick it until unblocked
# Unblock
skein unblock feature-x
# → Moves back to backlog4. Board Sync Automation
Current: Manual specsync sync or GitHub workflow.
Desired: Automatic syncing whenever specsync state changes, with human-move detection.
Supervisor Background Task
// In Skein supervisor's main loop
func backgroundBoardSync() {
ticker := time.NewTicker(5 * time.Minute) // Configurable
for range ticker.C {
changes := loadOpenSpecChanges()
for _, c := range changes {
if c.HasRef() && target.Configured() {
plan, err := specsyncSync(c)
if err != nil {
logWarning("board sync failed for %s: %v", c.Slug, err)
continue
}
// Log what happened
if plan.StatusSkipped != "" {
logInfo("board: %s - %s", c.Slug, plan.StatusSkipped)
}
if plan.StatusName != "" {
logInfo("board: %s - updated to %s", c.Slug, plan.StatusName)
}
}
}
}
}Or trigger on specsync state change:
# Hook: specsync set-stage fires → Skein detects file change → auto-syncs
specsync set-stage my-change active
# → .specsync/metadata.json updated
# → Skein's file watcher detects change
# → Triggers `specsync sync --dry-run` (preview)
# → Logs decision (pushed / skipped human-move / conflict)5. Priority Interpretation
P1-P5 → specsync 1-100 Mapping (Optional)
For compatibility with Skein's native P1-P5:
skein prioritize my-change P1 → specsync set-priority my-change 90
skein prioritize my-change P2 → specsync set-priority my-change 70
skein prioritize my-change P3 → specsync set-priority my-change 50
skein prioritize my-change P4 → specsync set-priority my-change 30
skein prioritize my-change P5 → specsync set-priority my-change 10
Or allow both systems to coexist:
- specsync priority: Fine-grained (1-100), committed, stable
- Skein P1-P5: Temporary session state, useful for real-time tuning
6. Data Flow Architecture
┌─────────────────────────────────────────────────────────────┐
│ OpenSpec Changes │
│ openspec/changes/<slug>/ │
└───────────────────────┬─────────────────────────────────────┘
│
├─ proposal.md (spec)
├─ tasks.md (work items)
├─ links.md (cross-refs)
└─ .specsync/
├─ metadata.json ← PRIORITY & STAGE (source of truth)
├─ refs.json (cached GitHub issue ref)
└─ board.json (cached board binding for 3-way merge)
╭───────────┴──────────╮
│ │
┌──────▼────────┐ ┌──────▼────────┐
│ Skein Queue │ │ GitHub Projects
│ (sessions) │ │ Board
└──────┬────────┘ └──────┬────────┘
│ │
specsync reads for: specsync writes to:
- Priority dispatch - Issue sync (existing)
- Stage filtering - Project card status (new)
- Human-move detection (new)
╭────────────┬────────────╮
│ │ │
┌──────▼──────┐ ┌───▼────┐ ┌───▼──────┐
│ Coder │ │Debugger│ │Reviewer │
│ Agent │ │ Agent │ │ Agent │
└─────┬───────┘ └───┬────┘ └───┬──────┘
│ │ │
Call on_assign hook on_block on_submit_review
↓ ↓ ↓
specsync set-stage active/blocked/in-review
│ │ │
└──────────────┼──────────┘
│
.specsync/metadata.json updated
│
Skein's file watcher detects
│
Trigger background board sync
│
.specsync/board.json updated
(three-way merge handles human moves)
7. Implementation Phases
Phase 1: Foundation (1-2 weeks)
- Extend
skein queueto read .specsync/metadata.json - Display priority and stage in queue output
- Implement priority-based sorting in dispatcher (dry-run only)
- Test with manual specsync state changes
Phase 2: Agent Integration (2-3 weeks)
- Add agent hooks:
on_assign,on_block,on_submit_review,on_complete - Create
specsync-workflowskill with state queries - Update coder/debugger/reviewer agents to call hooks
- Test workflow progression with real agent runs
Phase 3: Human Commands (1 week)
- Implement
skein focus <slug> - Implement
skein block <slug> <reason> - Implement
skein unblock <slug> - Test prioritization immediate effect on next dispatch
Phase 4: Board Automation (1-2 weeks)
- Add background sync task to supervisor
- Implement file watcher trigger for metadata changes
- Test three-way merge conflict detection
- Document board conflict resolution workflow
Phase 5: Polish & Observability (1 week)
- Add priority audit logging
- Extend
skein boardto show priority/stage - Create observability dashboard
- Comprehensive integration tests
8. Configuration
New .skein/config.yaml section:
specsync:
# Enable specsync priority dispatch
enabled: true
# How often to sync board state in background
board_sync_interval: 5m
# File watcher triggers immediate sync on metadata change
auto_sync_on_metadata_change: true
# Audit logging
audit_log: .skein/specsync-audit.log
# Priority interpretation
priority_model: specsync # or "p1-p5" for Skein compat, or "hybrid"
# Blocked change behavior
blocked_behavior: skip # skip immediately, or "wait-with-timeout"
# Board reconciliation
board:
enabled: true
conflict_strategy: report # "report", "prompt-human", or "favor-local"
background_sync: true9. Migration & Adoption
Existing Changes
For changes already in queue:
# Option 1: Backfill priorities based on task count
skein migrate specsync --auto-prioritize
# Changes with more tasks → higher priority
# Changes with less → lower priority
# Option 2: Assign all to backlog, let humans prioritize
skein migrate specsync --clear
# Resets all to priority=0, stage=backlogNew Workflows
- Human creates change with
specsync create(or via proposal.md) - Human runs
specsync set-priority <slug> 75to indicate importance - Skein reads priority on next queue update
- When agent assigned, calls
specsync set-stage <slug> active - As agent works, stage updates (active → in-review → complete)
- specsync syncs to board with human-move detection
10. Success Criteria
- ✅
skein focus feature-ximmediately affects next dispatch decision - ✅ Queue output shows priority and stage, sorted by priority
- ✅ Agents update specsync state without manual intervention
- ✅ Human board moves are preserved (not clobbered)
- ✅
specsync set-prioritychanges take effect on next supervisor cycle - ✅ All changes with
stage=blockedare skipped until unblocked - ✅ Audit log shows all priority/stage changes with reasons
- ✅ Integration tests cover dispatch, state progression, board sync
Files to Modify
skein/supervisor/dispatch.go— Dispatcher priority logicskein/queue/queue.go— Queue display and sortingskein/commands/focus.go— New focus/unfocus/block commandsskein/commands/queue.go— Extend to show priority/stageskein/supervisor/background.go— Background board syncskein/config/schema.go— New specsync config section.skein/config.yaml— New config values.skein/agents/*.yaml— Agent hooks configuration- Tests:
*_test.gofor all above
Skills & Automations to Create
specsync-dispatch— Agent skill to query and report next priority changespecsync-workflow— Agent skill to update state (block, mark-review, etc.)board-sync— Skill to manually trigger board reconciliationpriority-audit— Skill to show priority history
Open Questions
- Priority inheritance: If a change has no explicit priority, should we derive from task count (complexity) or commit velocity?
- Dynamic re-prioritization: Should human-set priority fade over time (e.g., "reset to default after 48 hours") or persist indefinitely?
- P1-P5 coexistence: Keep Skein's native P1-P5 for session tuning, or fully migrate to specsync 1-100?
- Conflict escalation: When board and local conflict, should Skein auto-escalate to human or make a decision rule-based?
- Archive behavior: Should archived changes be hidden from queue entirely or shown grayed-out?
Future Extensions
- Swimlanes by stage:
skein boardgrouped by backlog/active/blocked/in-review - Dependency awareness:
specsync linkcreates blockage relationships; defer dependent changes until blocker complete - Capacity planning:
skein healthshows capacity vs. active changes; suggest unblocking or deferring based on capacity - AI-driven prioritization: LLM analyzer reads change descriptions, suggests priority based on business value + complexity
- Multi-project coordination: Sync priorities across linked changes in different repos
Tasks
Implementation Tasks
NOTE: All tasks below are Skein-side changes (separate codebase). No Skein code exists in this specsync repo. This change remains 0/131 complete until work begins in the Skein repository.
Phase 1: Foundation — Queue & Dispatcher (Week 1-2)
Dispatcher Priority Logic
- Read .specsync/metadata.json in loadOpenSpecChanges()
- Parse priority field (null → 0, integer 1-100)
- Implement sortByPriority() function
- Add stage filtering: skip blocked/archived unless explicitly queried
- Test: dispatch picks higher-priority change first
- Test: equal priority → older created date wins
Queue Display Enhancement
- Extend skein queue output columns: add PRI, P_SOURCE, STAGE
- Parse .specsync/metadata.json for each change
- Determine P_SOURCE: manual / derived / default
- Format output with proper alignment
- Add --stage flag to filter queue by stage
- Test:
skein queue --stage activeshows only active - Test:
skein queue --sort prioritysorts descending
Dry-Run Dispatcher (No State Change)
- Create newDispatchDecision struct with reason
- Log dispatcher picks: "picked feature-x (priority=75, 5 active jobs)"
- Add --dry-run flag to supervisor for testing
- Test: confirm priority-based picking without mutation
Phase 2: Agent Integration (Week 2-3)
Agent Hooks Infrastructure
- Define AgentHook interface in skein/supervisor
- Implement hook types: onAssign, onProgress, onSubmitReview, onComplete, onBlock
- Load hooks from .skein/config.yaml
- Execute hooks as strings (bash + env vars)
- Test: hook runs when agent assigned
- Test: environment variables available (SLUG, CHANGE_DIR, etc.)
Hook Execution & Error Handling
- Capture hook output and errors
- Log hook execution to audit trail
- If hook fails: log warning, continue (don't block agent)
- Timeout hooks after 30s (configurable)
- Test: failed hook doesn't crash supervisor
Update Existing Agents
- Add on_assign hook to coder agent template
- Add on_block hook to coder/debugger agents
- Add on_submit_review hook to coder agent
- Add on_complete hook to reviewer agent
- Test: hooks fire during real agent runs
Create specsync-workflow Skill
- Implement
statuscommand: read and display current priority/stage - Implement
focus-nextcommand: find and report next high-priority backlog change - Implement
mark-blockedcommand: set stage to blocked + add reason - Implement
mark-reviewcommand: set stage to in-review - Implement
mark-completecommand: set stage to complete - Test: skill outputs are parseable JSON
Phase 3: Human Commands (Week 3)
skein focus Command
- Implement
skein focus <slug>command - Set priority to 99 (maximum)
- Log decision to audit trail with timestamp
- Ensure stage is backlog (ready to pick)
- Test: focused change picked first on next dispatch
- Test: audit log records focus event
skein unfocus Command
- Implement
skein unfocus <slug>command - Reset priority to 0 (or derived if available)
- Log decision to audit trail
- Test: unfocused change returns to normal sort order
skein block Command
- Implement
skein block <slug> <reason>command - Set stage to blocked
- Store reason in tasks.md as checklist item
- Log decision to audit trail
- Test: blocked change skipped in dispatch
- Test: reason visible in tasks.md
skein unblock Command
- Implement
skein unblock <slug>command - Set stage back to backlog
- Log decision to audit trail
- Test: unblocked change available for dispatch
Integration Tests
- Test: focus → dispatch picks it → unfocus → normal sort
- Test: block → dispatch skips it → unblock → available again
- Test: focus + block → block wins (skipped)
Phase 4: Board Automation (Week 4-5)
Background Board Sync Task
- Implement backgroundBoardSync() in supervisor main loop
- Read all changes every 5 minutes (configurable)
- For each change: check if has ref + board configured
- Call specsync sync (or sync --dry-run for preview)
- Log results: pushed / skipped (reason) / error
- Test: board updates on schedule
- Test: human-move detection works (from Phase 3 board.go)
File Watcher Trigger
- Watch .specsync/metadata.json for changes
- On change: immediately trigger sync (not on schedule)
- Test: priority change → board updates within 1s
- Test: stage change → board updates within 1s
Conflict Reporting
- Parse sync plan: detect StatusSkipped reason
- If StatusSkipped contains "human moved": log as human-move
- If StatusSkipped contains "conflict": log as conflict, escalate?
- Test: human-move logged correctly
- Test: conflict detected and reported
Board State Audit
- Add command:
skein specsync auditto show sync history - Parse .specsync/board.json bindings
- Display last sync time, local base, remote base
- Show human-move history
- Test: audit output readable and useful
Phase 5: Configuration & Documentation (Week 5-6)
Config Schema
- Define new specsync section in .skein/config.yaml
- Add: enabled, board_sync_interval, auto_sync_on_metadata_change
- Add: audit_log path, priority_model, blocked_behavior
- Add: board conflict_strategy
- Validate config on startup
- Test: invalid config rejected
Agent Hooks Configuration
- Document hook format in .skein/config.yaml reference
- Add example hooks for all types
- Test: hooks loaded and parsed correctly
Migration Tooling
- Implement
skein migrate specsync --auto-prioritize - Implement
skein migrate specsync --clear - Test: backfill works without data loss
- Test: clear resets safely
Documentation
- Update WORKFLOW.md with Skein dispatch explanation
- Create
.claude/CLAUDE.mdsection: how agents use priority - Document skein focus/block commands
- Document audit logging
- Create troubleshooting guide: "priority not affecting dispatch"
Phase 6: Integration Tests & Polish (Week 6)
End-to-End Tests
- Test:
specsync set-priority→ next dispatch affected - Test:
skein focus→ agent assigned → hook fires → stage changes - Test: board conflict → human-move detected → logged
- Test: full workflow: backlog → active → in-review → complete
- Test: agent blocked → dispatch skips → human unblocks → dispatch resumes
Performance
- Measure: queue load time with 100+ changes
- Measure: dispatcher decision time (< 100ms)
- Measure: background sync overhead (<5% CPU during sync)
- Test: no memory leaks on long runs
Observability
- Add priority metrics: average, distribution, changes/min
- Add dispatch metrics: picks/min, avg priority of picked
- Add board metrics: syncs/min, human-moves/min, conflicts/min
- Create dashboard: show active priorities, blocked changes, sync health
Documentation Polish
- Review all docs for completeness
- Add architecture diagrams
- Add examples: common scenarios
- Create troubleshooting: "Why isn't my priority working?"
Meta Tasks
Coordination
- Link related OpenSpec changes (e.g., board-state-reconciliation)
- Create roadmap showing phases
- Identify blocker dependencies (e.g., Phase 2 needs specsync-workflow skill)
Review Gates
- Code review: dispatcher logic correct?
- Security review: no injection vectors in hook execution?
- Performance review: dispatch time acceptable?
- UX review: CLI commands intuitive?
Release Checklist
- All tests passing
- Performance benchmarks met
- Documentation complete
- Backward compatibility verified (old configs still work)
- Migration path tested
- Release notes written
Activity