5 Commits

Author SHA1 Message Date
Bohan Jiang
e2ce3a2da8 MUL-5223 fix(runtime): forbid blocking on external CI in the runtime brief (#5840)
* fix(runtime): forbid blocking on external CI in the brief (MUL-5223)

The external-work boundary added in #5803 did not stop agents from
waiting on GitHub Actions. Two holes: the section's only concrete
"how to wait" example was a blocking foreground call, which is exactly
the shape of `gh pr checks --watch`; and the "unless acceptance
criteria require it" escape was satisfied by the repo's own merge
requirement that CI be green.

Name the banned tool shapes, allow a single non-blocking status
snapshot, deny branch protection as an acceptance criterion, and give
the replacement hand-off phrasing (local test result + PR link).

Co-authored-by: multica-agent <github@multica.ai>

* fix(runtime): scope the CI-wait ban so the explicit exception stays executable (MUL-5223)

Review feedback on #5840:

- The ban read as absolute ("Blocking on external CI is never part of
  your deliverable") while the next bullet allowed waiting when the
  task explicitly asks for the CI result, leaving no way to satisfy
  both. The ban is now scoped to "unless the explicit exception below
  applies", and the exception names the one executable shape: a single
  foreground blocking watch inside the same turn.
- `gh pr merge --auto` enables auto-merge and returns; it is not a
  wait. Only waiting for it to land is banned.

Both hard-pin tests now also pin the exception so it cannot be dropped
or re-absolutised.

Co-authored-by: multica-agent <github@multica.ai>

* polish(runtime): group Background Task Safety into run-owned and external-CI clusters (MUL-5223)

Co-authored-by: multica-agent <github@multica.ai>

* polish(runtime): cut redundant phrasing from the external-CI cluster (MUL-5223)

The cluster said "report and finish" three different ways and carried
two rhetorical tails. Fold the delivery template into the post-push
playbook bullet, tighten the merge-gate denial, and drop filler.
5 bullets -> 4, -36 words, every behavioral fact and test pin intact.

Co-authored-by: multica-agent <github@multica.ai>

---------

Co-authored-by: Bohan-J <bohan@devv.ai>
Co-authored-by: multica-agent <github@multica.ai>
2026-07-23 18:38:51 +08:00
Jiayuan Zhang
2c749ddf9f fix(runtime): clarify external background work (#5803) 2026-07-23 02:43:17 +08:00
Multica Eve
0c2e48ded2 refactor: retire FF_RUNTIME_BRIEF_SLIM, make slim runtime brief the only path (MUL-4297)
The runtime_brief_slim feature flag has burned in; the slim runtime brief is now the sole path.

- execenv: buildMetaSkillContent / BuildCommentReplyInstructions delegate to the slim assembler unconditionally; delete the legacy verbose brief body and writeBackgroundTaskSafetyInstructions.
- Remove the runtime_brief_slim flag and the daemon-bound flag delivery subsystem built solely for it: execenv flag wiring (runtime_config_flag.go, server_snapshot_provider.go), the featureflagdispatch package, the DaemonFeatureFlagSnapshot heartbeat protocol field, and the server/daemon wiring in router.go, handler, daemon.go, main.go, cmd_daemon.go.
- Keep the generic server/pkg/featureflag engine (still used by composio_mcp_apps).
- Update tests to slim-only expectations and docs/feature-flags.md.

Co-authored-by: Eve <eve@multica-ai.local>
Co-authored-by: multica-agent <github@multica.ai>
2026-07-09 13:48:33 +08:00
Bohan Jiang
4c510dfef6 fix(daemon): harden background-task-safety brief against background-and-yield (MUL-4140) (#4998)
A Multica-managed run goes terminal the moment the top-level turn exits;
there is no "background work finishes later and wakes you up" step. When an
agent starts background work (a run_in_background shell, a Monitor, an async
subagent) and ends its turn to "wait for a completion notification", the work
is orphaned and the result comment it meant to post is never sent (MUL-4091 /
PR #4970).

The existing claude-only protocol guard forces run_in_background tool inputs
to foreground and fails loud on async_launched tool results, but it cannot
catch the actual MUL-4091 mechanism: a turn that ends cleanly with a
"Standing by, I'll report when CI finishes" message. That shape is only
addressable behaviorally, and it is harness-agnostic.

Harden the Background Task Safety brief (both the legacy/verbose production
path and the slim staging path) with explicit hard pins:
- never background-and-yield / expect a future wakeup that does not exist here;
- do every wait synchronously in a single foreground call (e.g. gh run watch);
- the standalone-harness "running in the background, keep working" hint does
  not apply in Multica-managed runs;
- never end a turn with a "standing by" / "I'll report back" sign-off.

Add verbose- and slim-path test coverage for the new pins so a future brief
trim cannot silently drop them.

Co-authored-by: J <j@multica.ai>
Co-authored-by: multica-agent <github@multica.ai>
2026-07-06 19:47:00 +08:00
Multica Eve
8ad673fdb7 MUL-3560: gate slim runtime brief behind runtime_brief_slim feature flag (#4449)
The MUL-3560 slim runtime brief — kind-driven dispatcher, per-section
gating, prose compression for ~7k chars saved on the typical
comment-triggered task — now ships behind the `runtime_brief_slim`
feature flag wired via the framework-level service from MUL-3615.

Default: OFF in every environment (production stays on the legacy
brief that has shipped for ~2 years). Staging opts in via the YAML
rule set; ops can override per-process with `FF_RUNTIME_BRIEF_SLIM=true`.
Production is held back until staging has burned in long enough that
we are confident the slim brief does not regress agent behaviour.

Architecture (one toggle point, two code paths, both fully tested):

  buildMetaSkillContent (runtime_config.go)
      │
      └─ useSlimBrief() → false (default)
      │     → fall through to the legacy verbose body that ships on
      │       main today — byte-for-byte unchanged, no migration risk
      │
      └─ useSlimBrief() → true
            → buildMetaSkillContentSlim (runtime_config_sections.go)
              → classifyTask → 5-way kind switch → per-section writers

BuildCommentReplyInstructions takes the same gate, so the per-turn
comment prompt and the runtime brief stay in sync on which template
they emit.

What's in this PR:

- runtime_config_flag.go (new): package-scope `runtimeFlags` atomic
  pointer + `SetFeatureFlags` setter + `useSlimBrief` toggle point.
  Nil-safe: a daemon that forgets to wire the service falls back to
  legacy, no panic.
- runtime_config_kind.go (new): `taskKind` enum + `classifyTask` +
  `hasIssueContext` predicate. Used only by the slim path.
- runtime_config_sections.go (new): the slim brief itself —
  `buildMetaSkillContentSlim` + per-section `writeXxx` helpers
  + `writeAvailableCommandsQuickCreate` minimal variant +
  `writeBackgroundTaskSafetySlim` compressed safety section. The
  Section × Kind matrix is documented inline on
  `buildMetaSkillContentSlim` and the test below checks the
  dispatcher does not diverge from the spec.
- reply_instructions.go: `BuildCommentReplyInstructions` gains a
  short slim-or-legacy prelude; new `buildCommentReplyInstructionsSlim`
  is the compressed cookbook (defers the shell-hazard rationale to
  `## Comment Formatting`).
- runtime_config.go: `buildMetaSkillContent` gains a 2-line
  dispatcher at the top; the legacy body is otherwise untouched.
- runtime_config_kind_test.go (new): canaries for both paths.
  - TestClassifyTask: 5 kinds + 3 tiebreak cases.
  - TestTaskKindHasIssueContext: predicate semantics.
  - TestSlimFlagOffUsesLegacy: nil flag service → legacy path
    (renders "Get full issue details.", a legacy-only substring).
  - TestSlimFlagOnUsesSlim: flag on → slim path (renders "full
    issue.", a slim-only one-liner) AND must NOT render legacy
    "Get full issue details.".
  - TestBuildMetaSkillContentSlimKindMatrix: locks the per-kind
    section set; heading match is line-anchored so inline references
    don't trip absence assertions.
  - TestSlimQuickCreateAvailableCommands: locks the minimal-variant
    content for quick-create (issue create present, every other
    Core command absent).
  - TestSlimBriefIsSubstantiallyShorter: ≥ 30% reduction guard so
    a future change can't accidentally re-bloat the slim path back
    to legacy levels.
- cmd/server/main.go: now calls `execenv.SetFeatureFlags(flags)`
  immediately after constructing the feature flag service.

Measured impact (slim vs legacy, claude provider, realistic fixture
with 2 repos + 2 skills + member initiator):

    legacy = 19567 chars
    slim   = 11868 chars    Δ = -7699  (-39.3%)

Verification:

- go vet ./internal/daemon/... ./cmd/server/...                  ok
- go test ./internal/daemon/...                                  ok
- go test ./pkg/featureflag/...                                  ok
- TestSlimBriefIsSubstantiallyShorter logs the 39.3% ratio
- TestSlimFlagOffUsesLegacy + TestSlimFlagOnUsesSlim pass both
  directions, so the dispatcher is locked in code.

The pre-existing `internal/handler` test failures
(TestLeaveWorkspace_RevokesOwnRuntimes,
TestDeleteMember_CancelsTasksFromAgentReassignment,
TestDeleteMember_NoRuntimes_DeletesMember) reproduce on plain
`origin/main` with the same `relation "channel_user_binding" does
not exist` SQL error — they are a missing-migration bug from the
recent channels foundation PR (ce28d0aa0), not anything this PR
touched.

Rollout plan:

1. Merge this PR. Production daemons keep emitting the legacy brief
   (flag default false).
2. Add a YAML rule to staging's
   `MULTICA_FEATURE_FLAGS_FILE`:

       runtime_brief_slim:
         default: true

   Staging daemons start emitting the slim brief on next restart.
3. Watch `agent prompt prepared` logs + agent behaviour for 7 days.
4. If staging is clean, flip the prod YAML to `default: true`.
   Legacy code path stays in the binary as a kill-switch
   (`FF_RUNTIME_BRIEF_SLIM=false` to revert without a deploy).
5. After ~30 days clean in prod, follow up with a PR that deletes
   the legacy body and the flag — same pattern as docs/feature-flags.md
   recommends ("plan the death of the flag at birth").

Co-authored-by: Eve <eve@multica-ai.local>
Co-authored-by: multica-agent <github@multica.ai>
2026-06-24 14:23:17 +08:00