opencode-swarm 7.86.0 → 7.87.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/dist/cli/{capability-probe-jevmgwmf.js → capability-probe-wsjzcp48.js} +2 -2
  2. package/dist/cli/{config-doctor-zejarrr6.js → config-doctor-6h64pn8n.js} +4 -4
  3. package/dist/cli/{dispatch-k86d928w.js → dispatch-kb69qw40.js} +3 -3
  4. package/dist/cli/{evidence-summary-service-g2znnd33.js → evidence-summary-service-gg5m9z57.js} +4 -4
  5. package/dist/cli/{guardrail-explain-rtd1x26f.js → guardrail-explain-wb1cj312.js} +13 -13
  6. package/dist/cli/{guardrail-log-80116wmz.js → guardrail-log-eegabqcp.js} +5 -5
  7. package/dist/cli/{index-jwz50183.js → index-0m44n5qv.js} +14 -14
  8. package/dist/cli/{index-0sxvwjt0.js → index-1cb4wxnm.js} +2 -2
  9. package/dist/cli/{index-zfsbaaqh.js → index-5e4e2hvv.js} +1 -1
  10. package/dist/cli/{index-vq2321gg.js → index-5hvbw5xh.js} +2 -2
  11. package/dist/cli/{index-5cb86007.js → index-5vpe6vq9.js} +1 -1
  12. package/dist/cli/{index-red8fm8p.js → index-89xjr3h4.js} +1162 -214
  13. package/dist/cli/{index-f8r50m3h.js → index-adz3nk9b.js} +2 -2
  14. package/dist/cli/{index-7r2b453y.js → index-f13d3b69.js} +2 -2
  15. package/dist/cli/{index-ckntc5gf.js → index-gn8n22th.js} +2 -2
  16. package/dist/cli/{index-hw9b2xng.js → index-q9h0wb04.js} +36 -3
  17. package/dist/cli/{index-d9fbxaqd.js → index-s8bj492g.js} +1 -1
  18. package/dist/cli/{index-hz59hg4h.js → index-v4fcn4tr.js} +1 -1
  19. package/dist/cli/{index-eb85wtx9.js → index-vqyfscxd.js} +2 -2
  20. package/dist/cli/{index-5q66xc88.js → index-wv2yj8ka.js} +2598 -1406
  21. package/dist/cli/{index-yx44zd0p.js → index-zgwm4ryv.js} +9 -1
  22. package/dist/cli/index.js +12 -12
  23. package/dist/cli/{pending-delegations-rd40tv9s.js → pending-delegations-35fvcj7z.js} +3 -3
  24. package/dist/cli/{pr-subscriptions-y1nn36e5.js → pr-subscriptions-b18n1yd8.js} +4 -4
  25. package/dist/cli/{schema-8d32b2v6.js → schema-84146tvk.js} +3 -1
  26. package/dist/cli/{skill-generator-a5ehggyg.js → skill-generator-3pvpk4y2.js} +2 -2
  27. package/dist/commands/coupling.d.ts +36 -0
  28. package/dist/commands/epic.d.ts +52 -0
  29. package/dist/commands/registry.d.ts +18 -2
  30. package/dist/config/constants.d.ts +1 -0
  31. package/dist/config/schema.d.ts +145 -0
  32. package/dist/git/branch.d.ts +22 -1
  33. package/dist/hooks/delegation-gate/worktree-merge-status.d.ts +86 -0
  34. package/dist/index.js +8401 -5792
  35. package/dist/memory/schema.d.ts +3 -3
  36. package/dist/plan/manager.d.ts +10 -0
  37. package/dist/state.d.ts +16 -0
  38. package/dist/tools/epic-plan-waves.d.ts +79 -0
  39. package/dist/tools/epic-record-divergence.d.ts +73 -0
  40. package/dist/tools/epic-run-phase.d.ts +179 -0
  41. package/dist/tools/index.d.ts +3 -0
  42. package/dist/tools/manifest.d.ts +3 -0
  43. package/dist/tools/tool-metadata.d.ts +12 -0
  44. package/dist/turbo/epic/activation.d.ts +193 -0
  45. package/dist/turbo/epic/calibration-engine.d.ts +88 -0
  46. package/dist/turbo/epic/calibration.d.ts +65 -0
  47. package/dist/turbo/epic/cochange-conflict.d.ts +79 -0
  48. package/dist/turbo/epic/cochange-source.d.ts +80 -0
  49. package/dist/turbo/epic/coupling-report.d.ts +85 -0
  50. package/dist/turbo/epic/divergence-recorder.d.ts +112 -0
  51. package/dist/turbo/epic/index.d.ts +24 -0
  52. package/dist/turbo/epic/promotion-evidence.d.ts +42 -0
  53. package/dist/turbo/epic/state.d.ts +85 -0
  54. package/dist/turbo/epic/task-commit.d.ts +110 -0
  55. package/dist/turbo/epic/upstream-commits.d.ts +82 -0
  56. package/dist/turbo/epic/wave-planner.d.ts +83 -0
  57. package/dist/turbo/lean/partition-common.d.ts +85 -0
  58. package/dist/turbo/lean/planner.d.ts +12 -20
  59. package/dist/utils/index.d.ts +1 -1
  60. package/dist/utils/logger.d.ts +19 -0
  61. package/package.json +1 -1
@@ -27,6 +27,14 @@ function warn(message, data) {
27
27
  console.warn(`[opencode-swarm ${timestamp}] WARN: ${message}`);
28
28
  }
29
29
  }
30
+ function criticalWarn(message, data) {
31
+ const timestamp = new Date().toISOString();
32
+ if (data !== undefined) {
33
+ console.warn(`[opencode-swarm ${timestamp}] CRITICAL-WARN: ${message}`, data);
34
+ } else {
35
+ console.warn(`[opencode-swarm ${timestamp}] CRITICAL-WARN: ${message}`);
36
+ }
37
+ }
30
38
  function error(message, data) {
31
39
  const timestamp = new Date().toISOString();
32
40
  if (data !== undefined) {
@@ -37,4 +45,4 @@ function error(message, data) {
37
45
  }
38
46
  var init_logger = () => {};
39
47
 
40
- export { log, warn, error, init_logger };
48
+ export { log, warn, criticalWarn, error, init_logger };
package/dist/cli/index.js CHANGED
@@ -7,26 +7,26 @@ import {
7
7
  getPluginLockFilePaths,
8
8
  package_default,
9
9
  resolveCommand
10
- } from "./index-5q66xc88.js";
11
- import"./index-vq2321gg.js";
10
+ } from "./index-wv2yj8ka.js";
11
+ import"./index-5hvbw5xh.js";
12
12
  import"./index-yhsmmv2z.js";
13
- import"./index-d9fbxaqd.js";
13
+ import"./index-s8bj492g.js";
14
14
  import"./index-e8pk68cc.js";
15
- import"./index-0sxvwjt0.js";
15
+ import"./index-1cb4wxnm.js";
16
16
  import {
17
17
  DEFAULT_AGENT_CONFIGS
18
- } from "./index-hw9b2xng.js";
19
- import"./index-red8fm8p.js";
20
- import"./index-f8r50m3h.js";
21
- import"./index-hz59hg4h.js";
18
+ } from "./index-q9h0wb04.js";
19
+ import"./index-89xjr3h4.js";
20
+ import"./index-adz3nk9b.js";
21
+ import"./index-v4fcn4tr.js";
22
22
  import"./index-e7h9bb6v.js";
23
23
  import"./index-fjwwrwr5.js";
24
- import"./index-eb85wtx9.js";
25
- import"./index-ckntc5gf.js";
24
+ import"./index-vqyfscxd.js";
25
+ import"./index-gn8n22th.js";
26
26
  import"./index-jtqkh8jf.js";
27
- import"./index-zfsbaaqh.js";
27
+ import"./index-5e4e2hvv.js";
28
28
  import"./index-p0arc26j.js";
29
- import"./index-yx44zd0p.js";
29
+ import"./index-zgwm4ryv.js";
30
30
  import"./index-bcp79s17.js";
31
31
  import"./index-293f68mj.js";
32
32
  import"./index-b9v501fr.js";
@@ -1,14 +1,14 @@
1
1
  // @bun
2
2
  import {
3
3
  validateSwarmPath
4
- } from "./index-ckntc5gf.js";
4
+ } from "./index-gn8n22th.js";
5
5
  import"./index-jtqkh8jf.js";
6
- import"./index-zfsbaaqh.js";
6
+ import"./index-5e4e2hvv.js";
7
7
  import"./index-p0arc26j.js";
8
8
  import {
9
9
  init_logger,
10
10
  warn
11
- } from "./index-yx44zd0p.js";
11
+ } from "./index-zgwm4ryv.js";
12
12
  import {
13
13
  withEvidenceLock
14
14
  } from "./index-bcp79s17.js";
@@ -9,12 +9,12 @@ import {
9
9
  sweepStale,
10
10
  unsubscribe,
11
11
  updateSnapshot
12
- } from "./index-eb85wtx9.js";
13
- import"./index-ckntc5gf.js";
12
+ } from "./index-vqyfscxd.js";
13
+ import"./index-gn8n22th.js";
14
14
  import"./index-jtqkh8jf.js";
15
- import"./index-zfsbaaqh.js";
15
+ import"./index-5e4e2hvv.js";
16
16
  import"./index-p0arc26j.js";
17
- import"./index-yx44zd0p.js";
17
+ import"./index-zgwm4ryv.js";
18
18
  import"./index-bcp79s17.js";
19
19
  import"./index-293f68mj.js";
20
20
  import"./index-b9v501fr.js";
@@ -26,6 +26,7 @@ import {
26
26
  DesignDocsConfigSchema,
27
27
  DiscoverySourceSchema,
28
28
  DocsConfigSchema,
29
+ EpicConfigSchema,
29
30
  EvidenceConfigSchema,
30
31
  ExternalSkillCandidateEvaluationVerdictSchema,
31
32
  ExternalSkillCandidateSchema,
@@ -80,7 +81,7 @@ import {
80
81
  resolveGeneratedAgentRole,
81
82
  resolveGuardrailsConfig,
82
83
  stripKnownSwarmPrefix
83
- } from "./index-hw9b2xng.js";
84
+ } from "./index-q9h0wb04.js";
84
85
  import"./index-p0arc26j.js";
85
86
  import"./index-293f68mj.js";
86
87
  import"./index-a76rekgs.js";
@@ -139,6 +140,7 @@ export {
139
140
  ExternalSkillCandidateSchema,
140
141
  ExternalSkillCandidateEvaluationVerdictSchema,
141
142
  EvidenceConfigSchema,
143
+ EpicConfigSchema,
142
144
  DocsConfigSchema,
143
145
  DiscoverySourceSchema,
144
146
  DesignDocsConfigSchema,
@@ -21,11 +21,11 @@ import {
21
21
  retireSkill,
22
22
  sanitizeSlug,
23
23
  selectCandidateEntries
24
- } from "./index-d9fbxaqd.js";
24
+ } from "./index-s8bj492g.js";
25
25
  import"./index-e8pk68cc.js";
26
26
  import"./index-fjwwrwr5.js";
27
27
  import"./index-jtqkh8jf.js";
28
- import"./index-yx44zd0p.js";
28
+ import"./index-zgwm4ryv.js";
29
29
  import"./index-bcp79s17.js";
30
30
  import"./index-b9v501fr.js";
31
31
  import"./index-p0ye10nd.js";
@@ -0,0 +1,36 @@
1
+ /**
2
+ * `/swarm coupling` — read-only coupling report (Epic mode, Capability B).
3
+ *
4
+ * Computes `p` for the current plan and surfaces the modules that contribute
5
+ * most to detected coupling, with a ranked decoupling roadmap. Read-only:
6
+ * changes no execution behavior; with the optional `--persist` flag, writes
7
+ * a structured JSON report under `.swarm/epic/coupling-report.json` for
8
+ * programmatic consumption.
9
+ *
10
+ * This command always runs independent of `turbo.epic.cochange.enabled`. The
11
+ * config flag gates runtime planner integration (M3); `/swarm coupling` is a
12
+ * diagnostic and what-if tool, so users can see the report before opting in.
13
+ *
14
+ * Flags:
15
+ * --phase <n> Scope to one phase (default: whole plan).
16
+ * --threshold <number> NPMI floor override (default: EpicConfigSchema 0.6).
17
+ * --min-co-changes <n> Co-change-count floor override (default: 5).
18
+ * --format <fmt> 'markdown' (default) or 'json'.
19
+ * --persist Also write JSON to .swarm/epic/coupling-report.json.
20
+ */
21
+ import { loadPlanJsonOnly } from '../plan/manager.js';
22
+ import { getCoChangePairs } from '../turbo/epic/cochange-source.js';
23
+ /**
24
+ * Entry point invoked from the command registry. Returns the report
25
+ * formatted per the `--format` flag, plus a one-line "wrote to ..." trailer
26
+ * when `--persist` is on.
27
+ */
28
+ export declare function handleCouplingCommand(directory: string, args: string[]): Promise<string>;
29
+ /**
30
+ * Test-only DI seam. Production code calls `_internals.fn(...)` so tests can
31
+ * replace these without `mock.module` (AGENTS.md invariant 7).
32
+ */
33
+ export declare const _internals: {
34
+ loadPlanJsonOnly: typeof loadPlanJsonOnly;
35
+ getCoChangePairs: typeof getCoChangePairs;
36
+ };
@@ -0,0 +1,52 @@
1
+ /**
2
+ * `/swarm epic` — Epic Mode activation toggle and diagnostics (Capability C).
3
+ *
4
+ * Subcommands:
5
+ * /swarm epic on — enable Epic Mode for this session
6
+ * /swarm epic off — disable Epic Mode for this session
7
+ * /swarm epic — toggle (on if off, off if on)
8
+ * /swarm epic status — show current state + last decision rationale
9
+ * /swarm epic decide — run the activation decision once and print the
10
+ * verdict without dispatching execution
11
+ * (read-only what-if; does NOT write to
12
+ * `.swarm/evidence/epic-promotions.jsonl`)
13
+ *
14
+ * Toggling only mutates session state (and the durable
15
+ * `.swarm/epic-state.json`); it does not start or stop any execution. The
16
+ * `epic_decide_phase` + `epic_plan_waves` tools (plus per-wave Task dispatch
17
+ * by the architect) are the architect-facing entries that gate execution.
18
+ */
19
+ import { loadPluginConfigWithMeta } from '../config/index.js';
20
+ import { isGitRepo } from '../git/branch.js';
21
+ import { loadPlanJsonOnly } from '../plan/manager.js';
22
+ import { ensureAgentSession } from '../state.js';
23
+ import { decideEpicActivation } from '../turbo/epic/activation.js';
24
+ import { isCalibrationStateUnreadable, loadCalibrationState } from '../turbo/epic/calibration.js';
25
+ import { getCoChangeData } from '../turbo/epic/cochange-source.js';
26
+ import { readDivergenceHistory } from '../turbo/epic/divergence-recorder.js';
27
+ import { readPromotionEvidence } from '../turbo/epic/promotion-evidence.js';
28
+ import { disableEpicMode, enableEpicMode, isEpicModeActive, isStateUnreadable, loadEpicSessionState } from '../turbo/epic/state.js';
29
+ import { readTaskScopes } from '../turbo/lean/conflicts.js';
30
+ /**
31
+ * Test-only DI seam. Production code calls `_internals.fn(...)` so tests can
32
+ * replace these without `mock.module` (AGENTS.md invariant 7).
33
+ */
34
+ export declare const _internals: {
35
+ loadPluginConfigWithMeta: typeof loadPluginConfigWithMeta;
36
+ loadPlanJsonOnly: typeof loadPlanJsonOnly;
37
+ getCoChangeData: typeof getCoChangeData;
38
+ decideEpicActivation: typeof decideEpicActivation;
39
+ ensureAgentSession: typeof ensureAgentSession;
40
+ isEpicModeActive: typeof isEpicModeActive;
41
+ isStateUnreadable: typeof isStateUnreadable;
42
+ loadEpicSessionState: typeof loadEpicSessionState;
43
+ enableEpicMode: typeof enableEpicMode;
44
+ disableEpicMode: typeof disableEpicMode;
45
+ readTaskScopes: typeof readTaskScopes;
46
+ readPromotionEvidence: typeof readPromotionEvidence;
47
+ loadCalibrationState: typeof loadCalibrationState;
48
+ isCalibrationStateUnreadable: typeof isCalibrationStateUnreadable;
49
+ readDivergenceHistory: typeof readDivergenceHistory;
50
+ isGitRepo: typeof isGitRepo;
51
+ };
52
+ export declare function handleEpicCommand(directory: string, args: string[], sessionID: string): Promise<string>;
@@ -316,6 +316,22 @@ export declare const COMMAND_REGISTRY: {
316
316
  readonly category: "utility";
317
317
  readonly toolPolicy: "restricted";
318
318
  };
319
+ readonly coupling: {
320
+ readonly handler: (ctx: CommandContext) => Promise<string>;
321
+ readonly description: "Measure plan coupling (p) and rank modules driving conflicts (Epic mode preview)";
322
+ readonly details: "Computes the coupling coefficient p = (conflicting task pairs) / (total task pairs) over the current plan, using Epic mode's combined path + co-change conflict signal. Surfaces per-module contention and a ranked decoupling roadmap. Read-only: runs independent of `turbo.epic.cochange.enabled` so it can be used as a what-if diagnostic before opting into the runtime signal.";
323
+ readonly args: "--phase <n>, --threshold <-1..1>, --min-co-changes <n>, --format markdown|json, --persist";
324
+ readonly category: "diagnostics";
325
+ readonly toolPolicy: "none";
326
+ };
327
+ readonly epic: {
328
+ readonly handler: (ctx: CommandContext) => Promise<string>;
329
+ readonly description: "Toggle Epic Mode (autonomous coupling-aware parallel activation) and inspect its decisions";
330
+ readonly details: "Epic Mode is an additive overlay that composes Lean Turbo. When on, the architect follows the transparent decide-then-dispatch wave flow: declare_scope (per pending task) → epic_decide_phase → epic_plan_waves → for each wave in order, dispatch one Task per taskId in the wave, ALL in one assistant message (each concurrent coder appears as a visible subagent the user can click into) → epic_record_divergence. epic_decide_phase computes the plan-wide coupling coefficient p and gates parallel promotion on p + a hot-module check + a greenfield rule. epic_plan_waves partitions promoted phases into ordered concurrent groups (waves) that respect dependency order and scope disjointness. Subcommands: on, off, status, decide (read-only what-if), last (most recent decision from durable evidence log), calibration (Capability D state: learned threshold + hot modules + recent divergent tasks). Bare /swarm epic shows status. Decision rationale persists to .swarm/evidence/epic-promotions.jsonl after every epic_decide_phase invocation.";
331
+ readonly args: "on | off | status | decide | last | calibration";
332
+ readonly category: "diagnostics";
333
+ readonly toolPolicy: "none";
334
+ };
319
335
  readonly 'dark-matter': {
320
336
  readonly handler: (ctx: CommandContext) => Promise<string>;
321
337
  readonly description: "Detect hidden file couplings via co-change NPMI analysis";
@@ -644,8 +660,8 @@ export declare const COMMAND_REGISTRY: {
644
660
  };
645
661
  readonly turbo: {
646
662
  readonly handler: (ctx: CommandContext) => Promise<string>;
647
- readonly description: "Toggle Turbo Mode strategy for the active session [on|off|lean|standard|status]";
648
- readonly args: "on, off, lean, standard, status";
663
+ readonly description: "Toggle Turbo Mode strategy for the active session [on|off|lean|standard|epic|status]";
664
+ readonly args: "on, off, lean, standard, epic, status";
649
665
  readonly details: string;
650
666
  readonly category: "utility";
651
667
  readonly toolPolicy: "none";
@@ -93,3 +93,4 @@ export declare const AUTO_PROCEED_BANNER = "## \u23ED\uFE0F AUTO-PROCEED STATUS\
93
93
  export declare const DEFAULT_LEAN_TURBO_CONFIG: LeanTurboConfig;
94
94
  export declare const DEFAULT_WORKTREE_ISOLATION_CONFIG: WorktreeIsolationConfig;
95
95
  export declare const LEAN_TURBO_BANNER = "## \uD83D\uDEE4\uFE0F LEAN TURBO ACTIVE\n\nLane-based parallel execution is enabled for this phase.\n\nBehavioral changes:\n- Tasks are partitioned into parallel lanes based on file-scope conflicts. Tasks in the same lane run sequentially; tasks in different lanes run concurrently (up to max_parallel_coders).\n- **Lane dispatch overrides the one-agent-per-message rule**: for lean lane dispatch only, you may send multiple Task tool calls concurrently (one per lane).\n- **Lane tasks skip per-task Stage B** (reviewer + test_engineer). Quality is enforced at phase-end via phase reviewer and critic gates instead.\n- **Degraded tasks** (global files, protected paths, high-risk patterns) and **serialized tasks** (lock-conflicted) run through standard serial workflow with full Stage B gates.\n- **Phase reviewer and critic are REQUIRED** before phase_complete when lean turbo is active \u2014 they serve as the holistic quality gate for all lane work.\n- **Full-Auto composition**: if Full-Auto is also active, lane dispatch is subject to Full-Auto delegation policy and phase approval.\n- Use the lean_turbo_run_phase tool to execute a phase with parallel lanes\n\nDo NOT skip phase reviewer/critic when configured. Degraded and serialized tasks MUST still go through full Stage B.\n";
96
+ export declare const EPIC_MODE_BANNER = "## \uD83E\uDDED EPIC MODE ACTIVE\n\n**\u26D4 THE USER ALWAYS COMES FIRST \u2014 this overrides everything below.** The user can message you at ANY time, including mid-phase while coders are running or retrying. The instant a user message arrives \u2014 a question, a slash command, a comment, anything \u2014 STOP advancing the flow. Do not dispatch, do not retry, do not call another tool. Read what they said and respond to them directly, in plain conversation, first. Never keep executing the protocol and leave a user message unanswered \u2014 ignoring the user is the single worst failure mode in this mode. After you've answered, pick up where you left off. If you're mid-wave when they interrupt, tell them the state (\"3.1 and 3.2 are still running; I'll continue once I've answered you\") rather than going silent.\n\n**Activation \u2260 start.** Until the user asks for execution (\"start phase N\", \"run task X\", \"continue\"): do nothing. On `/swarm turbo epic`, `/swarm epic *` and any slash status/config command: call the named tool ONCE, surface its output VERBATIM, then stop. Don't infer intent \u2014 if unsure, ASK. This restraint applies ONLY before activation.\n\n**Talk to the user as you work** \u2014 like you naturally would. Once they ask you to run a phase, keep them in the loop with a sentence before each step about what you're doing and why (\"Declaring scopes for 3.1\u20133.3 so the planner can find parallelism\u2026\", \"Discrimination and calibration are independent, so I'll run 3.1 and 3.2 in parallel\u2026\"). This is normal conversation, not a form to fill in \u2014 the steps below tell you the key facts to share, but say them in your own voice. Don't go silent and tool-only through a phase.\n\nUse `epic_plan_waves` (NOT `lean_turbo_plan_lanes` or the deprecated `epic_run_phase`) for the wave plan. Do NOT call `lean_turbo_run_phase` directly.\n\n### Six-step flow (only when the user asks to run a phase)\n\n> Supersedes Rule 1a/3a: declare ALL pending scopes UP FRONT (step 1), BEFORE step 2. Just-in-time declaration breaks the wave planner.\n\n**1. `declare_scope` for every pending task** \u2014 one call per single `taskId` string (NOT ranges/arrays/globs). Tight, disjoint scopes; avoid shared files (`__init__.py`, barrels, registries) \u2014 they force serial waves. Declared scope is a CONTRACT; if a task needs more files mid-run, re-declare BEFORE dispatching.\n\n**2. `epic_decide_phase(directory, phase=N, sessionID)`** \u2014 returns:\n- `decided`+`promote` \u2192 step 3\n- `demoted` \u2192 step 6 (per-task serial)\n- `scopes-missing` \u2192 `declare_scope` each `missingScopes[]`, retry step 2\n- `no-phase` | `phase-empty` | `phase-already-complete` | `epic-state-unreadable` \u2192 fix per response `message`, retry. `phase-already-complete` means call step 2 with `phase=N+1` (NOT step 4 directly).\n- other \u2192 fix per `message`, retry\n\n**3. Surface the verdict to the user immediately, before any further action:**\n> Epic Mode: <PROMOTE|DEMOTE> (p=<value>) \u2014 <one-sentence rationale or top blocking reason>\n> Dependencies: <task_id> \u2190 <deps>; \u2026 (omit if none)\n\nThe verdict is the user's only visibility into what Epic is doing \u2014 silence here makes the mode invisible. If you're going to spend time on this phase, tell the user why up front. Phrase it naturally; the format above is a guide, not a script.\n\n**4. `epic_plan_waves(directory, phase=N)`** \u2014 returns `{ waves: [{ waveId, taskIds, files }], serializedTasks, degradedTasks, degradationSummary }`. Failure reasons mirror step 2; additionally: `git-failed` (retry), `planner-error` (check `errors[0]`).\n\n**4b. Surface the wave plan to the user, before dispatching any `Task`:**\n> Wave plan (<N> waves): Wave 1 \u2192 [<ids>] (parallel); Wave 2 \u2192 [<ids>]; \u2026 \u2014 serialized [<ids>], degraded [<ids>]\n\nWalk them through which tasks run in which wave and what's parallel \u2014 naturally, in your own words. If `waves.length` exceeds the distinct-dependency-layer count, also flag the over-split and its likely cause (typical: a shared file like a barrel/registry in multiple scopes forces serial waves), e.g. \"Wave N split into K single-task waves because every scope claims `<shared-file>` \u2014 re-declare those tasks without it to restore parallelism, then re-plan.\"\n\n`serializedTasks` causes (NOT `declare_scope`-fixable): cycle, `no-scope`, `invalid-scope`, cap-exhaustion. Fix dep graph or scope contents, re-plan.\n\n`degradedTasks[].reason` keys:\n- `global file conflict` / `protected path` \u2192 balanced mode, dispatch per-task after waves\n- `cross-batch upstream not committed (greenfield-smart Rule 3): <ids>` \u2192 commit named upstreams, re-plan\n- `unresolved in-batch dependency: <ids>` \u2192 fix upstream degrade/serialize, re-plan\n- `planning leftover (no identifiable blocker)` \u2192 surface as planner bug\n\n**5. Dispatch each wave: `wave.taskIds.length` SEPARATE `Task` calls in ONE assistant message.** Per wave in order:\n- One `Task(subagent_type=\"coder\", description=\"Phase N task <id>\", prompt=\"<scope + acceptance>\")` per `taskId`\n- ALL in same turn \u2192 concurrent\n- Wait for all in wave to reach `update_task_status(completed)` + `epic_record_divergence` before next wave\n\n\u26A0\uFE0F **Three defects:**\n1. **Bundling**: multiple ids in one Task call \u2192 kills 1:1 coder visibility\n2. **Splitting across messages**: serial execution, no parallelism\n3. **Skipping single-task waves**: still emit ONE Task, wait for completion+divergence\n\nThis is the only sanctioned dispatch path. Don't use `lean_turbo_run_phase`; don't bundle through other tools \u2014 visibility requires `Task`.\n\n`serializedTasks` + `degradedTasks` (after wave loop): ONE Task per assistant message each, never batched, wait for completion+divergence between.\n\n**6. After each `update_task_status(completed)`, call `epic_record_divergence(directory, taskId, sessionID)`** (feeds calibration). If `summary.isClean: false`:\n> Divergence: task `<id>` wrote <undeclaredCount> undeclared file(s) (ratio <ratio>)\n\n### Phase-complete + audit\n\nPhase reviewer + critic still required at `phase_complete` (Epic Mode doesn't change Stage B).\n\nAudit (no architect needed): `/swarm epic status | last | decide | calibration`.\n";
@@ -965,6 +965,37 @@ export declare const LeanTurboConfigSchema: z.ZodObject<{
965
965
  worktree_dir: z.ZodOptional<z.ZodString>;
966
966
  }, z.core.$strip>;
967
967
  export type LeanTurboConfig = z.infer<typeof LeanTurboConfigSchema>;
968
+ /**
969
+ * Epic mode — co-change-aware conflict detection settings.
970
+ *
971
+ * Epic mode is an additive layer that composes Lean Turbo (it never modifies it).
972
+ * Capability A surfaces git co-change history as an extra conflict signal so
973
+ * pairs of files that historically change together can be treated as conflicting
974
+ * even when path-based rules would not catch the coupling.
975
+ *
976
+ * With `cochange.enabled: false` (the default), no Epic-mode code runs in any
977
+ * existing flow — behavior is identical to upstream Lean Turbo.
978
+ */
979
+ export declare const EpicConfigSchema: z.ZodObject<{
980
+ cochange: z.ZodOptional<z.ZodObject<{
981
+ enabled: z.ZodDefault<z.ZodBoolean>;
982
+ threshold: z.ZodDefault<z.ZodNumber>;
983
+ min_co_changes: z.ZodDefault<z.ZodNumber>;
984
+ }, z.core.$strict>>;
985
+ mode: z.ZodOptional<z.ZodObject<{
986
+ enabled: z.ZodDefault<z.ZodBoolean>;
987
+ activation_threshold: z.ZodDefault<z.ZodNumber>;
988
+ min_commits_for_signal: z.ZodDefault<z.ZodNumber>;
989
+ }, z.core.$strict>>;
990
+ calibration: z.ZodOptional<z.ZodObject<{
991
+ enabled: z.ZodDefault<z.ZodBoolean>;
992
+ floor_threshold: z.ZodDefault<z.ZodNumber>;
993
+ tighten_step: z.ZodDefault<z.ZodNumber>;
994
+ loosen_step: z.ZodDefault<z.ZodNumber>;
995
+ loosen_window: z.ZodDefault<z.ZodNumber>;
996
+ }, z.core.$strict>>;
997
+ }, z.core.$strict>;
998
+ export type EpicConfig = z.infer<typeof EpicConfigSchema>;
968
999
  export declare const StandardTurboConfigSchema: z.ZodObject<{
969
1000
  strategy: z.ZodLiteral<"standard">;
970
1001
  lean: z.ZodOptional<z.ZodObject<{
@@ -987,6 +1018,25 @@ export declare const StandardTurboConfigSchema: z.ZodObject<{
987
1018
  }>>>;
988
1019
  worktree_dir: z.ZodOptional<z.ZodString>;
989
1020
  }, z.core.$strip>>;
1021
+ epic: z.ZodOptional<z.ZodObject<{
1022
+ cochange: z.ZodOptional<z.ZodObject<{
1023
+ enabled: z.ZodDefault<z.ZodBoolean>;
1024
+ threshold: z.ZodDefault<z.ZodNumber>;
1025
+ min_co_changes: z.ZodDefault<z.ZodNumber>;
1026
+ }, z.core.$strict>>;
1027
+ mode: z.ZodOptional<z.ZodObject<{
1028
+ enabled: z.ZodDefault<z.ZodBoolean>;
1029
+ activation_threshold: z.ZodDefault<z.ZodNumber>;
1030
+ min_commits_for_signal: z.ZodDefault<z.ZodNumber>;
1031
+ }, z.core.$strict>>;
1032
+ calibration: z.ZodOptional<z.ZodObject<{
1033
+ enabled: z.ZodDefault<z.ZodBoolean>;
1034
+ floor_threshold: z.ZodDefault<z.ZodNumber>;
1035
+ tighten_step: z.ZodDefault<z.ZodNumber>;
1036
+ loosen_step: z.ZodDefault<z.ZodNumber>;
1037
+ loosen_window: z.ZodDefault<z.ZodNumber>;
1038
+ }, z.core.$strict>>;
1039
+ }, z.core.$strict>>;
990
1040
  }, z.core.$strip>;
991
1041
  export declare const LeanTurboStrategyConfigSchema: z.ZodObject<{
992
1042
  strategy: z.ZodLiteral<"lean">;
@@ -1010,6 +1060,25 @@ export declare const LeanTurboStrategyConfigSchema: z.ZodObject<{
1010
1060
  }>>>;
1011
1061
  worktree_dir: z.ZodOptional<z.ZodString>;
1012
1062
  }, z.core.$strip>;
1063
+ epic: z.ZodOptional<z.ZodObject<{
1064
+ cochange: z.ZodOptional<z.ZodObject<{
1065
+ enabled: z.ZodDefault<z.ZodBoolean>;
1066
+ threshold: z.ZodDefault<z.ZodNumber>;
1067
+ min_co_changes: z.ZodDefault<z.ZodNumber>;
1068
+ }, z.core.$strict>>;
1069
+ mode: z.ZodOptional<z.ZodObject<{
1070
+ enabled: z.ZodDefault<z.ZodBoolean>;
1071
+ activation_threshold: z.ZodDefault<z.ZodNumber>;
1072
+ min_commits_for_signal: z.ZodDefault<z.ZodNumber>;
1073
+ }, z.core.$strict>>;
1074
+ calibration: z.ZodOptional<z.ZodObject<{
1075
+ enabled: z.ZodDefault<z.ZodBoolean>;
1076
+ floor_threshold: z.ZodDefault<z.ZodNumber>;
1077
+ tighten_step: z.ZodDefault<z.ZodNumber>;
1078
+ loosen_step: z.ZodDefault<z.ZodNumber>;
1079
+ loosen_window: z.ZodDefault<z.ZodNumber>;
1080
+ }, z.core.$strict>>;
1081
+ }, z.core.$strict>>;
1013
1082
  }, z.core.$strip>;
1014
1083
  export declare const TurboConfigSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
1015
1084
  strategy: z.ZodLiteral<"standard">;
@@ -1033,6 +1102,25 @@ export declare const TurboConfigSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
1033
1102
  }>>>;
1034
1103
  worktree_dir: z.ZodOptional<z.ZodString>;
1035
1104
  }, z.core.$strip>>;
1105
+ epic: z.ZodOptional<z.ZodObject<{
1106
+ cochange: z.ZodOptional<z.ZodObject<{
1107
+ enabled: z.ZodDefault<z.ZodBoolean>;
1108
+ threshold: z.ZodDefault<z.ZodNumber>;
1109
+ min_co_changes: z.ZodDefault<z.ZodNumber>;
1110
+ }, z.core.$strict>>;
1111
+ mode: z.ZodOptional<z.ZodObject<{
1112
+ enabled: z.ZodDefault<z.ZodBoolean>;
1113
+ activation_threshold: z.ZodDefault<z.ZodNumber>;
1114
+ min_commits_for_signal: z.ZodDefault<z.ZodNumber>;
1115
+ }, z.core.$strict>>;
1116
+ calibration: z.ZodOptional<z.ZodObject<{
1117
+ enabled: z.ZodDefault<z.ZodBoolean>;
1118
+ floor_threshold: z.ZodDefault<z.ZodNumber>;
1119
+ tighten_step: z.ZodDefault<z.ZodNumber>;
1120
+ loosen_step: z.ZodDefault<z.ZodNumber>;
1121
+ loosen_window: z.ZodDefault<z.ZodNumber>;
1122
+ }, z.core.$strict>>;
1123
+ }, z.core.$strict>>;
1036
1124
  }, z.core.$strip>, z.ZodObject<{
1037
1125
  strategy: z.ZodLiteral<"lean">;
1038
1126
  lean: z.ZodObject<{
@@ -1055,6 +1143,25 @@ export declare const TurboConfigSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
1055
1143
  }>>>;
1056
1144
  worktree_dir: z.ZodOptional<z.ZodString>;
1057
1145
  }, z.core.$strip>;
1146
+ epic: z.ZodOptional<z.ZodObject<{
1147
+ cochange: z.ZodOptional<z.ZodObject<{
1148
+ enabled: z.ZodDefault<z.ZodBoolean>;
1149
+ threshold: z.ZodDefault<z.ZodNumber>;
1150
+ min_co_changes: z.ZodDefault<z.ZodNumber>;
1151
+ }, z.core.$strict>>;
1152
+ mode: z.ZodOptional<z.ZodObject<{
1153
+ enabled: z.ZodDefault<z.ZodBoolean>;
1154
+ activation_threshold: z.ZodDefault<z.ZodNumber>;
1155
+ min_commits_for_signal: z.ZodDefault<z.ZodNumber>;
1156
+ }, z.core.$strict>>;
1157
+ calibration: z.ZodOptional<z.ZodObject<{
1158
+ enabled: z.ZodDefault<z.ZodBoolean>;
1159
+ floor_threshold: z.ZodDefault<z.ZodNumber>;
1160
+ tighten_step: z.ZodDefault<z.ZodNumber>;
1161
+ loosen_step: z.ZodDefault<z.ZodNumber>;
1162
+ loosen_window: z.ZodDefault<z.ZodNumber>;
1163
+ }, z.core.$strict>>;
1164
+ }, z.core.$strict>>;
1058
1165
  }, z.core.$strip>], "strategy">;
1059
1166
  export type TurboConfig = z.infer<typeof TurboConfigSchema>;
1060
1167
  /** Where an external skill candidate was discovered. */
@@ -1864,6 +1971,25 @@ export declare const PluginConfigSchema: z.ZodObject<{
1864
1971
  }>>>;
1865
1972
  worktree_dir: z.ZodOptional<z.ZodString>;
1866
1973
  }, z.core.$strip>>;
1974
+ epic: z.ZodOptional<z.ZodObject<{
1975
+ cochange: z.ZodOptional<z.ZodObject<{
1976
+ enabled: z.ZodDefault<z.ZodBoolean>;
1977
+ threshold: z.ZodDefault<z.ZodNumber>;
1978
+ min_co_changes: z.ZodDefault<z.ZodNumber>;
1979
+ }, z.core.$strict>>;
1980
+ mode: z.ZodOptional<z.ZodObject<{
1981
+ enabled: z.ZodDefault<z.ZodBoolean>;
1982
+ activation_threshold: z.ZodDefault<z.ZodNumber>;
1983
+ min_commits_for_signal: z.ZodDefault<z.ZodNumber>;
1984
+ }, z.core.$strict>>;
1985
+ calibration: z.ZodOptional<z.ZodObject<{
1986
+ enabled: z.ZodDefault<z.ZodBoolean>;
1987
+ floor_threshold: z.ZodDefault<z.ZodNumber>;
1988
+ tighten_step: z.ZodDefault<z.ZodNumber>;
1989
+ loosen_step: z.ZodDefault<z.ZodNumber>;
1990
+ loosen_window: z.ZodDefault<z.ZodNumber>;
1991
+ }, z.core.$strict>>;
1992
+ }, z.core.$strict>>;
1867
1993
  }, z.core.$strip>, z.ZodObject<{
1868
1994
  strategy: z.ZodLiteral<"lean">;
1869
1995
  lean: z.ZodObject<{
@@ -1886,6 +2012,25 @@ export declare const PluginConfigSchema: z.ZodObject<{
1886
2012
  }>>>;
1887
2013
  worktree_dir: z.ZodOptional<z.ZodString>;
1888
2014
  }, z.core.$strip>;
2015
+ epic: z.ZodOptional<z.ZodObject<{
2016
+ cochange: z.ZodOptional<z.ZodObject<{
2017
+ enabled: z.ZodDefault<z.ZodBoolean>;
2018
+ threshold: z.ZodDefault<z.ZodNumber>;
2019
+ min_co_changes: z.ZodDefault<z.ZodNumber>;
2020
+ }, z.core.$strict>>;
2021
+ mode: z.ZodOptional<z.ZodObject<{
2022
+ enabled: z.ZodDefault<z.ZodBoolean>;
2023
+ activation_threshold: z.ZodDefault<z.ZodNumber>;
2024
+ min_commits_for_signal: z.ZodDefault<z.ZodNumber>;
2025
+ }, z.core.$strict>>;
2026
+ calibration: z.ZodOptional<z.ZodObject<{
2027
+ enabled: z.ZodDefault<z.ZodBoolean>;
2028
+ floor_threshold: z.ZodDefault<z.ZodNumber>;
2029
+ tighten_step: z.ZodDefault<z.ZodNumber>;
2030
+ loosen_step: z.ZodDefault<z.ZodNumber>;
2031
+ loosen_window: z.ZodDefault<z.ZodNumber>;
2032
+ }, z.core.$strict>>;
2033
+ }, z.core.$strict>>;
1889
2034
  }, z.core.$strip>], "strategy">>;
1890
2035
  turbo_mode: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
1891
2036
  quiet: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
@@ -6,7 +6,28 @@ export type GitRepositoryStatus = {
6
6
  message: string;
7
7
  };
8
8
  /**
9
- * Execute git command safely
9
+ * Execute git command safely.
10
+ *
11
+ * Non-interactive enforcement (AGENTS.md #3): three defenses ensure git
12
+ * cannot block on a TTY prompt — GPG passphrase, credential helper,
13
+ * "are you sure?" rebase confirmation, etc.
14
+ *
15
+ * 1. `stdio: ['ignore', ...]` closes the child's stdin. A prompt has
16
+ * no input source, so git/GPG/credential-helper sees EOF.
17
+ * 2. `GIT_TERMINAL_PROMPT=0` tells git itself to refuse any prompt
18
+ * attempt outright rather than falling back to the controlling
19
+ * terminal (some prompts route through git, not the child).
20
+ * 3. `-c commit.gpgsign=false -c tag.gpgsign=false` prepended to every
21
+ * command. Closes the SILENT-failure path where a developer/CI host
22
+ * has `commit.gpgsign=true` globally and no GPG agent — without (3),
23
+ * defenses (1)+(2) merely turn the prompt-hang into an immediate
24
+ * non-zero exit, which `commitTaskCompletion` catches as
25
+ * `commit-failed` non-fatally. Result: every Epic Rule 2 commit
26
+ * silently fails, predecessor-evidence never accumulates, every
27
+ * phase demotes. (3) forces the underlying git subcommand to skip
28
+ * signing entirely. This is the single source of truth for
29
+ * non-interactive git, so the hardening covers every caller
30
+ * (current and future) uniformly.
10
31
  */
11
32
  declare function gitExec(args: string[], cwd: string): string;
12
33
  export declare function getGitRepositoryStatus(cwd: string): GitRepositoryStatus;
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Worktree merge-back status registry (leaf module — no app imports).
3
+ *
4
+ * Bridges two subsystems that otherwise cannot see each other:
5
+ *
6
+ * - WORKTREE ISOLATION (`worktree-isolation.ts`) knows, at merge-back
7
+ * time, whether a Task-dispatched coder's isolated worktree merged
8
+ * cleanly back into the main tree.
9
+ * - EPIC MODE Rule 2 (`plan/manager.updateTaskStatus`) auto-commits a
10
+ * `swarm(task <id>):` marker on completion. That marker is the
11
+ * evidence Rule 3 scans to decide a downstream task's upstream is
12
+ * satisfied.
13
+ *
14
+ * Without this bridge, a coder whose worktree merge-back FAILED (conflict)
15
+ * leaves its changes stranded in the preserved worktree, yet Rule 2 would
16
+ * still fire an `--allow-empty` marker and Rule 3 would treat the task as
17
+ * done — silently advancing the plan past work that never landed.
18
+ *
19
+ * This module is deliberately a LEAF: it imports nothing from the app so
20
+ * that both `worktree-isolation.ts` (writer) and `plan/manager.ts` (reader)
21
+ * can depend on it without creating an import cycle.
22
+ *
23
+ * STATE DURABILITY: Status is stored in an in-memory Map (fast, per-process)
24
+ * AND persisted to `.swarm/worktree-merge-status.json` (survives plugin restart).
25
+ * On each write, the JSON file is atomically updated. On read, the in-memory
26
+ * map is checked first (fast path); on plugin restart, the map is empty but
27
+ * the durable file is restored on first read or Rule 2 lookup.
28
+ */
29
+ /** Why a task's worktree work did not fully reach the main tree. */
30
+ export type WorktreeMergeOutcome = 'partial' | 'failed';
31
+ export interface WorktreeMergeFailure {
32
+ outcome: WorktreeMergeOutcome;
33
+ /** Pipeline stage that failed (e.g. 'merge', 'auto-commit'). */
34
+ stage: string;
35
+ /** Human-readable detail surfaced in the Rule 2 skip warning. */
36
+ message: string;
37
+ }
38
+ declare function getDurableStatusPath(projectDir?: string): string;
39
+ /**
40
+ * Load the durable file into the in-memory map. The clear+repopulate is
41
+ * synchronous (no `await`), so no read can observe a transiently-empty map
42
+ * in Node's single-threaded model. Called only from init and the one-shot
43
+ * lazy load — never on a steady-state lookup.
44
+ */
45
+ declare function loadDurableStatus(statusPath: string): void;
46
+ declare function saveDurableStatus(statusPath: string): void;
47
+ /**
48
+ * Initialize the durable status path for this project and load existing
49
+ * status from disk. Called from `createDelegationGateHook` before any coder
50
+ * dispatches. Re-initializing the SAME path is a no-op (the in-memory map is
51
+ * already the live authority); switching to a DIFFERENT path reloads.
52
+ */
53
+ export declare function initDurableStatusPath(projectDir: string): void;
54
+ /**
55
+ * Record that task `taskId`'s worktree merge-back did not fully land.
56
+ * No-op when `taskId` is undefined (non-plan dispatches carry no plan id
57
+ * and are never subject to Epic Rule 2).
58
+ */
59
+ export declare function recordWorktreeMergeFailure(taskId: string | undefined, failure: WorktreeMergeFailure): void;
60
+ /**
61
+ * Clear any recorded failure for `taskId`. Called when a (re-)dispatch of
62
+ * the same task merges cleanly, so a later success supersedes an earlier
63
+ * failure and Rule 2 is allowed to commit the marker again.
64
+ */
65
+ export declare function clearWorktreeMergeStatus(taskId: string | undefined): void;
66
+ /**
67
+ * Query whether task `taskId`'s most recent worktree merge-back failed.
68
+ * Returns the failure detail, or undefined when the task merged cleanly,
69
+ * was never worktree-isolated, or has no recorded status.
70
+ * On first read after plugin restart, loads from durable storage.
71
+ */
72
+ export declare function getWorktreeMergeFailure(taskId: string): WorktreeMergeFailure | undefined;
73
+ /**
74
+ * @internal test seam. Use `resetForTest()` in afterEach to isolate tests
75
+ * that set durableStatusPath via `initDurableStatusPath`.
76
+ */
77
+ export declare const _internals: {
78
+ failuresByTask: Map<string, WorktreeMergeFailure>;
79
+ getDurableStatusPath: typeof getDurableStatusPath;
80
+ loadDurableStatus: typeof loadDurableStatus;
81
+ saveDurableStatus: typeof saveDurableStatus;
82
+ initDurableStatusPath: typeof initDurableStatusPath;
83
+ /** Reset both in-memory and durable state for test isolation. */
84
+ resetForTest(): void;
85
+ };
86
+ export {};