opencode-swarm 7.188.12 → 7.190.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 (177) hide show
  1. package/README.md +42 -6
  2. package/binaries/win32-arm64/swarm-sandbox-runner.exe +0 -0
  3. package/binaries/win32-x64/swarm-sandbox-runner.exe +0 -0
  4. package/dist/agents/architect.d.ts +1 -1
  5. package/dist/background/completion-observer.d.ts +6 -0
  6. package/dist/cli/{auto-checkpoint-4g963859.js → auto-checkpoint-qvk04fya.js} +9 -9
  7. package/dist/cli/{checkpoint-y9nbjper.js → checkpoint-9q2r5c3j.js} +18 -20
  8. package/dist/cli/{coder-settlement-q8ea2jkw.js → coder-settlement-xcparbf3.js} +13 -13
  9. package/dist/cli/{config-doctor-4v5m6v30.js → config-doctor-by0ak49m.js} +23 -20
  10. package/dist/cli/{core-879105xb.js → core-b1b3kxzy.js} +4 -4
  11. package/dist/cli/{curation-policy-65ckmmd5.js → curation-policy-6p6fcfjb.js} +6 -6
  12. package/dist/cli/{curator-t7g3hamd.js → curator-d7b88mzr.js} +44 -44
  13. package/dist/cli/{curator-drift-satfdnp2.js → curator-drift-6v604220.js} +9 -9
  14. package/dist/cli/curator-llm-factory-yvbktznk.js +82 -0
  15. package/dist/cli/{doctor-knowledge-receipt-phase-id-y3ft2v24.js → doctor-knowledge-receipt-phase-id-kwrvnps9.js} +9 -9
  16. package/dist/cli/{evidence-summary-service-56rqzmrf.js → evidence-summary-service-mzr2q4bn.js} +21 -22
  17. package/dist/cli/{gate-evidence-zzmgph9z.js → gate-evidence-78jes9kq.js} +8 -8
  18. package/dist/cli/guardrail-explain-8kt2mxmw.js +83 -0
  19. package/dist/cli/{guardrail-log-sxv7q0xw.js → guardrail-log-2sthykag.js} +8 -8
  20. package/dist/cli/{guardrail-reset-37cr442b.js → guardrail-reset-2n746rzv.js} +44 -44
  21. package/dist/cli/{hive-promoter-hyeta8d6.js → hive-promoter-s7a5gwhz.js} +44 -44
  22. package/dist/cli/{index-e638qt2d.js → index-0qc7a88d.js} +3 -3
  23. package/dist/cli/{index-kap0d52p.js → index-1s95jrgc.js} +3 -3
  24. package/dist/cli/{index-9tvjwv01.js → index-2fpv2wmc.js} +1 -1
  25. package/dist/cli/{index-jgr1rsr1.js → index-2kpqqcch.js} +4 -4
  26. package/dist/cli/{index-msvf6p5y.js → index-3t9t9q2q.js} +44 -44
  27. package/dist/cli/{index-8q8783g0.js → index-3zwhxk98.js} +1 -1
  28. package/dist/cli/{index-0b172h6r.js → index-49e65y51.js} +5 -5
  29. package/dist/cli/{index-5pafc9x6.js → index-69tgdm9t.js} +18 -8
  30. package/dist/cli/{index-d3rjvm9e.js → index-7vnr5cmp.js} +2 -2
  31. package/dist/cli/{index-twb3f4tk.js → index-8bx6vhzk.js} +1 -1
  32. package/dist/cli/{index-84pwb6cz.js → index-9gcqmgbc.js} +5 -5
  33. package/dist/cli/{index-9sps637v.js → index-9xxqdzp4.js} +4 -4
  34. package/dist/cli/{index-j0qep2zr.js → index-a5ca62vt.js} +1 -1
  35. package/dist/cli/{index-xy2403g9.js → index-ajzgeb2s.js} +2 -2
  36. package/dist/cli/{index-8xabhfwr.js → index-b2j231jy.js} +2 -2
  37. package/dist/cli/{index-19yt5p2t.js → index-dmq7p8s9.js} +6 -6
  38. package/dist/cli/{index-95pzyv9a.js → index-ej1hdw16.js} +5 -5
  39. package/dist/cli/{index-1krdj1sc.js → index-ejk34dnw.js} +127 -13
  40. package/dist/cli/{index-tbyvvq50.js → index-fb5s32c2.js} +862 -1221
  41. package/dist/cli/{index-ntda6tje.js → index-feaxsjrt.js} +10 -2
  42. package/dist/cli/{index-94fgbb34.js → index-ffgcdf49.js} +22147 -15396
  43. package/dist/cli/{index-fhtcx5ag.js → index-h0xhec3d.js} +4 -4
  44. package/dist/cli/{index-kaqdzhj3.js → index-kfevm0n5.js} +2 -2
  45. package/dist/cli/{index-z7gakzb5.js → index-kp8k95v9.js} +4 -4
  46. package/dist/cli/{index-x1eh2636.js → index-mcv0v2dp.js} +1 -1
  47. package/dist/cli/{index-9czp2dcg.js → index-mjfpaxw3.js} +7 -7
  48. package/dist/cli/{index-tmn4pf7q.js → index-mp1s7b0n.js} +7 -7
  49. package/dist/cli/{index-f0jn2nvs.js → index-mseerehd.js} +26 -10
  50. package/dist/cli/{index-f6exny9m.js → index-n8q43eff.js} +2 -2
  51. package/dist/cli/{index-7hr5m7yp.js → index-nkgzkt7c.js} +1 -1
  52. package/dist/cli/{index-8jy605yf.js → index-npe10823.js} +7 -7
  53. package/dist/cli/{index-yc0y5fjt.js → index-p69k8v0e.js} +1 -1
  54. package/dist/cli/{index-p2tqzwh6.js → index-qs9myayh.js} +5 -10
  55. package/dist/cli/{index-hh7pa3yk.js → index-r9yvpr2m.js} +2 -2
  56. package/dist/cli/{index-qha2p47y.js → index-rgkzcw2a.js} +5 -5
  57. package/dist/cli/{index-zfbxfm2m.js → index-s8b67e6h.js} +33 -12
  58. package/dist/cli/{index-m3hhaf61.js → index-s92a5ta3.js} +4 -4
  59. package/dist/cli/{index-dd6p8xb5.js → index-sm01zpqe.js} +46 -46
  60. package/dist/cli/{index-q9kefh33.js → index-sxj143a4.js} +1 -1
  61. package/dist/cli/{index-2h13hjbf.js → index-t0gj8s46.js} +77 -16
  62. package/dist/cli/{index-3dqazqk4.js → index-t73adn9b.js} +2 -2
  63. package/dist/cli/{index-5cksakjf.js → index-tkhenmms.js} +101 -13
  64. package/dist/cli/{index-zsrtt42s.js → index-v3bmwmqw.js} +25 -1
  65. package/dist/cli/{index-w9tfb0am.js → index-vhn4e6rm.js} +7 -7
  66. package/dist/cli/{index-yjjyapbf.js → index-xyvrfram.js} +2 -2
  67. package/dist/cli/{index-k8sarme1.js → index-y9v41185.js} +1 -1
  68. package/dist/cli/{index-1xa8wq76.js → index-z3ahy9c2.js} +2 -2
  69. package/dist/cli/{index-wrdktgpy.js → index-zk6ttjkt.js} +2 -2
  70. package/dist/cli/{index-7z2pxxye.js → index-zyg1m9d0.js} +4 -4
  71. package/dist/cli/index.js +45 -45
  72. package/dist/cli/{knowledge-escalator-dzd35eb3.js → knowledge-escalator-n1ezv1zp.js} +15 -15
  73. package/dist/cli/{knowledge-events-wkr5ne5f.js → knowledge-events-nyqk3mr7.js} +13 -13
  74. package/dist/cli/{knowledge-link-h4pdcnya.js → knowledge-link-6hw3z6wn.js} +5 -5
  75. package/dist/cli/{knowledge-store-sme6sqrb.js → knowledge-store-mnxqcyet.js} +6 -6
  76. package/dist/cli/{knowledge-validator-jgzeawa8.js → knowledge-validator-z30113mp.js} +8 -8
  77. package/dist/cli/{mcp-egrbh8na.js → mcp-mnbqw2s5.js} +1 -1
  78. package/dist/cli/{model-preflight-h7azh4m4.js → model-preflight-ex482fsy.js} +44 -44
  79. package/dist/cli/{pending-delegations-pg284vsk.js → pending-delegations-j18afz5z.js} +12 -12
  80. package/dist/cli/{pr-subscriptions-hzc4fg25.js → pr-subscriptions-yngda8mz.js} +12 -12
  81. package/dist/cli/{pr-workflow-gate-j3r3g3q7.js → pr-workflow-gate-0vt614f0.js} +44 -44
  82. package/dist/cli/{runner-4hfmp1ag.js → runner-ssnsje35.js} +6 -6
  83. package/dist/cli/{scan-cursor-zg0a56c1.js → scan-cursor-8pwfg5g9.js} +7 -7
  84. package/dist/cli/{schema-p246rqkt.js → schema-e58ytmqt.js} +14 -2
  85. package/dist/cli/{scope-persistence-7qtsmx5c.js → scope-persistence-9qfx6ext.js} +16 -16
  86. package/dist/cli/{server-twhpwhgq.js → server-wxcfwsdx.js} +45 -45
  87. package/dist/cli/{skill-generator-7vfbmyrw.js → skill-generator-tbd4tq9r.js} +17 -17
  88. package/dist/cli/{snapshot-coordination-init-050hf69t.js → snapshot-coordination-init-sckyzb94.js} +44 -44
  89. package/dist/cli/{speckit-checkoff-d3v522cb.js → speckit-checkoff-k5jm0abh.js} +5 -5
  90. package/dist/cli/{telemetry-kvhe94gs.js → telemetry-9szfdfqv.js} +1 -1
  91. package/dist/cli/{workspace-snapshot-30kdf018.js → workspace-snapshot-9f12p1s1.js} +3 -3
  92. package/dist/cli/{worktree-collision-ownership-yy1tp8t9.js → worktree-collision-ownership-3wwfc72t.js} +14 -14
  93. package/dist/cli/{worktree-isolation-e56skbfw.js → worktree-isolation-zfk7zk6a.js} +46 -44
  94. package/dist/commands/coupling.d.ts +21 -4
  95. package/dist/commands/epic-learning.d.ts +28 -0
  96. package/dist/commands/epic.d.ts +81 -36
  97. package/dist/commands/registry.d.ts +8 -8
  98. package/dist/commands/turbo.d.ts +16 -1
  99. package/dist/config/constants.d.ts +11 -1
  100. package/dist/config/loader.d.ts +2 -0
  101. package/dist/config/schema.d.ts +291 -77
  102. package/dist/db/coordination-store.d.ts +6 -0
  103. package/dist/epic/close.d.ts +209 -0
  104. package/dist/{turbo/epic → epic}/cochange-conflict.d.ts +5 -4
  105. package/dist/{turbo/epic → epic}/cochange-source.d.ts +10 -1
  106. package/dist/epic/components.d.ts +238 -0
  107. package/dist/epic/config-gate.d.ts +25 -0
  108. package/dist/epic/config.d.ts +101 -0
  109. package/dist/{turbo/epic → epic}/coupling-report.d.ts +3 -1
  110. package/dist/epic/declared-scopes.d.ts +37 -0
  111. package/dist/epic/epic-branch.d.ts +157 -0
  112. package/dist/epic/gate-policy.d.ts +114 -0
  113. package/dist/epic/git-once.d.ts +26 -0
  114. package/dist/epic/learning-store.d.ts +176 -0
  115. package/dist/epic/learning.d.ts +198 -0
  116. package/dist/epic/legacy-migration.d.ts +26 -0
  117. package/dist/epic/lifecycle.d.ts +437 -0
  118. package/dist/epic/markers.d.ts +123 -0
  119. package/dist/epic/merge-epoch.d.ts +90 -0
  120. package/dist/epic/next-wave-format.d.ts +101 -0
  121. package/dist/epic/next-wave.d.ts +102 -0
  122. package/dist/epic/phase-readiness.d.ts +221 -0
  123. package/dist/epic/plan-key.d.ts +104 -0
  124. package/dist/epic/plan-shaping-seam.d.ts +112 -0
  125. package/dist/epic/planning-signals.d.ts +38 -0
  126. package/dist/epic/report.d.ts +152 -0
  127. package/dist/epic/residue-commit.d.ts +151 -0
  128. package/dist/epic/scorecard.d.ts +130 -0
  129. package/dist/epic/shaping-sizing.d.ts +108 -0
  130. package/dist/epic/shaping-suggestions.d.ts +172 -0
  131. package/dist/epic/shaping.d.ts +112 -0
  132. package/dist/epic/sizing.d.ts +60 -0
  133. package/dist/epic/start.d.ts +187 -0
  134. package/dist/epic/task-landing.d.ts +83 -0
  135. package/dist/epic/wave-close.d.ts +112 -0
  136. package/dist/epic/wave-select.d.ts +95 -0
  137. package/dist/evaluation/contracts.d.ts +1 -1
  138. package/dist/evaluation/public-api.d.ts +1 -1
  139. package/dist/full-auto/policy.d.ts +12 -1
  140. package/dist/hooks/delegation-gate/worktree-isolation.d.ts +16 -0
  141. package/dist/hooks/delegation-gate.d.ts +11 -0
  142. package/dist/hooks/guardrails/index.d.ts +7 -0
  143. package/dist/hooks/promotion-evidence-store.d.ts +2 -2
  144. package/dist/index.js +814 -807
  145. package/dist/plan/manager.d.ts +6 -8
  146. package/dist/plan/parallel-verdict.d.ts +13 -2
  147. package/dist/retention/caps.d.ts +1 -1
  148. package/dist/review/contracts.d.ts +8 -1
  149. package/dist/services/config-doctor.d.ts +38 -0
  150. package/dist/session/snapshot-reader.d.ts +2 -0
  151. package/dist/session/snapshot-writer.d.ts +13 -2
  152. package/dist/state.d.ts +13 -18
  153. package/dist/tools/epic-next-wave.d.ts +20 -0
  154. package/dist/tools/epic-phase-review.d.ts +43 -0
  155. package/dist/tools/index.d.ts +2 -3
  156. package/dist/tools/manifest.d.ts +2 -3
  157. package/dist/tools/phase-complete.d.ts +5 -1
  158. package/dist/tools/save-plan.d.ts +7 -0
  159. package/dist/tools/tool-metadata.d.ts +4 -8
  160. package/dist/worktree/merge.d.ts +14 -1
  161. package/opencode-swarm.schema.json +273 -28
  162. package/package.json +1 -1
  163. package/dist/cli/curator-llm-factory-fx1qnpsh.js +0 -82
  164. package/dist/cli/guardrail-explain-g03manbh.js +0 -83
  165. package/dist/tools/epic-plan-waves.d.ts +0 -79
  166. package/dist/tools/epic-record-divergence.d.ts +0 -73
  167. package/dist/tools/epic-run-phase.d.ts +0 -179
  168. package/dist/turbo/epic/activation.d.ts +0 -193
  169. package/dist/turbo/epic/calibration-engine.d.ts +0 -88
  170. package/dist/turbo/epic/calibration.d.ts +0 -80
  171. package/dist/turbo/epic/divergence-recorder.d.ts +0 -123
  172. package/dist/turbo/epic/index.d.ts +0 -24
  173. package/dist/turbo/epic/promotion-evidence.d.ts +0 -42
  174. package/dist/turbo/epic/state.d.ts +0 -69
  175. package/dist/turbo/epic/task-commit.d.ts +0 -110
  176. package/dist/turbo/epic/upstream-commits.d.ts +0 -82
  177. package/dist/turbo/epic/wave-planner.d.ts +0 -83
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Epic Mode declared-scope resolution (v2 binding store only).
3
+ *
4
+ * `declare_scope` persists ONLY v2 scope bindings in the authoritative
5
+ * coordination store; nothing in production writes the legacy v1
6
+ * `.swarm/scopes/scope-<taskId>.json` projection any more. Every Epic
7
+ * scheduling consumer (`epic_next_wave`, the `/swarm epic start` sizing
8
+ * preview, `/swarm coupling`) therefore resolves declared scopes
9
+ * here, using the same upstream readers `src/plan/parallel-verdict.ts` uses:
10
+ *
11
+ * - ONE `readAuthoritativeScopeBindingSet` scan per call (#2532 hoisting),
12
+ * - `readDeclaredScopeFilesFromBindings` per task, pinned to the exact plan
13
+ * identity (`planId` + `planStructureHash`), live + unexpired only.
14
+ *
15
+ * The result maps EVERY requested task id to a file list — `[]` when the task
16
+ * has no live, agreeing, non-empty binding (undeclared, expired, declared
17
+ * against an older plan revision, store unreadable/overloaded, or no plan).
18
+ * Passing that map as the explicit `scopes` argument of the shared partition
19
+ * preflight (`runPartitionPreflight`) guarantees the preflight never falls
20
+ * back to its own v1 file read for an Epic task: upstream treats an explicit
21
+ * `[]` entry as "effectively undeclared", which then takes exactly the
22
+ * `files_touched` fallback that `require_declared_scope` permits — the same
23
+ * semantics the preflight applies to an undeclared task.
24
+ *
25
+ * Nothing here is write authority: these are scheduling reads.
26
+ */
27
+ import type { Plan } from '../config/plan-schema.js';
28
+ /**
29
+ * Resolve the live declared scope of each task from the v2 binding store.
30
+ *
31
+ * @param directory - Project root (binding-store location).
32
+ * @param plan - The plan the scopes must be pinned to. `null` (unreadable /
33
+ * invalid plan) resolves every task to `[]` without touching the store.
34
+ * @param taskIds - Tasks to resolve. Every id appears in the result.
35
+ * @returns `taskId -> files` (`[]` = no live declared scope).
36
+ */
37
+ export declare function resolveEpicDeclaredScopes(directory: string, plan: Plan | null, taskIds: readonly string[]): Record<string, string[]>;
@@ -0,0 +1,157 @@
1
+ /**
2
+ * Epic branch (Epic v2 C1b): the `epic-branch` commit policy.
3
+ *
4
+ * With `epic.commit_policy: 'epic-branch'` (the default) a git epic
5
+ * runs on its own branch `swarm/epic/<epicKey>`:
6
+ * - `/swarm epic start` checks it out (`git checkout -b`) right after the
7
+ * lifecycle row is created; the record names it only once the checkout
8
+ * succeeded (M-e).
9
+ * - every Epic step that commits or plans against HEAD first verifies HEAD
10
+ * is still that branch ({@link checkEpicBranch} → EPIC_BRANCH_MISMATCH,
11
+ * fail closed) — the user may have checked out something else meanwhile.
12
+ * - `/swarm epic close` lands it back onto the original branch
13
+ * (`--land squash|merge|none`, default squash: staged, UNCOMMITTED
14
+ * changes the user reviews and commits). The epic branch itself is never
15
+ * deleted by the plugin.
16
+ *
17
+ * Read-only git calls go through `gitExec` (`src/git/branch.ts`: array argv,
18
+ * explicit cwd, closed stdin, timeout, bounded output, GIT_TERMINAL_PROMPT=0).
19
+ * State-changing calls (checkout -b, checkout, merge, merge --squash,
20
+ * reset --merge, merge --abort, branch -D) go through `gitExecOnce`
21
+ * (`./git-once.ts`): the same spawn primitive and hardening but WITHOUT
22
+ * `gitExec`'s transient (ETIMEDOUT) retry — re-running a non-idempotent
23
+ * command after a timeout whose effect may already have happened would
24
+ * misreport the state. Callers inspect the actual repository state after any
25
+ * failure instead.
26
+ * Landing additionally pins a non-interactive editor (`GIT_EDITOR=true`,
27
+ * `GIT_MERGE_AUTOEDIT=no`, `--no-edit`) and disables commit signing for the
28
+ * `--land merge` commit, so no merge can wait on an editor or a pinentry.
29
+ * Repository hooks (pre-merge-commit, commit-msg) still run with stdin
30
+ * closed; a hook that rejects the merge is a landing failure, rolled back
31
+ * with `git merge --abort`.
32
+ */
33
+ import { type EpicConfigSource } from './config.js';
34
+ import { gitExecOnce } from './git-once.js';
35
+ import type { EpicCommitPolicy, EpicLandMode, EpicRecordV1 } from './lifecycle.js';
36
+ /** Branch namespace of every epic branch. */
37
+ export declare const EPIC_BRANCH_PREFIX = "swarm/epic/";
38
+ export declare const DEFAULT_EPIC_COMMIT_POLICY: EpicCommitPolicy;
39
+ export declare const DEFAULT_EPIC_LAND_MODE: EpicLandMode;
40
+ /**
41
+ * DI seam (AGENTS.md invariant 7). Restore in `afterEach`.
42
+ */
43
+ export declare const _internals: {
44
+ gitExec: (args: string[], cwd: string, env?: Record<string, string>) => string;
45
+ /** State-changing git commands: one attempt, no transient retry. */
46
+ gitExecOnce: typeof gitExecOnce;
47
+ };
48
+ /** `epic.commit_policy`, defaulting to `epic-branch`. */
49
+ export declare function resolveEpicCommitPolicy(config: EpicConfigSource): EpicCommitPolicy;
50
+ /** `swarm/epic/<epicKey>`; fail-closed against option injection. */
51
+ export declare function epicBranchName(epicKey: string): string;
52
+ /**
53
+ * Paths with uncommitted changes outside `.swarm/` (tracked or untracked).
54
+ * Throws when git fails. `exec` lets the start module keep its own seam.
55
+ */
56
+ export declare function listDirtyPathsOutsideSwarm(directory: string, exec?: (args: string[], cwd: string) => string): string[];
57
+ /**
58
+ * The checked-out branch name, or null for a detached HEAD. Throws when git
59
+ * fails (including an unborn branch, which has no commit to branch from).
60
+ */
61
+ export declare function readCurrentBranch(directory: string): string | null;
62
+ /** True when the local branch exists. Throws when git fails. */
63
+ export declare function localBranchExists(directory: string, branch: string): boolean;
64
+ /** `git checkout -b <branch>` from the current HEAD. Throws git's stderr. */
65
+ export declare function checkoutNewEpicBranch(directory: string, branch: string): void;
66
+ /** `git checkout <branch>`. Throws git's stderr. */
67
+ export declare function checkoutExistingBranch(directory: string, branch: string): void;
68
+ /**
69
+ * Best-effort undo of a just-created epic branch (start rollback), driven
70
+ * by the ACTUAL repository state (a failed or timed-out `checkout -b` may
71
+ * or may not have created the branch / switched HEAD): switch back to the
72
+ * original branch when HEAD is on the epic branch, then delete the epic
73
+ * branch only while its tip is still `baseCommit` (no work on it). Returns
74
+ * the problems, if any, for the refusal text.
75
+ */
76
+ export declare function undoEpicBranchCreate(directory: string, originalBranch: string, branch: string, baseCommit: string): string[];
77
+ export type EpicBranchCheck = {
78
+ ok: true;
79
+ } | {
80
+ ok: false;
81
+ code: 'EPIC_BRANCH_MISMATCH';
82
+ expected: string | null;
83
+ actual: string | null;
84
+ message: string;
85
+ };
86
+ /**
87
+ * Branch-drift guard (M-e): under the epic-branch policy HEAD must be the
88
+ * recorded epic branch. One `git rev-parse --abbrev-ref HEAD`; a git failure
89
+ * or an unrecorded branch fails closed. Current-branch / non-git epics
90
+ * always pass.
91
+ */
92
+ export declare function checkEpicBranch(directory: string, record: EpicRecordV1): EpicBranchCheck;
93
+ export type EpicLandingRefusal = 'dirty-worktree' | 'epic-branch-missing' | 'original-branch-missing' | 'detached-head' | 'git-failed';
94
+ export type EpicLandingPreflight = {
95
+ kind: 'not-applicable';
96
+ detail: string;
97
+ } | {
98
+ kind: 'already-landed';
99
+ detail: string;
100
+ }
101
+ /** On the original branch and the epic branch has no changes: no git work. */
102
+ | {
103
+ kind: 'nothing-to-land';
104
+ detail: string;
105
+ } | {
106
+ kind: 'ready';
107
+ currentBranch: string | null;
108
+ nothingToLand: boolean;
109
+ } | {
110
+ kind: 'refused';
111
+ reason: EpicLandingRefusal;
112
+ details: string[];
113
+ };
114
+ /** Where the repository actually ended up after a landing attempt. */
115
+ export interface EpicLandingAfterState {
116
+ branch: string | null;
117
+ /** No changes outside `.swarm/` and no merge in progress. */
118
+ clean: boolean;
119
+ }
120
+ export interface EpicLandingResult {
121
+ status: 'landed' | 'nothing-to-land' | 'conflict' | 'failed';
122
+ conflictFiles: string[];
123
+ detail: string;
124
+ after: EpicLandingAfterState;
125
+ }
126
+ /** Branch pair of an epic-branch epic, or null when there is nothing to land. */
127
+ export declare function epicBranchPair(record: EpicRecordV1): {
128
+ epicBranch: string;
129
+ originalBranch: string;
130
+ } | null;
131
+ /**
132
+ * Read-only landing preflight, run BEFORE the close marks the row `closing`
133
+ * (M-f). Refuses a dirty tree, a missing epic/original branch, and a
134
+ * detached HEAD whose commits are on neither branch; detects an
135
+ * already-landed (or nothing-to-land) state so a resumed close is idempotent.
136
+ */
137
+ export declare function preflightEpicLanding(directory: string, record: EpicRecordV1, mode: EpicLandMode): EpicLandingPreflight;
138
+ /**
139
+ * Land the epic branch per `mode` after a `ready` preflight: check out the
140
+ * original branch, then squash (staged, uncommitted) / merge (`--no-ff`
141
+ * merge commit) / nothing. On a conflict the attempt is rolled back —
142
+ * squash with `git reset --merge` (a squash writes no MERGE_HEAD, so
143
+ * `merge --abort` cannot be used), merge with `git merge --abort` — and the
144
+ * result reports where the repository actually ended up.
145
+ */
146
+ export declare function performEpicLanding(directory: string, record: EpicRecordV1, mode: EpicLandMode, preflight: Extract<EpicLandingPreflight, {
147
+ kind: 'ready';
148
+ }>): EpicLandingResult;
149
+ /**
150
+ * `--abandon` (and `/swarm close` finalization, MINOR 3): never land; switch
151
+ * back to the original branch when the tree is clean and no detached commit
152
+ * would be orphaned, so the epic branch is not left checked out. Never throws.
153
+ */
154
+ export declare function leaveEpicBranchOnAbandon(directory: string, record: EpicRecordV1): {
155
+ status: 'checked-out-original' | 'left-in-place';
156
+ detail: string;
157
+ };
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Epic v2 C4 — the delegation gate's single source of truth while an epic is
3
+ * open: coder dispatch is admitted ONLY for a task of the ACTIVE wave, and
4
+ * the wave's parallelism is decided from the scopes FROZEN into the wave
5
+ * record when `epic_next_wave` issued it (never from live bindings, which
6
+ * expire and follow plan revisions — critic M4).
7
+ *
8
+ * {@link computeEpicWaveVerdict} is THE verdict call: `epic_next_wave`
9
+ * asserts it is `all_disjoint` before it issues a multi-task wave, and the
10
+ * gate recomputes exactly the same call over the wave's unresolved tasks
11
+ * to decide `parallel`. The two can therefore never disagree.
12
+ *
13
+ * {@link resolveEpicDispatchPolicy} returns `null` when no epic is open for
14
+ * the current plan — the gate then runs its normal (non-Epic) logic,
15
+ * untouched. The gate calls it only after its own synchronous sentinel
16
+ * check (one `existsSync`), so Epic-off costs nothing more. Everything here
17
+ * is synchronous: no await, no microtask.
18
+ *
19
+ * Out of scope by construction (documented in docs/modes.md): PR-feedback
20
+ * coders (the gate returns before this seam — they carry no plan task), and
21
+ * reviewer / test_engineer / other agents (never routed through coder
22
+ * admission).
23
+ */
24
+ import type { Plan } from '../config/plan-schema.js';
25
+ import { type ComputeParallelVerdictOptions, computeParallelVerdict as computeParallelVerdict_import, type ParallelVerdict } from '../plan/parallel-verdict.js';
26
+ import { isEpicModeConfigEnabledForDirectory as isEpicModeConfigEnabledForDirectory_import } from './config-gate.js';
27
+ import { checkEpicBranch as checkEpicBranch_import } from './epic-branch.js';
28
+ import { type EpicWaveRecord, epicSentinelExists as epicSentinelExists_import, getOpenEpic as getOpenEpic_import, inspectEpic as inspectEpic_import } from './lifecycle.js';
29
+ /** DI seam (AGENTS.md invariant 7). Restore in `afterEach`. */
30
+ export declare const _internals: {
31
+ epicSentinelExists: typeof epicSentinelExists_import;
32
+ isEpicModeConfigEnabledForDirectory: typeof isEpicModeConfigEnabledForDirectory_import;
33
+ getOpenEpic: typeof getOpenEpic_import;
34
+ inspectEpic: typeof inspectEpic_import;
35
+ checkEpicBranch: typeof checkEpicBranch_import;
36
+ computeParallelVerdict: typeof computeParallelVerdict_import;
37
+ };
38
+ export type EpicDispatchRejectCode = 'EPIC_NO_ACTIVE_WAVE' | 'EPIC_TASK_NOT_IN_ACTIVE_WAVE' | 'EPIC_TASK_UNKNOWN' | 'EPIC_WAVE_SCOPE_DRIFT' | 'EPIC_STATE_UNREADABLE' | 'EPIC_CLOSING' | 'EPIC_BRANCH_MISMATCH';
39
+ export type EpicDispatchPolicy = {
40
+ kind: 'allow';
41
+ /** Concurrent coders of this wave are allowed (slot cap = maxConcurrent). */
42
+ parallel: boolean;
43
+ /** The coder runs in an isolated git worktree (degradation refused). */
44
+ isolate: boolean;
45
+ /** The epic's wave width (`record.config.maxParallel`). */
46
+ maxConcurrent: number;
47
+ } | {
48
+ kind: 'reject';
49
+ code: EpicDispatchRejectCode;
50
+ message: string;
51
+ };
52
+ /** The frozen part of a wave the verdict is computed from. */
53
+ export type EpicWaveScopes = Pick<EpicWaveRecord, 'files' | 'cochange'>;
54
+ /** The verdict options for a wave's frozen scopes (exported for parity tests). */
55
+ export declare function epicWaveVerdictOptions(plan: Plan, wave: EpicWaveScopes): ComputeParallelVerdictOptions;
56
+ /**
57
+ * THE wave verdict: `computeParallelVerdict` over `taskIds` with the wave's
58
+ * frozen scopes (and frozen co-change pairs when the wave recorded them).
59
+ * Used by `epic_next_wave` at issue and by the gate at dispatch.
60
+ */
61
+ export declare function computeEpicWaveVerdict(directory: string, plan: Plan, wave: EpicWaveScopes, taskIds: string[]): ParallelVerdict;
62
+ /**
63
+ * The open epic's dispatch policy for a coder of `incomingTaskId` whose
64
+ * live declared scope is `liveBindingFiles`; `null` when no epic is open for
65
+ * this plan (sentinel absent, no open row, Epic disabled by config, or the
66
+ * epic is orphaned/closing).
67
+ */
68
+ export declare function resolveEpicDispatchPolicy(directory: string, plan: Plan, incomingTaskId: string | null | undefined, liveBindingFiles: readonly string[] | null | undefined): EpicDispatchPolicy | null;
69
+ export type EpicGateAttribution =
70
+ /** No open epic, no active wave, or a single-task wave: upstream behaviour. */
71
+ {
72
+ kind: 'none';
73
+ }
74
+ /** Exactly one task of the active wave owns every checked file. */
75
+ | {
76
+ kind: 'task';
77
+ taskId: string;
78
+ }
79
+ /** Fail closed: credit no task, and tell the architect why. */
80
+ | {
81
+ kind: 'unattributable';
82
+ message: string;
83
+ };
84
+ /**
85
+ * The wave task a gate-tool run (`pre_check_batch`, …) belongs to while an
86
+ * epic is open.
87
+ *
88
+ * Upstream credits a gate run to the session's single `currentTaskId`, which
89
+ * holds whichever coder returned last. In a multi-task wave every Stage A run
90
+ * would then be credited to that one task: the task whose files were checked
91
+ * never reaches Stage B, and another task gets credit for a check it never
92
+ * had. Epic knows each wave task's frozen scope, so it credits the run to the
93
+ * ONE active-wave task whose frozen scope contains every checked file
94
+ * (containment as the write gates enforce it: a frozen directory covers the
95
+ * files beneath it). No files, no owner, several owners, or unreadable epic
96
+ * state credit nothing (fail closed). Sentinel-first: one `existsSync` when
97
+ * Epic is off.
98
+ */
99
+ export declare function resolveEpicGateTaskAttribution(directory: string, files: readonly string[] | null): EpicGateAttribution;
100
+ export type EpicPrFeedbackConflict = {
101
+ code: 'EPIC_PR_FEEDBACK_SCOPE_OVERLAP' | 'EPIC_STATE_UNREADABLE';
102
+ message: string;
103
+ };
104
+ /**
105
+ * Review F-007: a PR-feedback coder is admitted outside the wave gate (its
106
+ * authority is the authenticated PR-feedback declaration, not a plan task),
107
+ * so nothing stopped it from writing files a running wave task owns. While
108
+ * an epic has an ISSUED wave, refuse a PR-feedback coder whose declared files
109
+ * overlap any of that wave's frozen task scopes — in either direction (a
110
+ * declared directory that contains a frozen file overlaps too). Null when
111
+ * there is no overlap, no issued wave, or no open epic; unreadable epic state
112
+ * fails closed. Sentinel-first: one `existsSync` when Epic is off.
113
+ */
114
+ export declare function resolveEpicPrFeedbackConflict(directory: string, declaredFiles: readonly string[] | null | undefined): EpicPrFeedbackConflict | null;
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Single-attempt git primitives for Epic's state-changing commands.
3
+ *
4
+ * `gitExec` (`src/git/branch.ts`) retries transient spawn failures, which is
5
+ * right for reads but wrong for a command whose effect may already have
6
+ * happened when it timed out (checkout, merge, commit, update-ref): running
7
+ * it again would misreport the state. Epic therefore runs every
8
+ * state-changing git command exactly once through {@link gitExecOnce} — the
9
+ * same spawn seam and hardening as `gitExec` (array argv, explicit cwd,
10
+ * closed stdin, timeout, bounded output, `GIT_TERMINAL_PROMPT=0`) without
11
+ * the retry — and callers inspect the real repository state after a failure.
12
+ *
13
+ * {@link gitProbeExitCode} answers yes/no questions git encodes in its exit
14
+ * status (`merge-base --is-ancestor`): 0 and 1 are answers, anything else
15
+ * (or a spawn error) throws.
16
+ */
17
+ /**
18
+ * One bounded, non-interactive git spawn with NO transient retry. Throws
19
+ * git's stderr (or the spawn error) on failure.
20
+ */
21
+ export declare function gitExecOnce(args: string[], cwd: string, env?: Record<string, string>): string;
22
+ /**
23
+ * Exit status of a read-only git question (0 = yes, 1 = no). Throws on a
24
+ * spawn error, a timeout, or any other exit status.
25
+ */
26
+ export declare function gitProbeExitCode(args: string[], cwd: string): 0 | 1;
@@ -0,0 +1,176 @@
1
+ /**
2
+ * Epic v2 C6 — persistence of what epics learn (`learning.ts` is the math).
3
+ *
4
+ * Two levels:
5
+ * - the PROJECT PRIOR `.swarm/epic-prior/learning.json` (schema
6
+ * `epic-learning-v1`, paths and numbers only). It lives outside
7
+ * `.swarm/epic/`, so it survives `/swarm close` (not in
8
+ * `ACTIVE_STATE_DIRS_TO_CLEAN`); `/swarm epic prior reset`
9
+ * clears it;
10
+ * - the EPIC POSTERIOR `.swarm/epic/posterior.json` (schema
11
+ * `epic-posterior-v1`): at `/swarm epic start` a copy of the prior
12
+ * (`base`, whose digest the epic record keeps as `priorDigest`) plus the
13
+ * `increments` learned from this epic's closed waves. Every wave close
14
+ * applies the outcomes of each closed wave with a seq above
15
+ * `lastAppliedWaveSeq` exactly once (idempotent per wave; a crash between
16
+ * the close and the update is caught up by the next update or the close).
17
+ * At `/swarm epic close` (any outcome — also `/swarm close`'s
18
+ * finalization) the prior becomes
19
+ * bound(decay_per_epic × timeDecay(prior) ⊕ increments)
20
+ * once per epic instance (`mergedEpics`, the newest 50 report keys); an
21
+ * epic with no increments only marks itself merged (no decay).
22
+ *
23
+ * Planning reads the posterior of the open epic, else the prior, decayed by
24
+ * 0.5^floor(Δdays / half_life_days) since it was written (whole half-lives:
25
+ * full weight until a half-life has passed). An unreadable prior is
26
+ * never overwritten (planning runs without learned signals; the remedy is
27
+ * `/swarm epic prior reset`); an unreadable or foreign posterior
28
+ * is rebuilt from the prior and the epic record's outcomes.
29
+ *
30
+ * First start migration: Epic v1 `.swarm/epic/calibration.json`
31
+ * (`hotModuleAdditions` ⇒ α = 2) and `.swarm/epic/divergence.jsonl`
32
+ * (declared ⇒ exposures, undeclared ⇒ incidents + co-write edges, latest
33
+ * record per plan + task) are imported once into the prior, which records
34
+ * `importedFrom`; a reset keeps that marker (or writes a `reset` marker),
35
+ * so nothing is imported after an import or a reset. A `/swarm close`
36
+ * before the first start archives the v1 files away: nothing is imported.
37
+ *
38
+ * All writes are atomic `.swarm/`-contained writes; every state is bounded
39
+ * by `boundEpicLearning` (2000 files, 2000 edges).
40
+ */
41
+ import { z } from 'zod';
42
+ import { type EpicLearningSettings, type EpicLearningStats } from './learning.js';
43
+ import type { EpicRecordV1 } from './lifecycle.js';
44
+ export declare const EPIC_PRIOR_LEARNING_RELATIVE_PATH: string;
45
+ /** The prior's path as shown to users (POSIX separators). */
46
+ export declare const EPIC_PRIOR_LEARNING_DISPLAY_PATH = ".swarm/epic-prior/learning.json";
47
+ export declare const EPIC_POSTERIOR_RELATIVE_PATH: string;
48
+ /** Report keys of the epics already merged into the prior (newest kept). */
49
+ export declare const EPIC_PRIOR_MERGED_EPICS_KEEP = 50;
50
+ /** α seeded for each Epic v1 `hotModuleAdditions` entry. */
51
+ export declare const EPIC_LEGACY_HOT_MODULE_ALPHA = 2;
52
+ declare const importedFromSchema: z.ZodObject<{
53
+ at: z.ZodString;
54
+ source: z.ZodEnum<{
55
+ reset: "reset";
56
+ "epic-v1-import": "epic-v1-import";
57
+ }>;
58
+ calibrationHotModules: z.ZodNumber;
59
+ divergenceRecords: z.ZodNumber;
60
+ }, z.core.$strict>;
61
+ export type EpicPriorImport = z.infer<typeof importedFromSchema>;
62
+ /** The persisted project prior, decoded. */
63
+ export interface EpicPrior {
64
+ updatedAt: string;
65
+ importedFrom: EpicPriorImport | null;
66
+ mergedEpics: string[];
67
+ stats: EpicLearningStats;
68
+ }
69
+ export interface EpicPosterior {
70
+ epicKey: string;
71
+ token: string;
72
+ priorDigest: string | null;
73
+ createdAt: string;
74
+ lastAppliedWaveSeq: number;
75
+ base: {
76
+ updatedAt: string;
77
+ stats: EpicLearningStats;
78
+ };
79
+ increments: EpicLearningStats;
80
+ }
81
+ export type EpicPriorRead = {
82
+ status: 'absent';
83
+ digest: null;
84
+ } | {
85
+ status: 'ok';
86
+ prior: EpicPrior;
87
+ digest: string;
88
+ } | {
89
+ status: 'unreadable';
90
+ reason: string;
91
+ digest: null;
92
+ };
93
+ /** Read the project prior (no decay applied). Never throws. */
94
+ export declare function readEpicPrior(directory: string): EpicPriorRead;
95
+ /** Read the epic posterior; null when absent or unreadable. Never throws. */
96
+ export declare function readEpicPosterior(directory: string): EpicPosterior | null;
97
+ export type EpicLearningSource = 'posterior' | 'prior' | 'none' | 'prior-unreadable';
98
+ /** Identifies one epic instance (`epicKey` repeats when a plan restarts). */
99
+ export type EpicIdentity = Pick<EpicRecordV1, 'epicKey' | 'token'>;
100
+ export interface EpicLearningView {
101
+ source: EpicLearningSource;
102
+ stats: EpicLearningStats;
103
+ }
104
+ /**
105
+ * What the planner learns from right now: the posterior of the open epic
106
+ * `epic` (time-decayed base ⊕ increments), else the time-decayed prior.
107
+ * An unreadable prior yields no learned signal (warned once). Never throws.
108
+ */
109
+ export declare function loadEpicLearningView(directory: string, epic: EpicIdentity | null, settings: EpicLearningSettings, nowMs: number): EpicLearningView;
110
+ /**
111
+ * `/swarm epic start`, after the epic was opened: the posterior starts as a
112
+ * copy of the prior snapshot the start read (its digest is the record's
113
+ * `priorDigest`). Throws on a write failure (the caller warns; the first
114
+ * wave close rebuilds it).
115
+ */
116
+ export declare function initEpicPosterior(directory: string, record: Pick<EpicRecordV1, 'epicKey' | 'token' | 'priorDigest' | 'startedAt'>, snapshot: EpicPriorRead): void;
117
+ export interface EpicWaveLearningResult {
118
+ /** Wave seqs whose outcomes this call applied (empty: nothing new). */
119
+ appliedWaves: number[];
120
+ }
121
+ /**
122
+ * Apply the outcomes of every CLOSED wave of `record` not applied yet
123
+ * (seq > `lastAppliedWaveSeq`) to the posterior — exactly once per wave.
124
+ * Throws on a write failure (the caller decides; the next call catches up).
125
+ */
126
+ export declare function applyClosedWavesToPosterior(directory: string, record: EpicRecordV1, settings: EpicLearningSettings): EpicWaveLearningResult;
127
+ export type EpicPriorMergeStatus = 'merged' | 'already-merged' | 'nothing-to-learn' | 'disabled' | 'prior-unreadable' | 'failed';
128
+ export interface EpicPriorMergeResult {
129
+ status: EpicPriorMergeStatus;
130
+ /** Files / co-write edges this epic contributed. */
131
+ learnedFiles: number;
132
+ learnedEdges: number;
133
+ /** Hot files of the prior after the merge (≤ 10). */
134
+ hotFiles: string[];
135
+ detail: string;
136
+ }
137
+ /**
138
+ * `/swarm epic close` (any outcome): catch the posterior up with the
139
+ * record, then prior := bound(decay_per_epic × timeDecay(prior) ⊕
140
+ * increments), once per epic instance (`reportKey`). The posterior is
141
+ * removed afterwards. An epic that learned nothing leaves the statistics
142
+ * (and their write time) untouched and is only marked merged. Never throws.
143
+ */
144
+ export declare function mergeEpicPosteriorIntoPrior(directory: string, record: EpicRecordV1, reportKey: string, settings: EpicLearningSettings, nowMs: number): EpicPriorMergeResult;
145
+ export type EpicPriorResetResult = {
146
+ status: 'reset';
147
+ hadPrior: boolean;
148
+ } | {
149
+ status: 'failed';
150
+ detail: string;
151
+ };
152
+ /**
153
+ * `/swarm epic prior reset`: the prior becomes empty. The
154
+ * `importedFrom` marker survives (a v1 import is never repeated); an open
155
+ * epic keeps its posterior and merges into the empty prior at close.
156
+ */
157
+ export declare function resetEpicPrior(directory: string, nowMs: number): EpicPriorResetResult;
158
+ export type EpicLegacyImportResult = {
159
+ status: 'imported';
160
+ calibrationHotModules: number;
161
+ divergenceRecords: number;
162
+ } | {
163
+ status: 'already-imported' | 'nothing-to-import' | 'prior-unreadable';
164
+ } | {
165
+ status: 'failed';
166
+ detail: string;
167
+ };
168
+ /**
169
+ * Import the Epic v1 learning files into the prior once (see the module
170
+ * header). Runs at `/swarm epic start`; writes only when there is
171
+ * something to import. Never throws.
172
+ */
173
+ export declare function importLegacyEpicCalibrationOnce(directory: string, nowMs: number): EpicLegacyImportResult;
174
+ /** One close-output line; always states that the project prior is kept. */
175
+ export declare function describeEpicPriorMerge(result: EpicPriorMergeResult): string;
176
+ export {};