Compare commits

...

2 Commits

Author SHA1 Message Date
Jiang Bohan
463cdad98b refactor(execenv): drop duplicated quick-create field rules from CLAUDE.md/AGENTS.md
The quick-create field rules (title / description / priority / assignee
fallback / project / status) lived in two places — the per-turn user
message built by BuildPrompt, and the workflow block injected into
CLAUDE.md / AGENTS.md by buildMetaSkillContent. Same content, two
sources, easy to update one and forget the other (the assignee-default
change in this PR had to touch both).

Quick-create is one-shot, so the per-turn user message is always
present and is the natural single source of truth. The injected
file's quick-create section now keeps only the hard guardrails:
"do exactly one issue create, no issue get / status / comment add,
exit on CLI error". Those guardrails stay in BOTH surfaces because
they're the safety net for providers that don't propagate the user
message into resumed-session context.

renderQuickCreateContext (issue_context.md) was already
guardrails-only — no change needed there.
2026-04-29 15:57:26 +08:00
Jiang Bohan
3a88b36655 feat(quick-create): default assignee to picker agent when user didn't name one
The quick-create prompt previously told the agent to OMIT --assignee
when the user's input didn't name a person. That left almost every
quick-created issue unassigned, which doesn't match user intent — the
user opened quick-create with a specific agent picked, so that agent
is the obvious owner.

Both prompt surfaces (BuildPrompt for the dispatched message, plus
the workflow block in injected CLAUDE.md / AGENTS.md) now instruct
the agent: if the input doesn't name an assignee, pass
`--assignee "<your name>"`. The picker agent's name is interpolated
into the prompt at task-build time so the agent has a literal value
to use rather than guessing its own name. The "explicitly named
assignee → resolve via members" branch is unchanged.
2026-04-29 15:49:52 +08:00
2 changed files with 22 additions and 19 deletions

View File

@@ -151,24 +151,17 @@ func buildMetaSkillContent(provider string, ctx TaskContextForEnv) string {
b.WriteString("- If the task requires code changes, use `multica repo checkout <url>` to get the code first\n")
b.WriteString("- Keep responses concise and direct\n\n")
} else if ctx.QuickCreatePrompt != "" {
// Quick-create task: no issue exists yet. The agent's only job is to
// translate one line of natural language into a single
// `multica issue create` call. Suppress the default assignment
// workflow that would tell the agent to call `multica issue get` /
// `multica issue status` / `multica issue comment add` against an
// empty IssueID — those would either error or silently target the
// wrong issue.
b.WriteString("**This task was triggered by quick-create.** There is NO existing Multica issue. Translate the user's input into a single `multica issue create` invocation and exit.\n\n")
fmt.Fprintf(&b, "User input:\n> %s\n\n", ctx.QuickCreatePrompt)
b.WriteString("Field rules:\n")
b.WriteString("- title: required, short imperative summary extracted from the user input.\n")
b.WriteString("- description: optional; only include if the user supplied detail beyond the title.\n")
b.WriteString("- priority: one of `urgent`, `high`, `medium`, `low`, or omit. Map P0/P1 → urgent/high; \"asap\"/\"紧急\" → urgent; \"低优先级\" → low.\n")
b.WriteString("- assignee: when the user says \"分给 X\" / \"assign to X\" / \"@X\", call `multica workspace members --output json` and find the matching member. On clean match, pass `--assignee <name>`. On no/ambiguous match, OMIT `--assignee` and append a final line to the description: `未识别 assignee: X`.\n")
b.WriteString("- project / status: omit (defaults apply).\n\n")
b.WriteString("Output rules:\n")
b.WriteString("- Run exactly one `multica issue create` invocation.\n")
b.WriteString("- After it succeeds, print exactly one line: `Created MUL-<n>: <title>` and exit.\n")
// Quick-create task: detailed field / output rules live in the
// per-turn prompt (BuildPrompt → buildQuickCreatePrompt) so they
// have a single source of truth. Quick-create is one-shot, so the
// per-turn message is always present and the agent reads the rules
// from there. We only keep the hard guardrails here so a provider
// that doesn't propagate the user message into its working context
// (or a resumed session) still avoids the assignment-task workflow
// pointing at an empty issue id.
b.WriteString("**This task was triggered by quick-create.** There is NO existing Multica issue. Follow the field and output rules in the user message you just received; ignore the default assignment-task workflow.\n\n")
b.WriteString("Hard guardrails (apply even if the user message is missing):\n")
b.WriteString("- Run exactly one `multica issue create` invocation, then exit.\n")
b.WriteString("- Do NOT call `multica issue get`, `multica issue status`, or `multica issue comment add` for this task — there is no issue to query, transition, or comment on. The platform writes the user's success/failure inbox notification automatically based on whether `multica issue create` succeeded.\n")
b.WriteString("- If the CLI returns an error, exit with that error as the only output. Do not retry.\n\n")
} else if ctx.AutopilotRunID != "" {

View File

@@ -45,7 +45,17 @@ func buildQuickCreatePrompt(task Task) string {
b.WriteString("- title: required. A short, imperative summary extracted from the user input (e.g. \"fix inbox loading\"). Strip filler words.\n")
b.WriteString("- description: optional. Include only if the user supplied detail beyond the title; otherwise omit. Never echo the title here.\n")
b.WriteString("- priority: one of `urgent`, `high`, `medium`, `low`, or omit. Map P0/P1 → urgent/high; \"asap\"/\"紧急\" → urgent; \"低优先级\" → low. If unspecified, omit.\n")
b.WriteString("- assignee: when the user says \"分给 X\" / \"assign to X\" / \"@X\", call `multica workspace members --output json` and find the matching member by display name (case-insensitive substring match is fine). On a clean match, pass `--assignee <name>`. On no match or ambiguous match, do NOT pass `--assignee` — instead append a final line to the description: `未识别 assignee: X`.\n")
b.WriteString("- assignee:\n")
b.WriteString(" - When the user names someone (\"分给 X\" / \"assign to X\" / \"@X\"), call `multica workspace members --output json` and find the matching member by display name (case-insensitive substring match is fine). On a clean match, pass `--assignee <name>`. On no match or ambiguous match, do NOT pass `--assignee` — instead append a final line to the description: `未识别 assignee: X`.\n")
agentName := ""
if task.Agent != nil {
agentName = task.Agent.Name
}
if agentName != "" {
fmt.Fprintf(&b, " - When the user did NOT name an assignee, default to YOURSELF: pass `--assignee %q`. The picker agent is the expected owner because the user opened quick-create with you selected — never leave the issue unassigned.\n", agentName)
} else {
b.WriteString(" - When the user did NOT name an assignee, default to YOURSELF (the picker agent): pass `--assignee <your agent name>`. Never leave the issue unassigned.\n")
}
b.WriteString("- project: omit. The platform will route the issue to the workspace default.\n")
b.WriteString("- status: omit (defaults to `todo`).\n\n")
b.WriteString("Output format:\n")