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.
- package/dist/cli/{capability-probe-jevmgwmf.js → capability-probe-wsjzcp48.js} +2 -2
- package/dist/cli/{config-doctor-zejarrr6.js → config-doctor-6h64pn8n.js} +4 -4
- package/dist/cli/{dispatch-k86d928w.js → dispatch-kb69qw40.js} +3 -3
- package/dist/cli/{evidence-summary-service-g2znnd33.js → evidence-summary-service-gg5m9z57.js} +4 -4
- package/dist/cli/{guardrail-explain-rtd1x26f.js → guardrail-explain-wb1cj312.js} +13 -13
- package/dist/cli/{guardrail-log-80116wmz.js → guardrail-log-eegabqcp.js} +5 -5
- package/dist/cli/{index-jwz50183.js → index-0m44n5qv.js} +14 -14
- package/dist/cli/{index-0sxvwjt0.js → index-1cb4wxnm.js} +2 -2
- package/dist/cli/{index-zfsbaaqh.js → index-5e4e2hvv.js} +1 -1
- package/dist/cli/{index-vq2321gg.js → index-5hvbw5xh.js} +2 -2
- package/dist/cli/{index-5cb86007.js → index-5vpe6vq9.js} +1 -1
- package/dist/cli/{index-red8fm8p.js → index-89xjr3h4.js} +1162 -214
- package/dist/cli/{index-f8r50m3h.js → index-adz3nk9b.js} +2 -2
- package/dist/cli/{index-7r2b453y.js → index-f13d3b69.js} +2 -2
- package/dist/cli/{index-ckntc5gf.js → index-gn8n22th.js} +2 -2
- package/dist/cli/{index-hw9b2xng.js → index-q9h0wb04.js} +36 -3
- package/dist/cli/{index-d9fbxaqd.js → index-s8bj492g.js} +1 -1
- package/dist/cli/{index-hz59hg4h.js → index-v4fcn4tr.js} +1 -1
- package/dist/cli/{index-eb85wtx9.js → index-vqyfscxd.js} +2 -2
- package/dist/cli/{index-5q66xc88.js → index-wv2yj8ka.js} +2598 -1406
- package/dist/cli/{index-yx44zd0p.js → index-zgwm4ryv.js} +9 -1
- package/dist/cli/index.js +12 -12
- package/dist/cli/{pending-delegations-rd40tv9s.js → pending-delegations-35fvcj7z.js} +3 -3
- package/dist/cli/{pr-subscriptions-y1nn36e5.js → pr-subscriptions-b18n1yd8.js} +4 -4
- package/dist/cli/{schema-8d32b2v6.js → schema-84146tvk.js} +3 -1
- package/dist/cli/{skill-generator-a5ehggyg.js → skill-generator-3pvpk4y2.js} +2 -2
- package/dist/commands/coupling.d.ts +36 -0
- package/dist/commands/epic.d.ts +52 -0
- package/dist/commands/registry.d.ts +18 -2
- package/dist/config/constants.d.ts +1 -0
- package/dist/config/schema.d.ts +145 -0
- package/dist/git/branch.d.ts +22 -1
- package/dist/hooks/delegation-gate/worktree-merge-status.d.ts +86 -0
- package/dist/index.js +8401 -5792
- package/dist/memory/schema.d.ts +3 -3
- package/dist/plan/manager.d.ts +10 -0
- package/dist/state.d.ts +16 -0
- package/dist/tools/epic-plan-waves.d.ts +79 -0
- package/dist/tools/epic-record-divergence.d.ts +73 -0
- package/dist/tools/epic-run-phase.d.ts +179 -0
- package/dist/tools/index.d.ts +3 -0
- package/dist/tools/manifest.d.ts +3 -0
- package/dist/tools/tool-metadata.d.ts +12 -0
- package/dist/turbo/epic/activation.d.ts +193 -0
- package/dist/turbo/epic/calibration-engine.d.ts +88 -0
- package/dist/turbo/epic/calibration.d.ts +65 -0
- package/dist/turbo/epic/cochange-conflict.d.ts +79 -0
- package/dist/turbo/epic/cochange-source.d.ts +80 -0
- package/dist/turbo/epic/coupling-report.d.ts +85 -0
- package/dist/turbo/epic/divergence-recorder.d.ts +112 -0
- package/dist/turbo/epic/index.d.ts +24 -0
- package/dist/turbo/epic/promotion-evidence.d.ts +42 -0
- package/dist/turbo/epic/state.d.ts +85 -0
- package/dist/turbo/epic/task-commit.d.ts +110 -0
- package/dist/turbo/epic/upstream-commits.d.ts +82 -0
- package/dist/turbo/epic/wave-planner.d.ts +83 -0
- package/dist/turbo/lean/partition-common.d.ts +85 -0
- package/dist/turbo/lean/planner.d.ts +12 -20
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/logger.d.ts +19 -0
- 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-
|
|
11
|
-
import"./index-
|
|
10
|
+
} from "./index-wv2yj8ka.js";
|
|
11
|
+
import"./index-5hvbw5xh.js";
|
|
12
12
|
import"./index-yhsmmv2z.js";
|
|
13
|
-
import"./index-
|
|
13
|
+
import"./index-s8bj492g.js";
|
|
14
14
|
import"./index-e8pk68cc.js";
|
|
15
|
-
import"./index-
|
|
15
|
+
import"./index-1cb4wxnm.js";
|
|
16
16
|
import {
|
|
17
17
|
DEFAULT_AGENT_CONFIGS
|
|
18
|
-
} from "./index-
|
|
19
|
-
import"./index-
|
|
20
|
-
import"./index-
|
|
21
|
-
import"./index-
|
|
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-
|
|
25
|
-
import"./index-
|
|
24
|
+
import"./index-vqyfscxd.js";
|
|
25
|
+
import"./index-gn8n22th.js";
|
|
26
26
|
import"./index-jtqkh8jf.js";
|
|
27
|
-
import"./index-
|
|
27
|
+
import"./index-5e4e2hvv.js";
|
|
28
28
|
import"./index-p0arc26j.js";
|
|
29
|
-
import"./index-
|
|
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-
|
|
4
|
+
} from "./index-gn8n22th.js";
|
|
5
5
|
import"./index-jtqkh8jf.js";
|
|
6
|
-
import"./index-
|
|
6
|
+
import"./index-5e4e2hvv.js";
|
|
7
7
|
import"./index-p0arc26j.js";
|
|
8
8
|
import {
|
|
9
9
|
init_logger,
|
|
10
10
|
warn
|
|
11
|
-
} from "./index-
|
|
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-
|
|
13
|
-
import"./index-
|
|
12
|
+
} from "./index-vqyfscxd.js";
|
|
13
|
+
import"./index-gn8n22th.js";
|
|
14
14
|
import"./index-jtqkh8jf.js";
|
|
15
|
-
import"./index-
|
|
15
|
+
import"./index-5e4e2hvv.js";
|
|
16
16
|
import"./index-p0arc26j.js";
|
|
17
|
-
import"./index-
|
|
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-
|
|
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-
|
|
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-
|
|
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";
|
package/dist/config/schema.d.ts
CHANGED
|
@@ -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>>;
|
package/dist/git/branch.d.ts
CHANGED
|
@@ -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 {};
|