p9: remove pending_approval/pending_review from protocol
Drop status constants, readPendingFromStore, pending-approval/pending-review/resume ctl handlers, and the pending virtual file from the 9P directory listing. No migration needed — status is a string field; unused values simply won't appear. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
c57e98e167
commit
cb97b3640c
|
|
@ -18,15 +18,6 @@ import (
|
|||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// Approval gate statuses extend the beads status set locally.
|
||||
// These statuses are not surfaced by GetReadyWork (which only returns
|
||||
// open/in_progress), ensuring bots do not claim work that is pending
|
||||
// human approval or review.
|
||||
const (
|
||||
StatusPendingApproval = bd.Status("pending_approval") // Awaiting APPROVAL_RESPONSE from human
|
||||
StatusPendingReview = bd.Status("pending_review") // Awaiting REVIEW_RESPONSE from human
|
||||
)
|
||||
|
||||
// Capability level labels — portable model-tier hints carried on beads.
|
||||
// These are label values of the form "capability:<level>". The Conductor
|
||||
// maps them to backend-specific model names at session-spawn time.
|
||||
|
|
@ -260,8 +251,6 @@ func (b *BeadsFS) readFromMount(m *MountedProject, endpoint string) ([]byte, err
|
|||
limit = 100
|
||||
}
|
||||
return b.readReadyFromStore(m.store, limit)
|
||||
case len(parts) == 1 && parts[0] == "pending":
|
||||
return b.readPendingFromStore(m.store)
|
||||
case len(parts) == 1 && parts[0] == "blocked":
|
||||
return b.readBlockedFromStore(m.store)
|
||||
case len(parts) == 1 && parts[0] == "stale":
|
||||
|
|
@ -542,24 +531,6 @@ func (b *BeadsFS) readBlockedFromStore(store bd.Storage) ([]byte, error) {
|
|||
return json.MarshalIndent(blocked, "", " ")
|
||||
}
|
||||
|
||||
// readPending returns beads in pending_approval or pending_review status,
|
||||
// i.e. those awaiting a human APPROVAL_RESPONSE or REVIEW_RESPONSE.
|
||||
|
||||
func (b *BeadsFS) readPendingFromStore(store bd.Storage) ([]byte, error) {
|
||||
issues, err := store.SearchIssues(b.ctx, "", bd.IssueFilter{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pending := []*bd.Issue{}
|
||||
for _, issue := range issues {
|
||||
if issue.Status == StatusPendingApproval || issue.Status == StatusPendingReview {
|
||||
pending = append(pending, issue)
|
||||
}
|
||||
}
|
||||
return json.MarshalIndent(pending, "", " ")
|
||||
}
|
||||
|
||||
|
||||
func (b *BeadsFS) readDependenciesMetaFromStore(store bd.Storage, beadID string) ([]byte, error) {
|
||||
deps, err := store.GetDependenciesWithMetadata(b.ctx, beadID)
|
||||
if err != nil {
|
||||
|
|
@ -937,71 +908,6 @@ func (b *BeadsFS) executeCtlOnStore(store bd.Storage, cmd string) error {
|
|||
}
|
||||
return store.CreateIssues(b.ctx, issues, actor)
|
||||
|
||||
// Approval gate commands.
|
||||
// Bots should call these after sending an APPROVAL_REQUEST or REVIEW_REQUEST
|
||||
// to the user. The human responds via the inbox UI; on approve/reject the
|
||||
// bot calls "resume" or "fail" to continue or abort work.
|
||||
|
||||
case "pending-approval":
|
||||
// Atomically set status=pending_approval and assignee (defaults to "user").
|
||||
// Usage: pending-approval <bead-id> [assignee]
|
||||
// Canonical workflow:
|
||||
// 1. Bot sends APPROVAL_REQUEST to user mailbox
|
||||
// 2. Bot calls: echo "pending-approval <id>" | 9p write anvillm/beads/ctl
|
||||
// 3. Human approves → bot calls: echo "resume <id> <bot-id>" | 9p write anvillm/beads/ctl
|
||||
// 4. Human rejects → bot calls: echo "fail <id> 'rejected'" | 9p write anvillm/beads/ctl
|
||||
if len(args) < 1 {
|
||||
return fmt.Errorf("usage: pending-approval <bead-id> [assignee]")
|
||||
}
|
||||
assignee := "user"
|
||||
if len(args) > 1 {
|
||||
assignee = args[1]
|
||||
}
|
||||
updates := map[string]any{
|
||||
"status": StatusPendingApproval,
|
||||
"assignee": assignee,
|
||||
}
|
||||
return store.UpdateIssue(b.ctx, args[0], updates, actor)
|
||||
|
||||
case "pending-review":
|
||||
// Atomically set status=pending_review and assignee (defaults to "user").
|
||||
// Usage: pending-review <bead-id> [assignee]
|
||||
// Canonical workflow:
|
||||
// 1. Bot sends REVIEW_REQUEST to user mailbox
|
||||
// 2. Bot calls: echo "pending-review <id>" | 9p write anvillm/beads/ctl
|
||||
// 3. Human responds → bot calls: echo "resume <id> <bot-id>" | 9p write anvillm/beads/ctl
|
||||
// 4. Human rejects → bot calls: echo "fail <id> 'review failed'" | 9p write anvillm/beads/ctl
|
||||
if len(args) < 1 {
|
||||
return fmt.Errorf("usage: pending-review <bead-id> [assignee]")
|
||||
}
|
||||
assignee := "user"
|
||||
if len(args) > 1 {
|
||||
assignee = args[1]
|
||||
}
|
||||
updates := map[string]any{
|
||||
"status": StatusPendingReview,
|
||||
"assignee": assignee,
|
||||
}
|
||||
return store.UpdateIssue(b.ctx, args[0], updates, actor)
|
||||
|
||||
case "resume":
|
||||
// Atomically set status=in_progress and assignee after human approval/review.
|
||||
// Usage: resume <bead-id> [assignee]
|
||||
// Assignee defaults to "user"; pass the bot's agent ID to hand work back to a bot.
|
||||
// Use "fail" instead if the human rejected.
|
||||
if len(args) < 1 {
|
||||
return fmt.Errorf("usage: resume <bead-id> [assignee]")
|
||||
}
|
||||
assignee := "user"
|
||||
if len(args) > 1 {
|
||||
assignee = args[1]
|
||||
}
|
||||
updates := map[string]any{
|
||||
"status": bd.StatusInProgress,
|
||||
"assignee": assignee,
|
||||
}
|
||||
return store.UpdateIssue(b.ctx, args[0], updates, actor)
|
||||
|
||||
case "unclaim":
|
||||
// Atomically clear assignee and reset status to open.
|
||||
// Usage: unclaim <bead-id>
|
||||
|
|
|
|||
|
|
@ -555,7 +555,7 @@ func (s *Server) walk(cs *connState, fc *plan9.Fcall) *plan9.Fcall {
|
|||
if _, isMount := mounts[mountName]; isMount {
|
||||
// Check if name is a control file or bead ID
|
||||
switch name {
|
||||
case "cwd", "ctl", "list", "ready", "pending", "stats", "blocked", "stale", "query", "config":
|
||||
case "cwd", "ctl", "list", "ready", "stats", "blocked", "stale", "query", "config":
|
||||
qid = plan9.Qid{Type: QTFile, Path: qidBeadsBase + hashID(mountName+name)}
|
||||
newPath = path + "/" + name
|
||||
default:
|
||||
|
|
@ -1235,10 +1235,6 @@ func (s *Server) readDir(path string, offset uint64, count uint32) []byte {
|
|||
Qid: plan9.Qid{Type: QTFile, Path: qidBeadsBase + hashID(mountName+"ready")}, Mode: 0444, Name: "ready",
|
||||
Uid: "q", Gid: "q", Muid: "q",
|
||||
})
|
||||
dirs = append(dirs, plan9.Dir{
|
||||
Qid: plan9.Qid{Type: QTFile, Path: qidBeadsBase + hashID(mountName+"pending")}, Mode: 0444, Name: "pending",
|
||||
Uid: "q", Gid: "q", Muid: "q",
|
||||
})
|
||||
dirs = append(dirs, plan9.Dir{
|
||||
Qid: plan9.Qid{Type: QTFile, Path: qidBeadsBase + hashID(mountName+"stats")}, Mode: 0444, Name: "stats",
|
||||
Uid: "q", Gid: "q", Muid: "q",
|
||||
|
|
|
|||
Reference in New Issue