Compare commits

...

1 Commits

Author SHA1 Message Date
Jiang Bohan
55cfaa1e1d fix(daemon/execenv): make posting result comment an explicit workflow step
Agents were silently finishing tasks without ever posting results to the
issue — their final reply stayed in terminal/log output only. See MUL-1124.

Root cause: the injected CLAUDE.md / AGENTS.md put "post a comment with
results" inside the body of step 4 (a nested clause in the default workflow
description), so skill-driven flows jumped straight from "do the work" to
`status in_review`.

- Hoist posting the result comment into its own explicit, numbered step in
  both assignment-triggered and comment-triggered workflows, with the exact
  `multica issue comment add` invocation inlined.
- Add a hard warning at the top of the Output section that terminal / chat
  text is never delivered to the user.
- Add regression test covering both workflow branches.
2026-04-20 16:51:41 +08:00
2 changed files with 72 additions and 11 deletions

View File

@@ -671,6 +671,66 @@ func TestPrepareWithRepoContextOpencode(t *testing.T) {
}
}
// TestInjectRuntimeConfigRequiresExplicitCommentPost ensures the injected
// workflow makes "post a comment with results" an explicit, unmissable step in
// both the assignment- and comment-triggered branches, plus hard-warns in the
// Output section that terminal/log text is not user-visible. Agents were
// silently finishing tasks without ever posting their result to the issue; see
// MUL-1124. Covering this in a test prevents the guidance from decaying back
// into a nested clause again.
func TestInjectRuntimeConfigRequiresExplicitCommentPost(t *testing.T) {
t.Parallel()
assignmentCtx := TaskContextForEnv{IssueID: "issue-1"}
commentCtx := TaskContextForEnv{IssueID: "issue-1", TriggerCommentID: "comment-1"}
for _, tc := range []struct {
name string
ctx TaskContextForEnv
}{
{"assignment-triggered", assignmentCtx},
{"comment-triggered", commentCtx},
} {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
dir := t.TempDir()
if err := InjectRuntimeConfig(dir, "claude", tc.ctx); err != nil {
t.Fatalf("InjectRuntimeConfig failed: %v", err)
}
data, err := os.ReadFile(filepath.Join(dir, "CLAUDE.md"))
if err != nil {
t.Fatalf("read CLAUDE.md: %v", err)
}
s := string(data)
// The workflow must contain an explicit `multica issue comment add`
// invocation for this issue — not just a prose mention of posting.
mustContain := []string{
"multica issue comment add issue-1",
"mandatory",
}
for _, want := range mustContain {
if !strings.Contains(s, want) {
t.Errorf("%s: CLAUDE.md missing %q\n---\n%s", tc.name, want, s)
}
}
// The Output section must carry a hard warning that terminal/log
// output is not user-visible. This is the second line of defense
// in case the agent skips past the workflow steps.
for _, want := range []string{
"Final results MUST be delivered via `multica issue comment add`",
"does NOT see your terminal output",
} {
if !strings.Contains(s, want) {
t.Errorf("%s: Output warning missing %q", tc.name, want)
}
}
})
}
}
func TestInjectRuntimeConfigUnknownProvider(t *testing.T) {
t.Parallel()
dir := t.TempDir()
@@ -1056,11 +1116,11 @@ network_access = true
func TestCodexSandboxPolicyFor(t *testing.T) {
t.Parallel()
cases := []struct {
name string
goos string
version string
wantMode string
wantNet bool
name string
goos string
version string
wantMode string
wantNet bool
}{
{"linux any version", "linux", "0.100.0", "workspace-write", true},
{"linux unknown version", "linux", "", "workspace-write", true},

View File

@@ -129,9 +129,9 @@ func buildMetaSkillContent(provider string, ctx TaskContextForEnv) string {
fmt.Fprintf(&b, "2. Run `multica issue comment list %s --output json` to read the conversation\n", ctx.IssueID)
b.WriteString(" - If the output is very large or truncated, use pagination: `--limit 30` to get the latest 30 comments, or `--since <timestamp>` to fetch only recent ones\n")
fmt.Fprintf(&b, "3. Find the triggering comment (ID: `%s`) and understand what is being asked — do NOT confuse it with previous comments\n", ctx.TriggerCommentID)
b.WriteString("4. ")
b.WriteString("4. If the comment requests code changes or further work, do the work first\n")
b.WriteString("5. **Post your reply as a comment — this step is mandatory.** Text in your terminal or run logs is NOT delivered to the user. ")
b.WriteString(BuildCommentReplyInstructions(ctx.IssueID, ctx.TriggerCommentID))
b.WriteString("5. If the comment requests code changes or further work, do the work first, then reply with your results\n")
b.WriteString("6. Do NOT change the issue status unless the comment explicitly asks for it\n\n")
} else {
// Assignment-triggered: defer to agent Skills for workflow specifics.
@@ -139,10 +139,10 @@ func buildMetaSkillContent(provider string, ctx TaskContextForEnv) string {
fmt.Fprintf(&b, "1. Run `multica issue get %s --output json` to understand your task\n", ctx.IssueID)
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. Follow your Skills and Agent Identity to determine how to complete this task.\n")
b.WriteString(" If no relevant skill applies, the default workflow is: understand the task → do the work → post a comment with results → update issue status.\n")
fmt.Fprintf(&b, "5. When done, run `multica issue status %s in_review`\n", ctx.IssueID)
fmt.Fprintf(&b, "6. If blocked, run `multica issue status %s blocked` and post a comment explaining why\n\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, "7. If blocked, run `multica issue status %s blocked` and post a comment explaining why\n\n", ctx.IssueID)
}
if len(ctx.AgentSkills) > 0 {
@@ -190,6 +190,7 @@ func buildMetaSkillContent(provider string, ctx TaskContextForEnv) string {
b.WriteString("do NOT attempt to work around it. Instead, post a comment mentioning the workspace owner to request the missing functionality.\n\n")
b.WriteString("## Output\n\n")
b.WriteString("⚠️ **Final results MUST be delivered via `multica issue comment add`.** The user does NOT see your terminal output, assistant chat text, or run logs — only comments on the issue. A task that finishes without a result comment is invisible to the user, even if the work itself was correct.\n\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")