mirror of
https://github.com/multica-ai/multica.git
synced 2026-06-17 11:48:42 +02:00
Compare commits
2 Commits
template-r
...
agent/lamb
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5552f96e4c | ||
|
|
0a16fa784c |
@@ -341,7 +341,7 @@ multica issue assign <id> --unassign
|
||||
multica issue status <id> in_progress
|
||||
```
|
||||
|
||||
Valid statuses: `backlog`, `todo`, `in_progress`, `in_review`, `done`, `blocked`, `cancelled`.
|
||||
Valid statuses: `backlog`, `todo`, `in_progress`, `needs_review`, `done`, `blocked`, `cancelled`.
|
||||
|
||||
### Comments
|
||||
|
||||
|
||||
@@ -260,7 +260,7 @@ multica issue assign <id> --unassign
|
||||
multica issue status <id> in_progress
|
||||
```
|
||||
|
||||
Valid statuses: `backlog`, `todo`, `in_progress`, `in_review`, `done`, `blocked`, `cancelled`.
|
||||
Valid statuses: `backlog`, `todo`, `in_progress`, `needs_review`, `done`, `blocked`, `cancelled`.
|
||||
|
||||
### Comments
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ Multica has seven statuses. **Any status can move directly to any other** — Mu
|
||||
| `backlog` | Not scheduled yet |
|
||||
| `todo` | Scheduled, ready to start |
|
||||
| `in_progress` | Being worked on |
|
||||
| `in_review` | Awaiting review |
|
||||
| `needs_review` | Awaiting review |
|
||||
| `done` | Completed |
|
||||
| `blocked` | Stuck on an external factor |
|
||||
| `cancelled` | Cancelled |
|
||||
|
||||
@@ -28,7 +28,7 @@ Multica 提供七种状态。**任何状态可以直接改到任何状态**—
|
||||
| `backlog` | 还没排期 |
|
||||
| `todo` | 已排期、准备开工 |
|
||||
| `in_progress` | 正在做 |
|
||||
| `in_review` | 等待 review |
|
||||
| `needs_review` | 等待 review |
|
||||
| `done` | 已完成 |
|
||||
| `blocked` | 被外部因素卡住 |
|
||||
| `cancelled` | 已取消 |
|
||||
|
||||
@@ -137,7 +137,7 @@ const allAssignees: Assignee[] = [
|
||||
{ type: "agent", id: "tina", name: "Tina-dev" },
|
||||
];
|
||||
|
||||
const statusCycle: IssueStatus[] = ["backlog", "todo", "in_progress", "in_review", "done"];
|
||||
const statusCycle: IssueStatus[] = ["backlog", "todo", "in_progress", "needs_review", "done"];
|
||||
const priorityCycle: IssuePriority[] = ["none", "low", "medium", "high", "urgent"];
|
||||
|
||||
function TeammatesVisual() {
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
- ⬜ Not started
|
||||
- 🔍 Source research(正在读源码验证)
|
||||
- ✍️ Drafting(正在写初稿)
|
||||
- 👀 In review(待 review)
|
||||
- 👀 Needs review(待 review)
|
||||
- ✅ Shipped
|
||||
|
||||
### 1.4 Flag(只在需要决策时填)
|
||||
@@ -198,7 +198,7 @@ multica issue assign <issue-id> --agent <agent-slug>
|
||||
|
||||
## 板块 1:Welcome & Quickstart
|
||||
|
||||
### 1.1 Welcome — 👀 In review [v1]
|
||||
### 1.1 Welcome — 👀 Needs review [v1]
|
||||
|
||||
- **Source files**: `README.md`, `docs/docs-rewrite-plan.md`(定位段), `apps/docs/content/docs/index.mdx`(现状)
|
||||
- **目标读者**: P0 新用户 / evaluator(第一次听说 Multica)
|
||||
@@ -336,7 +336,7 @@ multica issue assign <issue-id> --agent <agent-slug>
|
||||
- **写什么**(1500-2000 字):
|
||||
- **Issues 部分**:
|
||||
- Polymorphic assignee(member/agent/null)——第一次正式提"可以分配给 agent"
|
||||
- Status 枚举(backlog/todo/in_progress/in_review/done/blocked/cancelled),**无强制 FSM**
|
||||
- Status 枚举(backlog/todo/in_progress/needs_review/done/blocked/cancelled),**无强制 FSM**
|
||||
- Priority / Label / Subscription / Reaction / Dependency / Bulk 操作
|
||||
- Issue number per-workspace 自增
|
||||
- Comment reply 树、@mention
|
||||
|
||||
@@ -4,7 +4,7 @@ export const STATUS_ORDER: IssueStatus[] = [
|
||||
"backlog",
|
||||
"todo",
|
||||
"in_progress",
|
||||
"in_review",
|
||||
"needs_review",
|
||||
"done",
|
||||
"blocked",
|
||||
"cancelled",
|
||||
@@ -14,7 +14,7 @@ export const ALL_STATUSES: IssueStatus[] = [
|
||||
"backlog",
|
||||
"todo",
|
||||
"in_progress",
|
||||
"in_review",
|
||||
"needs_review",
|
||||
"done",
|
||||
"blocked",
|
||||
"cancelled",
|
||||
@@ -25,7 +25,7 @@ export const BOARD_STATUSES: IssueStatus[] = [
|
||||
"backlog",
|
||||
"todo",
|
||||
"in_progress",
|
||||
"in_review",
|
||||
"needs_review",
|
||||
"done",
|
||||
"blocked",
|
||||
];
|
||||
@@ -43,7 +43,7 @@ export const STATUS_CONFIG: Record<
|
||||
backlog: { label: "Backlog", iconColor: "text-muted-foreground", hoverBg: "hover:bg-accent", dividerColor: "bg-muted-foreground/40", columnBg: "bg-muted/40" },
|
||||
todo: { label: "Todo", iconColor: "text-muted-foreground", hoverBg: "hover:bg-accent", dividerColor: "bg-muted-foreground/40", columnBg: "bg-muted/40" },
|
||||
in_progress: { label: "In Progress", iconColor: "text-warning", hoverBg: "hover:bg-warning/10", dividerColor: "bg-warning", columnBg: "bg-warning/5" },
|
||||
in_review: { label: "In Review", iconColor: "text-success", hoverBg: "hover:bg-success/10", dividerColor: "bg-success", columnBg: "bg-success/5" },
|
||||
needs_review: { label: "Needs Review", iconColor: "text-success", hoverBg: "hover:bg-success/10", dividerColor: "bg-success", columnBg: "bg-success/5" },
|
||||
done: { label: "Done", iconColor: "text-info", hoverBg: "hover:bg-info/10", dividerColor: "bg-info", columnBg: "bg-info/5" },
|
||||
blocked: { label: "Blocked", iconColor: "text-destructive", hoverBg: "hover:bg-destructive/10", dividerColor: "bg-destructive", columnBg: "bg-destructive/5" },
|
||||
cancelled: { label: "Cancelled", iconColor: "text-muted-foreground", hoverBg: "hover:bg-accent", dividerColor: "bg-muted-foreground/40", columnBg: "bg-muted/40" },
|
||||
|
||||
@@ -4,7 +4,7 @@ export type IssueStatus =
|
||||
| "backlog"
|
||||
| "todo"
|
||||
| "in_progress"
|
||||
| "in_review"
|
||||
| "needs_review"
|
||||
| "done"
|
||||
| "blocked"
|
||||
| "cancelled";
|
||||
|
||||
@@ -203,14 +203,14 @@ vi.mock("@multica/core/api", () => ({
|
||||
|
||||
// Mock issue config
|
||||
vi.mock("@multica/core/issues/config", () => ({
|
||||
ALL_STATUSES: ["backlog", "todo", "in_progress", "in_review", "done", "blocked", "cancelled"],
|
||||
BOARD_STATUSES: ["backlog", "todo", "in_progress", "in_review", "done", "blocked"],
|
||||
STATUS_ORDER: ["backlog", "todo", "in_progress", "in_review", "done", "blocked", "cancelled"],
|
||||
ALL_STATUSES: ["backlog", "todo", "in_progress", "needs_review", "done", "blocked", "cancelled"],
|
||||
BOARD_STATUSES: ["backlog", "todo", "in_progress", "needs_review", "done", "blocked"],
|
||||
STATUS_ORDER: ["backlog", "todo", "in_progress", "needs_review", "done", "blocked", "cancelled"],
|
||||
STATUS_CONFIG: {
|
||||
backlog: { label: "Backlog", iconColor: "text-muted-foreground", hoverBg: "hover:bg-accent" },
|
||||
todo: { label: "Todo", iconColor: "text-muted-foreground", hoverBg: "hover:bg-accent" },
|
||||
in_progress: { label: "In Progress", iconColor: "text-warning", hoverBg: "hover:bg-warning/10" },
|
||||
in_review: { label: "In Review", iconColor: "text-success", hoverBg: "hover:bg-success/10" },
|
||||
needs_review: { label: "Needs Review", iconColor: "text-success", hoverBg: "hover:bg-success/10" },
|
||||
done: { label: "Done", iconColor: "text-info", hoverBg: "hover:bg-info/10" },
|
||||
blocked: { label: "Blocked", iconColor: "text-destructive", hoverBg: "hover:bg-destructive/10" },
|
||||
cancelled: { label: "Cancelled", iconColor: "text-muted-foreground", hoverBg: "hover:bg-accent" },
|
||||
|
||||
@@ -74,14 +74,14 @@ vi.mock("@multica/core/api", () => ({
|
||||
|
||||
// Mock issue config
|
||||
vi.mock("@multica/core/issues/config", () => ({
|
||||
ALL_STATUSES: ["backlog", "todo", "in_progress", "in_review", "done", "blocked", "cancelled"],
|
||||
BOARD_STATUSES: ["backlog", "todo", "in_progress", "in_review", "done", "blocked"],
|
||||
STATUS_ORDER: ["backlog", "todo", "in_progress", "in_review", "done", "blocked", "cancelled"],
|
||||
ALL_STATUSES: ["backlog", "todo", "in_progress", "needs_review", "done", "blocked", "cancelled"],
|
||||
BOARD_STATUSES: ["backlog", "todo", "in_progress", "needs_review", "done", "blocked"],
|
||||
STATUS_ORDER: ["backlog", "todo", "in_progress", "needs_review", "done", "blocked", "cancelled"],
|
||||
STATUS_CONFIG: {
|
||||
backlog: { label: "Backlog", iconColor: "text-muted-foreground", hoverBg: "hover:bg-accent" },
|
||||
todo: { label: "Todo", iconColor: "text-muted-foreground", hoverBg: "hover:bg-accent" },
|
||||
in_progress: { label: "In Progress", iconColor: "text-warning", hoverBg: "hover:bg-warning/10" },
|
||||
in_review: { label: "In Review", iconColor: "text-success", hoverBg: "hover:bg-success/10" },
|
||||
needs_review: { label: "Needs Review", iconColor: "text-success", hoverBg: "hover:bg-success/10" },
|
||||
done: { label: "Done", iconColor: "text-info", hoverBg: "hover:bg-info/10" },
|
||||
blocked: { label: "Blocked", iconColor: "text-destructive", hoverBg: "hover:bg-destructive/10" },
|
||||
cancelled: { label: "Cancelled", iconColor: "text-muted-foreground", hoverBg: "hover:bg-accent" },
|
||||
|
||||
@@ -92,7 +92,7 @@ function InProgressIcon() {
|
||||
return <ProgressCircle progress={0.5} />;
|
||||
}
|
||||
|
||||
function InReviewIcon() {
|
||||
function NeedsReviewIcon() {
|
||||
return <ProgressCircle progress={0.75} />;
|
||||
}
|
||||
|
||||
@@ -147,7 +147,7 @@ const STATUS_RENDERERS: Record<IssueStatus, () => React.ReactNode> = {
|
||||
backlog: BacklogIcon,
|
||||
todo: TodoIcon,
|
||||
in_progress: InProgressIcon,
|
||||
in_review: InReviewIcon,
|
||||
needs_review: NeedsReviewIcon,
|
||||
done: DoneIcon,
|
||||
blocked: BlockedIcon,
|
||||
cancelled: CancelledIcon,
|
||||
|
||||
@@ -240,7 +240,7 @@ function WelcomeIllustration() {
|
||||
actor={{ kind: "agent", name: "Review Agent", provider: "openclaw" }}
|
||||
issueId="MCA-42"
|
||||
content="Reviewed Monday's draft — left 4 notes on tone. Standing by for the new one."
|
||||
status="in_review"
|
||||
status="needs_review"
|
||||
/>
|
||||
<MockActivityCard
|
||||
className="translate-x-6 rotate-[1deg]"
|
||||
@@ -286,7 +286,7 @@ function MockActivityCard({
|
||||
actor: ActivityActor;
|
||||
issueId: string;
|
||||
content: React.ReactNode;
|
||||
status?: Extract<IssueStatus, "in_progress" | "done" | "in_review">;
|
||||
status?: Extract<IssueStatus, "in_progress" | "done" | "needs_review">;
|
||||
timestamp?: string;
|
||||
className?: string;
|
||||
}) {
|
||||
|
||||
@@ -179,7 +179,7 @@ var issueSearchCmd = &cobra.Command{
|
||||
}
|
||||
|
||||
var validIssueStatuses = []string{
|
||||
"backlog", "todo", "in_progress", "in_review", "done", "blocked", "cancelled",
|
||||
"backlog", "todo", "in_progress", "needs_review", "done", "blocked", "cancelled",
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
||||
@@ -507,7 +507,7 @@ func TestValidIssueStatuses(t *testing.T) {
|
||||
"backlog": true,
|
||||
"todo": true,
|
||||
"in_progress": true,
|
||||
"in_review": true,
|
||||
"needs_review": true,
|
||||
"done": true,
|
||||
"blocked": true,
|
||||
"cancelled": true,
|
||||
|
||||
@@ -31,7 +31,7 @@ func registerAutopilotListeners(bus *events.Bus, svc *service.AutopilotService)
|
||||
return
|
||||
}
|
||||
// Only handle statuses that finalize an autopilot run.
|
||||
if issue.Status != "done" && issue.Status != "in_review" && issue.Status != "cancelled" && issue.Status != "blocked" {
|
||||
if issue.Status != "done" && issue.Status != "needs_review" && issue.Status != "cancelled" && issue.Status != "blocked" {
|
||||
return
|
||||
}
|
||||
// Load the full issue from DB to check origin_type.
|
||||
|
||||
@@ -25,7 +25,7 @@ var statusLabels = map[string]string{
|
||||
"backlog": "Backlog",
|
||||
"todo": "Todo",
|
||||
"in_progress": "In Progress",
|
||||
"in_review": "In Review",
|
||||
"needs_review": "Needs Review",
|
||||
"done": "Done",
|
||||
"blocked": "Blocked",
|
||||
"cancelled": "Cancelled",
|
||||
|
||||
@@ -433,10 +433,10 @@ func TestSweepResetsInProgressIssueToTodo(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestSweepDoesNotResetIssueAlreadyInReview verifies that the sweeper only resets
|
||||
// TestSweepDoesNotResetIssueAlreadyNeedsReview verifies that the sweeper only resets
|
||||
// issues that are truly stuck in in_progress — it must not clobber issues whose
|
||||
// agents already moved them forward (e.g. to in_review) before the task timed out.
|
||||
func TestSweepDoesNotResetIssueAlreadyInReview(t *testing.T) {
|
||||
// agents already moved them forward (e.g. to needs_review) before the task timed out.
|
||||
func TestSweepDoesNotResetIssueAlreadyNeedsReview(t *testing.T) {
|
||||
if testPool == nil {
|
||||
t.Skip("no database connection")
|
||||
}
|
||||
@@ -455,11 +455,11 @@ func TestSweepDoesNotResetIssueAlreadyInReview(t *testing.T) {
|
||||
t.Fatalf("failed to find test agent: %v", err)
|
||||
}
|
||||
|
||||
// Issue already advanced to in_review by the agent before the task timed out.
|
||||
// Issue already advanced to needs_review by the agent before the task timed out.
|
||||
var issueID string
|
||||
err = testPool.QueryRow(ctx, `
|
||||
INSERT INTO issue (workspace_id, title, status, priority, creator_type, creator_id, assignee_type, assignee_id)
|
||||
SELECT $1, 'Already in_review issue', 'in_review', 'none', 'member', m.user_id, 'agent', $2
|
||||
SELECT $1, 'Already needs_review issue', 'needs_review', 'none', 'member', m.user_id, 'agent', $2
|
||||
FROM member m WHERE m.workspace_id = $1 LIMIT 1
|
||||
RETURNING id
|
||||
`, testWorkspaceID, agentID).Scan(&issueID)
|
||||
@@ -494,14 +494,14 @@ func TestSweepDoesNotResetIssueAlreadyInReview(t *testing.T) {
|
||||
|
||||
broadcastFailedTasks(ctx, queries, nil, bus, failedTasks)
|
||||
|
||||
// Issue should remain in_review — the sweeper must not clobber agent progress.
|
||||
// Issue should remain needs_review — the sweeper must not clobber agent progress.
|
||||
var issueStatus string
|
||||
err = testPool.QueryRow(ctx, `SELECT status FROM issue WHERE id = $1`, issueID).Scan(&issueStatus)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to query issue status: %v", err)
|
||||
}
|
||||
if issueStatus != "in_review" {
|
||||
t.Fatalf("expected issue status 'in_review' to be preserved, got '%s'", issueStatus)
|
||||
if issueStatus != "needs_review" {
|
||||
t.Fatalf("expected issue status 'needs_review' to be preserved, got '%s'", issueStatus)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -124,7 +124,7 @@ func buildMetaSkillContent(provider string, ctx TaskContextForEnv) string {
|
||||
b.WriteString("### Write\n")
|
||||
b.WriteString("- `multica issue create --title \"...\" [--description \"...\"] [--priority X] [--status X] [--assignee X] [--parent <issue-id>] [--project <project-id>] [--due-date <RFC3339>] [--attachment <path>]` — Create a new issue. `--attachment` may be repeated to upload multiple files; labels and subscribers are not accepted here, attach them after create with the commands below.\n")
|
||||
b.WriteString("- `multica issue update <id> [--title X] [--description X] [--priority X] [--status X] [--assignee X] [--parent <issue-id>] [--project <project-id>] [--due-date <RFC3339>]` — Update one or more issue fields in a single call. Use `--parent \"\"` to clear the parent.\n")
|
||||
b.WriteString("- `multica issue status <id> <status>` — Shortcut for `issue update --status` when you only need to flip status (todo, in_progress, in_review, done, blocked, backlog, cancelled)\n")
|
||||
b.WriteString("- `multica issue status <id> <status>` — Shortcut for `issue update --status` when you only need to flip status (todo, in_progress, needs_review, done, blocked, backlog, cancelled)\n")
|
||||
b.WriteString("- `multica issue assign <id> --to <name>` — Assign an issue to a member or agent by name (use `--unassign` to remove assignee)\n")
|
||||
b.WriteString("- `multica issue label add <issue-id> <label-id>` — Attach a label to an issue (look up the label id via `multica label list`)\n")
|
||||
b.WriteString("- `multica issue label remove <issue-id> <label-id>` — Detach a label from an issue\n")
|
||||
@@ -261,7 +261,7 @@ func buildMetaSkillContent(provider string, ctx TaskContextForEnv) string {
|
||||
fmt.Fprintf(&b, "3. Run `multica issue status %s in_progress`\n", ctx.IssueID)
|
||||
b.WriteString("4. Follow your Skills and Agent Identity to complete the task (write code, investigate, etc.)\n")
|
||||
fmt.Fprintf(&b, "5. **Post your final results as a comment — this step is mandatory**: `multica issue comment add %s --content \"...\"`. Your results are only visible to the user if posted via this CLI call; text in your terminal or run logs is NOT delivered.\n", ctx.IssueID)
|
||||
fmt.Fprintf(&b, "6. When done, run `multica issue status %s in_review`\n", ctx.IssueID)
|
||||
fmt.Fprintf(&b, "6. When done, run `multica issue status %s needs_review`\n", ctx.IssueID)
|
||||
fmt.Fprintf(&b, "7. If blocked, run `multica issue status %s blocked` and post a comment explaining why\n\n", ctx.IssueID)
|
||||
}
|
||||
|
||||
|
||||
@@ -382,7 +382,7 @@ func buildSearchQuery(phrase string, terms []string, queryNum int, hasNum bool,
|
||||
// Status priority: active issues first
|
||||
statusRank := `CASE i.status
|
||||
WHEN 'in_progress' THEN 0
|
||||
WHEN 'in_review' THEN 1
|
||||
WHEN 'needs_review' THEN 1
|
||||
WHEN 'todo' THEN 2
|
||||
WHEN 'blocked' THEN 3
|
||||
WHEN 'backlog' THEN 4
|
||||
|
||||
@@ -244,7 +244,7 @@ func (s *AutopilotService) SyncRunFromIssue(ctx context.Context, issue db.Issue)
|
||||
wsID := util.UUIDToString(issue.WorkspaceID)
|
||||
|
||||
switch issue.Status {
|
||||
case "done", "in_review":
|
||||
case "done", "needs_review":
|
||||
if _, err := s.Queries.UpdateAutopilotRunCompleted(ctx, db.UpdateAutopilotRunCompletedParams{
|
||||
ID: run.ID,
|
||||
}); err != nil {
|
||||
|
||||
@@ -55,7 +55,7 @@ CREATE TABLE issue (
|
||||
title TEXT NOT NULL,
|
||||
description TEXT,
|
||||
status TEXT NOT NULL DEFAULT 'backlog'
|
||||
CHECK (status IN ('backlog', 'todo', 'in_progress', 'in_review', 'done', 'blocked', 'cancelled')),
|
||||
CHECK (status IN ('backlog', 'todo', 'in_progress', 'needs_review', 'done', 'blocked', 'cancelled')),
|
||||
priority TEXT NOT NULL DEFAULT 'none'
|
||||
CHECK (priority IN ('urgent', 'high', 'medium', 'low', 'none')),
|
||||
assignee_type TEXT CHECK (assignee_type IN ('member', 'agent')),
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
-- Revert: rename issue status 'needs_review' → 'in_review'.
|
||||
-- 1. Drop the constraint so the UPDATE is allowed.
|
||||
ALTER TABLE issue DROP CONSTRAINT IF EXISTS issue_status_check;
|
||||
|
||||
-- 2. Revert existing rows.
|
||||
UPDATE issue SET status = 'in_review' WHERE status = 'needs_review';
|
||||
|
||||
-- 3. Re-add the original constraint.
|
||||
ALTER TABLE issue ADD CONSTRAINT issue_status_check
|
||||
CHECK (status IN ('backlog', 'todo', 'in_progress', 'in_review', 'done', 'blocked', 'cancelled'));
|
||||
@@ -0,0 +1,10 @@
|
||||
-- Rename issue status 'in_review' → 'needs_review'.
|
||||
-- 1. Drop the existing constraint so the UPDATE is allowed.
|
||||
ALTER TABLE issue DROP CONSTRAINT IF EXISTS issue_status_check;
|
||||
|
||||
-- 2. Update existing rows.
|
||||
UPDATE issue SET status = 'needs_review' WHERE status = 'in_review';
|
||||
|
||||
-- 3. Add the new constraint.
|
||||
ALTER TABLE issue ADD CONSTRAINT issue_status_check
|
||||
CHECK (status IN ('backlog', 'todo', 'in_progress', 'needs_review', 'done', 'blocked', 'cancelled'));
|
||||
Reference in New Issue
Block a user