refactor: decouple task lifecycle from issue status (#151)

* refactor: decouple task lifecycle from issue status, add daemon health server

- Remove automatic issue status changes from StartTask (in_progress),
  CompleteTask (in_review), and FailTask (blocked) in task service.
  Issue status is now fully managed by the agent via `multica issue status`.
- Update agent prompt and meta skill to instruct agents to manage issue
  status themselves (in_progress → done/in_review/blocked).
- Add daemon health HTTP server on 127.0.0.1:19514 with /health endpoint
  exposing pid, uptime, agents, and workspaces. Fail fast if port is taken
  (another daemon already running).
- Update `multica status` to check both server and daemon health.
- Add Save button to repos section in workspace settings UI.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor(daemon): simplify prompt, fix runtime config path, improve task error logging

- Slim down BuildPrompt to a minimal hint; detailed workflow now lives in CLAUDE.md/AGENTS.md
- Write CLAUDE.md to workDir root instead of .claude/CLAUDE.md
- Fix git-exclude pattern (.claude → CLAUDE.md)
- Decouple task queue reconciliation from issue status changes (agents manage status via CLI)
- Add diagnostic logging when CompleteTask/FailTask fail due to unexpected task state

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(task): use task_completed/task_failed inbox notification types

FailTask was sending "agent_blocked" which conflates agent crash with
issue-level blocked status. Align notification types with the new
decoupled model: task_completed and task_failed. Update frontend types
and labels accordingly.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
LinYushen 2026-03-27 18:30:21 +08:00 committed by GitHub
parent 8bd476f47c
commit 6d2a0b45d2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 269 additions and 82 deletions

View file

@ -10,18 +10,14 @@ import (
// InjectRuntimeConfig writes the meta skill content into the runtime-specific
// config file so the agent discovers .agent_context/ through its native mechanism.
//
// For Claude: writes {workDir}/.claude/CLAUDE.md
// For Claude: writes {workDir}/CLAUDE.md
// For Codex: writes {workDir}/AGENTS.md
func InjectRuntimeConfig(workDir, provider string, ctx TaskContextForEnv) error {
content := buildMetaSkillContent(ctx)
switch provider {
case "claude":
dir := filepath.Join(workDir, ".claude")
if err := os.MkdirAll(dir, 0o755); err != nil {
return fmt.Errorf("create .claude dir: %w", err)
}
return os.WriteFile(filepath.Join(dir, "CLAUDE.md"), []byte(content), 0o644)
return os.WriteFile(filepath.Join(workDir, "CLAUDE.md"), []byte(content), 0o644)
case "codex":
return os.WriteFile(filepath.Join(workDir, "AGENTS.md"), []byte(content), 0o644)
default:
@ -52,10 +48,19 @@ func buildMetaSkillContent(ctx TaskContextForEnv) string {
b.WriteString("- `multica issue update <id> [--title X] [--description X] [--priority X]` — Update issue fields\n\n")
b.WriteString("### Workflow\n")
b.WriteString("You are responsible for managing the issue status throughout your work.\n\n")
fmt.Fprintf(&b, "1. Run `multica issue get %s --output json` to understand your task\n", ctx.IssueID)
b.WriteString("2. Read comments for additional context or human instructions\n")
b.WriteString("3. Complete the work in the local codebase\n")
b.WriteString("4. Post a comment summarizing what you did\n\n")
fmt.Fprintf(&b, "2. Run `multica issue status %s in_progress`\n", ctx.IssueID)
b.WriteString("3. Read comments for additional context or human instructions\n")
b.WriteString("4. If the task requires code changes:\n")
b.WriteString(" a. Create a new branch\n")
b.WriteString(" b. Implement the changes and commit\n")
b.WriteString(" c. Push the branch to the remote\n")
b.WriteString(" d. Create a pull request (decide the target branch based on the repo's conventions)\n")
fmt.Fprintf(&b, " e. Post the PR link as a comment: `multica issue comment add %s --content \"PR: <url>\"`\n", ctx.IssueID)
b.WriteString("5. If the task does not require code (e.g. research, documentation), post your findings as a comment\n")
fmt.Fprintf(&b, "6. Run `multica issue status %s in_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)
if len(ctx.AgentSkills) > 0 {
b.WriteString("## Skills\n\n")
@ -72,10 +77,9 @@ func buildMetaSkillContent(ctx TaskContextForEnv) string {
}
b.WriteString("## Output\n\n")
b.WriteString("When done, return a concise Markdown summary of your work.\n")
b.WriteString("- Lead with the outcome.\n")
b.WriteString("- Mention concrete files or commands if you changed anything.\n")
b.WriteString("- If blocked, explain the blocker clearly.\n")
b.WriteString("Keep comments concise and natural — state the outcome, not the process.\n")
b.WriteString("Good: \"Fixed the login redirect. PR: https://...\"\n")
b.WriteString("Bad: \"1. Read the issue 2. Found the bug in auth.go 3. Created branch 4. ...\"\n")
return b.String()
}