opencode-swarm 7.139.5 → 7.139.7

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 (87) hide show
  1. package/.opencode/skills/brainstorm/SKILL.md +8 -101
  2. package/.opencode/skills/execute/SKILL.md +10 -9
  3. package/.opencode/skills/issue-ingest/SKILL.md +1 -1
  4. package/.opencode/skills/plan/SKILL.md +40 -97
  5. package/.opencode/skills/specify/SKILL.md +3 -89
  6. package/.opencode/skills/swarm-pr-review/SKILL.md +62 -6
  7. package/.opencode/skills/swarm-pr-review/references/prompt-templates.md +44 -6
  8. package/dist/agents/architect.d.ts +2 -3
  9. package/dist/agents/explorer.d.ts +1 -1
  10. package/dist/cli/{config-doctor-jgv6h8hd.js → config-doctor-pc48vfzg.js} +3 -3
  11. package/dist/cli/{core-aj5g7an6.js → core-jtj9j6z5.js} +2 -2
  12. package/dist/cli/{curation-policy-9a8xmh4x.js → curation-policy-2rh3zysm.js} +6 -6
  13. package/dist/cli/{curator-llm-factory-7c5s2cdm.js → curator-llm-factory-de10hnhj.js} +28 -28
  14. package/dist/cli/{curator-xhzdk0qy.js → curator-tm9rp9yx.js} +28 -28
  15. package/dist/cli/{evidence-summary-service-g82bsjqa.js → evidence-summary-service-nfdc1ht9.js} +9 -8
  16. package/dist/cli/{explorer-swjc297z.js → explorer-e9h66egs.js} +1 -1
  17. package/dist/cli/{gate-evidence-72cgrvs2.js → gate-evidence-46qeyfc1.js} +4 -4
  18. package/dist/cli/{guardrail-explain-vw1kz4k1.js → guardrail-explain-979dxbd5.js} +29 -29
  19. package/dist/cli/{guardrail-log-j91e1s6q.js → guardrail-log-wxa8janm.js} +4 -4
  20. package/dist/cli/{hive-promoter-5rk01jea.js → hive-promoter-w4ezne1c.js} +28 -28
  21. package/dist/cli/{index-sd657fkp.js → index-1m1ksyqt.js} +1 -1
  22. package/dist/cli/{index-hhgbbceq.js → index-3r61js2q.js} +4 -4
  23. package/dist/cli/{index-gee0y2d8.js → index-3w0kxqyy.js} +2 -2
  24. package/dist/cli/{index-wqk50bv0.js → index-4tjk6k4a.js} +4 -4
  25. package/dist/cli/{index-9xczz1yh.js → index-6d60gctc.js} +2 -2
  26. package/dist/cli/{index-0cyqmr36.js → index-7f4jhdhw.js} +2 -2
  27. package/dist/cli/{index-z0tt5j2s.js → index-92j14gdt.js} +2 -2
  28. package/dist/cli/{index-xmhhhjjw.js → index-adyxbmt5.js} +3 -3
  29. package/dist/cli/{index-j77frjq8.js → index-b9rd29ge.js} +1 -1
  30. package/dist/cli/{index-6nzmp937.js → index-brh827g0.js} +442 -27
  31. package/dist/cli/{index-9rbwfydj.js → index-cp592623.js} +6 -6
  32. package/dist/cli/{index-1kz6da87.js → index-d181x9zr.js} +2 -1
  33. package/dist/cli/{index-jtg7gtky.js → index-f7wq4q9r.js} +5 -5
  34. package/dist/cli/{index-6r522srn.js → index-h2waehqq.js} +1 -1
  35. package/dist/cli/{index-6z0a940s.js → index-hbnsmnb0.js} +2 -2
  36. package/dist/cli/{index-s0qc4gj6.js → index-hfvxhk4z.js} +1 -1
  37. package/dist/cli/{index-w9tezcdd.js → index-j0gy1ggg.js} +2 -2
  38. package/dist/cli/{index-0xkjhgrf.js → index-jfjnn408.js} +1 -1
  39. package/dist/cli/{index-r2bggfra.js → index-k4tcdnvd.js} +3 -3
  40. package/dist/cli/{index-6rxgb5r9.js → index-kbfsp07d.js} +16 -6
  41. package/dist/cli/{index-5z2xps64.js → index-n0pja0qs.js} +2 -2
  42. package/dist/cli/{index-px7z3r1c.js → index-n4x9racs.js} +3 -3
  43. package/dist/cli/{index-2ga0zt4s.js → index-nxs7r6sd.js} +1532 -972
  44. package/dist/cli/{index-4wjk296r.js → index-r4t92q32.js} +2 -2
  45. package/dist/cli/{index-33skgk1a.js → index-rs1f1bh0.js} +1 -1
  46. package/dist/cli/{index-h2zm446q.js → index-rvr1p73g.js} +2 -2
  47. package/dist/cli/{index-rsrx77fg.js → index-ry0hgg3m.js} +3 -3
  48. package/dist/cli/{index-9jr92bff.js → index-sd7ajhb5.js} +1 -1
  49. package/dist/cli/{index-fb11cwqr.js → index-ssjm3458.js} +2 -2
  50. package/dist/cli/{index-6mjf4vr1.js → index-v2xrer41.js} +16 -29
  51. package/dist/cli/{index-ngfw3sd4.js → index-vx6mwms2.js} +30 -30
  52. package/dist/cli/{index-nrx08gcr.js → index-yyyk63kk.js} +2 -2
  53. package/dist/cli/index.js +28 -28
  54. package/dist/cli/{knowledge-escalator-rhycg2rh.js → knowledge-escalator-kyejsd7z.js} +7 -7
  55. package/dist/cli/{knowledge-events-yjkszs87.js → knowledge-events-n5z7w8x2.js} +5 -5
  56. package/dist/cli/{knowledge-link-bmbatt8s.js → knowledge-link-q46k9adx.js} +4 -4
  57. package/dist/cli/{knowledge-store-qe7geq48.js → knowledge-store-s65wnc1m.js} +5 -5
  58. package/dist/cli/{knowledge-validator-rwptbkpz.js → knowledge-validator-cgtmhqya.js} +8 -8
  59. package/dist/cli/{pending-delegations-7ayryxbg.js → pending-delegations-qzrx38y9.js} +3 -3
  60. package/dist/cli/{pr-subscriptions-w5y08ebc.js → pr-subscriptions-5s59h4dd.js} +3 -3
  61. package/dist/cli/{runner-h7vmgkth.js → runner-y78d7ryb.js} +5 -5
  62. package/dist/cli/{scan-cursor-v1zkh6st.js → scan-cursor-6z3rbdxp.js} +6 -6
  63. package/dist/cli/{schema-9v8wgmxf.js → schema-fvj8smjz.js} +2 -2
  64. package/dist/cli/{scope-persistence-tj6qway9.js → scope-persistence-dr1msqxw.js} +5 -5
  65. package/dist/cli/{skill-generator-a91bxwv2.js → skill-generator-9kzsefn1.js} +9 -9
  66. package/dist/cli/{telemetry-62cn5t24.js → telemetry-sc57xtb0.js} +1 -1
  67. package/dist/cli/{worktree-collision-ownership-hs5c2qzp.js → worktree-collision-ownership-7a8e6gr9.js} +3 -3
  68. package/dist/config/plan-schema.d.ts +2 -0
  69. package/dist/db/index.d.ts +1 -1
  70. package/dist/db/qa-gate-profile.d.ts +86 -2
  71. package/dist/db/task-checkpoint-receipt.d.ts +27 -0
  72. package/dist/hooks/pr-workflow-gate.d.ts +2 -2
  73. package/dist/index.js +453 -363
  74. package/dist/plan/manager.d.ts +4 -0
  75. package/dist/qa-gate/recovery.d.ts +6 -0
  76. package/dist/services/export-service.d.ts +2 -6
  77. package/dist/services/handoff-service.d.ts +2 -6
  78. package/dist/tools/checkpoint.d.ts +17 -7
  79. package/dist/tools/dispatch-lanes.d.ts +3 -6
  80. package/dist/tools/get-approved-plan.d.ts +8 -6
  81. package/dist/tools/get-qa-gate-profile.d.ts +7 -3
  82. package/dist/tools/phase-complete/gates/gate-helpers.d.ts +1 -0
  83. package/dist/tools/qa-gate-plan-identity.d.ts +28 -0
  84. package/dist/tools/save-plan.d.ts +3 -14
  85. package/dist/tools/set-qa-gates.d.ts +13 -8
  86. package/dist/tools/write-drift-evidence.d.ts +2 -0
  87. package/package.json +1 -1
@@ -3,7 +3,10 @@ import {
3
3
  EvaluationRunV1Schema,
4
4
  GateAuditResultV1Schema,
5
5
  getProtectedEvaluationRunIds
6
- } from "./index-nrx08gcr.js";
6
+ } from "./index-yyyk63kk.js";
7
+ import {
8
+ loadDatabaseCtor
9
+ } from "./index-134d35c1.js";
7
10
  import {
8
11
  readEffectiveSpecSync
9
12
  } from "./index-9ss2m4rs.js";
@@ -13,6 +16,7 @@ import {
13
16
  computeCurrentPlanHash,
14
17
  computePlanHash,
15
18
  derivePlanId,
19
+ derivePlanIdentityHash,
16
20
  getLatestLedgerSeq,
17
21
  initLedger,
18
22
  ledgerExists,
@@ -22,7 +26,7 @@ import {
22
26
  replayFromLedgerWithStatus,
23
27
  takeSnapshotEvent,
24
28
  takeSnapshotWithRetry
25
- } from "./index-6rxgb5r9.js";
29
+ } from "./index-kbfsp07d.js";
26
30
  import {
27
31
  getWorktreeMergeFailure
28
32
  } from "./index-b8zgtz81.js";
@@ -35,7 +39,7 @@ import {
35
39
  readTaskEvidence,
36
40
  readTaskEvidenceRaw,
37
41
  sanitizeTaskId
38
- } from "./index-5z2xps64.js";
42
+ } from "./index-n0pja0qs.js";
39
43
  import {
40
44
  _internals,
41
45
  buildDefaultPolicy,
@@ -49,8 +53,9 @@ import {
49
53
  } from "./index-ed5ykebp.js";
50
54
  import {
51
55
  assertProjectRoot,
56
+ tryAcquireLock,
52
57
  withEvidenceLock
53
- } from "./index-s0qc4gj6.js";
58
+ } from "./index-hfvxhk4z.js";
54
59
  import {
55
60
  bunHash,
56
61
  bunWrite,
@@ -63,7 +68,7 @@ import {
63
68
  } from "./index-gprxjmm7.js";
64
69
  import {
65
70
  PlanSchema
66
- } from "./index-1kz6da87.js";
71
+ } from "./index-d181x9zr.js";
67
72
  import {
68
73
  criticalWarn,
69
74
  error,
@@ -72,7 +77,7 @@ import {
72
77
  } from "./index-zgwm4ryv.js";
73
78
  import {
74
79
  emit
75
- } from "./index-rsrx77fg.js";
80
+ } from "./index-ry0hgg3m.js";
76
81
  import {
77
82
  ZodError,
78
83
  init_zod
@@ -972,9 +977,9 @@ var init_executor = __esm(() => {
972
977
  import {
973
978
  closeSync,
974
979
  copyFileSync,
975
- existsSync as existsSync3,
980
+ existsSync as existsSync4,
976
981
  fsyncSync,
977
- mkdirSync as mkdirSync3,
982
+ mkdirSync as mkdirSync4,
978
983
  openSync,
979
984
  readdirSync,
980
985
  renameSync as renameSync2,
@@ -984,6 +989,342 @@ import {
984
989
  import * as fsPromises from "fs/promises";
985
990
  import * as path8 from "path";
986
991
 
992
+ // src/db/task-checkpoint-receipt.ts
993
+ import { createHash } from "crypto";
994
+
995
+ // src/db/project-db.ts
996
+ import { existsSync, mkdirSync } from "fs";
997
+ import { join, resolve } from "path";
998
+ var MIGRATIONS = [
999
+ {
1000
+ version: 1,
1001
+ name: "create_project_constraints",
1002
+ sql: `CREATE TABLE project_constraints (
1003
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
1004
+ constraint_type TEXT NOT NULL,
1005
+ content TEXT NOT NULL,
1006
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
1007
+ )`
1008
+ },
1009
+ {
1010
+ version: 2,
1011
+ name: "create_qa_gate_profile",
1012
+ sql: `CREATE TABLE qa_gate_profile (
1013
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
1014
+ plan_id TEXT NOT NULL UNIQUE,
1015
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
1016
+ project_type TEXT,
1017
+ gates TEXT NOT NULL DEFAULT '{}',
1018
+ locked_at TEXT,
1019
+ locked_by_snapshot_seq INTEGER
1020
+ )`
1021
+ },
1022
+ {
1023
+ version: 3,
1024
+ name: "create_qa_gate_profile_immutability_trigger",
1025
+ sql: `CREATE TRIGGER IF NOT EXISTS trg_qa_gate_profile_no_update_after_lock
1026
+ BEFORE UPDATE ON qa_gate_profile
1027
+ WHEN OLD.locked_at IS NOT NULL
1028
+ BEGIN
1029
+ SELECT RAISE(ABORT, 'qa_gate_profile row is locked and cannot be modified after critic approval');
1030
+ END`
1031
+ },
1032
+ {
1033
+ version: 4,
1034
+ name: "add_qa_gate_profile_raw_swarm",
1035
+ sql: "ALTER TABLE qa_gate_profile ADD COLUMN raw_swarm TEXT"
1036
+ },
1037
+ {
1038
+ version: 5,
1039
+ name: "add_qa_gate_profile_raw_title",
1040
+ sql: "ALTER TABLE qa_gate_profile ADD COLUMN raw_title TEXT"
1041
+ },
1042
+ {
1043
+ version: 6,
1044
+ name: "add_qa_gate_profile_identity_hash",
1045
+ sql: "ALTER TABLE qa_gate_profile ADD COLUMN identity_hash TEXT"
1046
+ },
1047
+ {
1048
+ version: 7,
1049
+ name: "create_qa_gate_profile_identity_hash_index",
1050
+ sql: `CREATE UNIQUE INDEX IF NOT EXISTS idx_qa_gate_profile_identity_hash
1051
+ ON qa_gate_profile(identity_hash)
1052
+ WHERE identity_hash IS NOT NULL`
1053
+ },
1054
+ {
1055
+ version: 8,
1056
+ name: "create_qa_gate_profile_identity",
1057
+ sql: `CREATE TABLE qa_gate_profile_identity (
1058
+ identity_hash TEXT PRIMARY KEY,
1059
+ profile_id INTEGER NOT NULL UNIQUE REFERENCES qa_gate_profile(id) ON DELETE CASCADE,
1060
+ raw_swarm TEXT NOT NULL,
1061
+ raw_title TEXT NOT NULL,
1062
+ readable_plan_id TEXT NOT NULL
1063
+ )`
1064
+ },
1065
+ {
1066
+ version: 9,
1067
+ name: "create_qa_gate_profile_identity_readable_plan_index",
1068
+ sql: `CREATE INDEX idx_qa_gate_profile_identity_readable_plan_id
1069
+ ON qa_gate_profile_identity(readable_plan_id)`
1070
+ },
1071
+ {
1072
+ version: 10,
1073
+ name: "backfill_qa_gate_profile_identity_from_legacy_columns",
1074
+ sql: `INSERT INTO qa_gate_profile_identity (
1075
+ identity_hash,
1076
+ profile_id,
1077
+ raw_swarm,
1078
+ raw_title,
1079
+ readable_plan_id
1080
+ )
1081
+ SELECT
1082
+ identity_hash,
1083
+ id,
1084
+ raw_swarm,
1085
+ raw_title,
1086
+ plan_id
1087
+ FROM qa_gate_profile
1088
+ WHERE identity_hash IS NOT NULL
1089
+ AND raw_swarm IS NOT NULL
1090
+ AND raw_title IS NOT NULL
1091
+ AND NOT EXISTS (
1092
+ SELECT 1
1093
+ FROM qa_gate_profile_identity AS qi
1094
+ WHERE qi.profile_id = qa_gate_profile.id
1095
+ )`
1096
+ },
1097
+ {
1098
+ version: 11,
1099
+ name: "create_task_checkpoint_receipt",
1100
+ sql: `CREATE TABLE task_checkpoint_receipt (
1101
+ plan_identity_hash TEXT NOT NULL,
1102
+ task_id TEXT NOT NULL,
1103
+ label TEXT NOT NULL,
1104
+ state TEXT NOT NULL CHECK(state IN ('pending', 'committed', 'logged')),
1105
+ sha TEXT,
1106
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
1107
+ updated_at TEXT NOT NULL DEFAULT (datetime('now')),
1108
+ PRIMARY KEY(plan_identity_hash, task_id)
1109
+ )`
1110
+ },
1111
+ {
1112
+ version: 12,
1113
+ name: "add_task_checkpoint_receipt_completion_generation",
1114
+ sql: `ALTER TABLE task_checkpoint_receipt
1115
+ ADD COLUMN generation INTEGER NOT NULL DEFAULT 1;
1116
+ ALTER TABLE task_checkpoint_receipt
1117
+ ADD COLUMN completion_active INTEGER NOT NULL DEFAULT 1
1118
+ CHECK(completion_active IN (0, 1))`
1119
+ },
1120
+ {
1121
+ version: 13,
1122
+ name: "bind_task_checkpoint_receipt_to_completion_ledger_seq",
1123
+ sql: `ALTER TABLE task_checkpoint_receipt
1124
+ ADD COLUMN completion_ledger_seq INTEGER
1125
+ CHECK(completion_ledger_seq IS NULL OR completion_ledger_seq >= 0)`
1126
+ }
1127
+ ];
1128
+ var _projectDbs = new Map;
1129
+ function runProjectMigrations(db) {
1130
+ db.run(`CREATE TABLE IF NOT EXISTS schema_migrations (
1131
+ version INTEGER PRIMARY KEY,
1132
+ name TEXT NOT NULL,
1133
+ applied_at TEXT NOT NULL DEFAULT (datetime('now'))
1134
+ )`);
1135
+ const row = db.query("SELECT MAX(version) as version FROM schema_migrations").get();
1136
+ const currentVersion = row?.version ?? 0;
1137
+ for (const migration of MIGRATIONS) {
1138
+ if (migration.version <= currentVersion)
1139
+ continue;
1140
+ const apply = db.transaction(() => {
1141
+ db.run(migration.sql);
1142
+ db.run("INSERT INTO schema_migrations (version, name) VALUES (?, ?)", [
1143
+ migration.version,
1144
+ migration.name
1145
+ ]);
1146
+ });
1147
+ apply();
1148
+ }
1149
+ }
1150
+ function projectDbPath(directory) {
1151
+ return join(resolve(directory), ".swarm", "swarm.db");
1152
+ }
1153
+ function projectDbExists(directory) {
1154
+ return existsSync(projectDbPath(directory));
1155
+ }
1156
+ function getProjectDb(directory) {
1157
+ const key = resolve(directory);
1158
+ const existing = _projectDbs.get(key);
1159
+ if (existing)
1160
+ return existing;
1161
+ const swarmDir = join(key, ".swarm");
1162
+ mkdirSync(swarmDir, { recursive: true });
1163
+ const Db = loadDatabaseCtor();
1164
+ const db = new Db(join(swarmDir, "swarm.db"));
1165
+ db.run("PRAGMA journal_mode = WAL;");
1166
+ db.run("PRAGMA synchronous = NORMAL;");
1167
+ db.run("PRAGMA busy_timeout = 5000;");
1168
+ db.run("PRAGMA foreign_keys = ON;");
1169
+ runProjectMigrations(db);
1170
+ _projectDbs.set(key, db);
1171
+ return db;
1172
+ }
1173
+ function closeProjectDb(directory) {
1174
+ const key = resolve(directory);
1175
+ const db = _projectDbs.get(key);
1176
+ if (db) {
1177
+ db.close();
1178
+ _projectDbs.delete(key);
1179
+ }
1180
+ }
1181
+
1182
+ // src/db/task-checkpoint-receipt.ts
1183
+ var TASK_COMPLETION_HASH_LENGTH = 16;
1184
+ function taskReceiptLabelSuffix(planIdentityHash, taskId, generation) {
1185
+ return createHash("sha256").update(JSON.stringify(generation === 1 ? [planIdentityHash, taskId] : [planIdentityHash, taskId, generation]), "utf8").digest("hex").slice(0, TASK_COMPLETION_HASH_LENGTH);
1186
+ }
1187
+ function scrubTaskIdForCheckpointLabel(taskId) {
1188
+ const scrubbed = taskId.replace(/[^a-zA-Z0-9._-]/g, "_");
1189
+ return scrubbed.length > 32 ? scrubbed.slice(0, 32) : scrubbed;
1190
+ }
1191
+ function buildTaskCompletionDescriptor(planIdentityHash, taskId, generation) {
1192
+ if (!Number.isSafeInteger(generation) || generation < 1) {
1193
+ throw new Error(`Invalid task checkpoint generation: ${generation}`);
1194
+ }
1195
+ const suffix = taskReceiptLabelSuffix(planIdentityHash, taskId, generation);
1196
+ const safeTaskId = scrubTaskIdForCheckpointLabel(taskId) || "task";
1197
+ return {
1198
+ planIdentityHash,
1199
+ taskId,
1200
+ generation,
1201
+ label: `task-${safeTaskId}-complete-${suffix}`,
1202
+ subject: generation === 1 ? `checkpoint(task-complete ${suffix} plan ${planIdentityHash}): ${safeTaskId}` : `checkpoint(task-complete ${suffix} gen ${generation} plan ${planIdentityHash}): ${safeTaskId}`
1203
+ };
1204
+ }
1205
+ function readTaskCheckpointReceipt(directory, planIdentityHash, taskId) {
1206
+ if (!projectDbExists(directory))
1207
+ return null;
1208
+ return getProjectDb(directory).query("SELECT * FROM task_checkpoint_receipt WHERE plan_identity_hash = ? AND task_id = ?").get(planIdentityHash, taskId) ?? null;
1209
+ }
1210
+ function ensureTaskCheckpointReceipt(directory, planIdentityHash, taskId, completionLedgerSeq) {
1211
+ if (!Number.isSafeInteger(completionLedgerSeq) || completionLedgerSeq < 0) {
1212
+ throw new Error(`Invalid task checkpoint completion ledger seq: ${completionLedgerSeq}`);
1213
+ }
1214
+ const db = getProjectDb(directory);
1215
+ const initial = buildTaskCompletionDescriptor(planIdentityHash, taskId, 1);
1216
+ db.transaction(() => {
1217
+ db.run(`INSERT OR IGNORE INTO task_checkpoint_receipt
1218
+ (plan_identity_hash, task_id, label, state, sha, generation,
1219
+ completion_active, completion_ledger_seq)
1220
+ VALUES (?, ?, ?, 'pending', NULL, 1, 1, ?)`, [planIdentityHash, taskId, initial.label, completionLedgerSeq]);
1221
+ const row2 = db.query("SELECT * FROM task_checkpoint_receipt WHERE plan_identity_hash = ? AND task_id = ?").get(planIdentityHash, taskId);
1222
+ if (!row2)
1223
+ return;
1224
+ if (row2.completion_ledger_seq === null) {
1225
+ db.run(`UPDATE task_checkpoint_receipt
1226
+ SET completion_ledger_seq = ?, completion_active = 1,
1227
+ updated_at = datetime('now')
1228
+ WHERE plan_identity_hash = ? AND task_id = ?
1229
+ AND completion_ledger_seq IS NULL`, [completionLedgerSeq, planIdentityHash, taskId]);
1230
+ return;
1231
+ }
1232
+ if (row2.completion_ledger_seq !== completionLedgerSeq) {
1233
+ const nextGeneration = row2.generation + 1;
1234
+ const descriptor = buildTaskCompletionDescriptor(planIdentityHash, taskId, nextGeneration);
1235
+ db.run(`UPDATE task_checkpoint_receipt
1236
+ SET label = ?, state = 'pending', sha = NULL, generation = ?,
1237
+ completion_active = 1, completion_ledger_seq = ?,
1238
+ updated_at = datetime('now')
1239
+ WHERE plan_identity_hash = ? AND task_id = ?
1240
+ AND completion_ledger_seq = ?`, [
1241
+ descriptor.label,
1242
+ nextGeneration,
1243
+ completionLedgerSeq,
1244
+ planIdentityHash,
1245
+ taskId,
1246
+ row2.completion_ledger_seq
1247
+ ]);
1248
+ return;
1249
+ }
1250
+ if (row2.completion_active === 0) {
1251
+ db.run(`UPDATE task_checkpoint_receipt
1252
+ SET completion_active = 1, updated_at = datetime('now')
1253
+ WHERE plan_identity_hash = ? AND task_id = ?
1254
+ AND completion_ledger_seq = ? AND completion_active = 0`, [planIdentityHash, taskId, completionLedgerSeq]);
1255
+ }
1256
+ })();
1257
+ const row = readTaskCheckpointReceipt(directory, planIdentityHash, taskId);
1258
+ if (!row) {
1259
+ throw new Error(`Failed to create or read task checkpoint receipt for ${taskId}`);
1260
+ }
1261
+ return row;
1262
+ }
1263
+ function activateTaskCheckpointReceipt(directory, planIdentityHash, taskId) {
1264
+ if (!projectDbExists(directory))
1265
+ return null;
1266
+ const db = getProjectDb(directory);
1267
+ db.transaction(() => {
1268
+ const row = db.query("SELECT * FROM task_checkpoint_receipt WHERE plan_identity_hash = ? AND task_id = ?").get(planIdentityHash, taskId);
1269
+ if (!row || row.completion_active === 1)
1270
+ return;
1271
+ const descriptor = buildTaskCompletionDescriptor(planIdentityHash, taskId, row.generation);
1272
+ db.run(`UPDATE task_checkpoint_receipt
1273
+ SET label = ?, state = 'pending', sha = NULL, completion_active = 1,
1274
+ updated_at = datetime('now')
1275
+ WHERE plan_identity_hash = ? AND task_id = ? AND completion_active = 0`, [descriptor.label, planIdentityHash, taskId]);
1276
+ })();
1277
+ return readTaskCheckpointReceipt(directory, planIdentityHash, taskId);
1278
+ }
1279
+ function advanceTaskCheckpointReceiptGeneration(directory, planIdentityHash, taskId) {
1280
+ if (!projectDbExists(directory))
1281
+ return null;
1282
+ const db = getProjectDb(directory);
1283
+ db.transaction(() => {
1284
+ const row = db.query("SELECT * FROM task_checkpoint_receipt WHERE plan_identity_hash = ? AND task_id = ?").get(planIdentityHash, taskId);
1285
+ if (!row || row.completion_active === 0)
1286
+ return;
1287
+ const nextGeneration = row.generation + 1;
1288
+ const descriptor = buildTaskCompletionDescriptor(planIdentityHash, taskId, nextGeneration);
1289
+ db.run(`UPDATE task_checkpoint_receipt
1290
+ SET label = ?, state = 'pending', sha = NULL, generation = ?,
1291
+ completion_active = 0, completion_ledger_seq = NULL,
1292
+ updated_at = datetime('now')
1293
+ WHERE plan_identity_hash = ? AND task_id = ? AND completion_active = 1`, [descriptor.label, nextGeneration, planIdentityHash, taskId]);
1294
+ })();
1295
+ return readTaskCheckpointReceipt(directory, planIdentityHash, taskId);
1296
+ }
1297
+ function repairTaskCheckpointReceiptForCompletion(directory, planIdentityHash, taskId) {
1298
+ if (!projectDbExists(directory))
1299
+ return null;
1300
+ const current = readTaskCheckpointReceipt(directory, planIdentityHash, taskId);
1301
+ if (!current)
1302
+ return null;
1303
+ if (current.completion_active === 0) {
1304
+ return activateTaskCheckpointReceipt(directory, planIdentityHash, taskId);
1305
+ }
1306
+ advanceTaskCheckpointReceiptGeneration(directory, planIdentityHash, taskId);
1307
+ return activateTaskCheckpointReceipt(directory, planIdentityHash, taskId);
1308
+ }
1309
+ function updateTaskCheckpointReceipt(directory, descriptor, state, sha) {
1310
+ const db = getProjectDb(directory);
1311
+ db.run(`UPDATE task_checkpoint_receipt
1312
+ SET state = ?, sha = ?, updated_at = datetime('now')
1313
+ WHERE plan_identity_hash = ? AND task_id = ?
1314
+ AND generation = ? AND completion_active = 1`, [
1315
+ state,
1316
+ sha,
1317
+ descriptor.planIdentityHash,
1318
+ descriptor.taskId,
1319
+ descriptor.generation
1320
+ ]);
1321
+ const row = readTaskCheckpointReceipt(directory, descriptor.planIdentityHash, descriptor.taskId);
1322
+ if (!row || row.generation !== descriptor.generation || row.completion_active !== 1) {
1323
+ throw new Error(`Task checkpoint receipt generation changed for ${descriptor.taskId}`);
1324
+ }
1325
+ return row;
1326
+ }
1327
+
987
1328
  // src/git/branch.ts
988
1329
  init_executor();
989
1330
  import * as child_process from "child_process";
@@ -1292,7 +1633,7 @@ async function resetToRemoteBranch(cwd, options) {
1292
1633
  let lastError;
1293
1634
  for (let retry = 0;retry < 4; retry++) {
1294
1635
  if (retry > 0 && process.platform === "win32") {
1295
- await new Promise((resolve) => setTimeout(resolve, 500));
1636
+ await new Promise((resolve2) => setTimeout(resolve2, 500));
1296
1637
  }
1297
1638
  try {
1298
1639
  gitExec(["reset", "--hard", targetBranch], cwd);
@@ -1502,7 +1843,7 @@ async function resetToMainAfterMerge(cwd, options) {
1502
1843
  let discardSucceeded = false;
1503
1844
  for (let retry = 0;retry < 4; retry++) {
1504
1845
  if (retry > 0 && process.platform === "win32") {
1505
- await new Promise((resolve) => setTimeout(resolve, 500));
1846
+ await new Promise((resolve2) => setTimeout(resolve2, 500));
1506
1847
  }
1507
1848
  try {
1508
1849
  _internals5.gitExec(["checkout", "--", "."], cwd);
@@ -2094,7 +2435,7 @@ var _internals7 = {
2094
2435
  gitHeadSha: (cwd) => {
2095
2436
  return _internals5.gitExec(["rev-parse", "HEAD"], cwd).trim();
2096
2437
  },
2097
- sleep: (ms) => new Promise((resolve3) => setTimeout(resolve3, ms)),
2438
+ sleep: (ms) => new Promise((resolve4) => setTimeout(resolve4, ms)),
2098
2439
  hasExistingTaskCommit: (cwd, taskId) => {
2099
2440
  const safeId = scrubTaskIdForGitSubject(taskId);
2100
2441
  const escaped = safeId.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
@@ -2257,14 +2598,14 @@ function readTaskScopes(directory, taskId) {
2257
2598
  }
2258
2599
 
2259
2600
  // src/utils/spec-hash.ts
2260
- import { createHash as createHash2 } from "crypto";
2601
+ import { createHash as createHash3 } from "crypto";
2261
2602
  import * as fsSync2 from "fs";
2262
- import { join as join6 } from "path";
2603
+ import { join as join7 } from "path";
2263
2604
  async function computeSpecHash(directory) {
2264
2605
  const spec = _internals8.readEffectiveSpecSync(directory);
2265
2606
  if (!spec)
2266
2607
  return null;
2267
- return createHash2("sha256").update(spec.content, "utf-8").digest("hex");
2608
+ return createHash3("sha256").update(spec.content, "utf-8").digest("hex");
2268
2609
  }
2269
2610
  async function isSpecStale(directory, plan) {
2270
2611
  const currentHash = await _internals8.computeSpecHash(directory);
@@ -2288,7 +2629,7 @@ async function isSpecStale(directory, plan) {
2288
2629
  return { stale: false };
2289
2630
  }
2290
2631
  function isObligationPreserving(directory) {
2291
- const snapshotPath = join6(directory, ".swarm", "spec-snapshot.md");
2632
+ const snapshotPath = join7(directory, ".swarm", "spec-snapshot.md");
2292
2633
  try {
2293
2634
  let recordedContent;
2294
2635
  try {
@@ -2334,7 +2675,7 @@ var _internals8 = {
2334
2675
  readEffectiveSpecSync
2335
2676
  };
2336
2677
  function computeSpecDiff(directory) {
2337
- const snapshotPath = join6(directory, ".swarm", "spec-snapshot.md");
2678
+ const snapshotPath = join7(directory, ".swarm", "spec-snapshot.md");
2338
2679
  let recordedContent;
2339
2680
  try {
2340
2681
  const snapshotSize = fsSync2.statSync(snapshotPath).size;
@@ -2538,7 +2879,7 @@ async function retryCasWithBackoff(directory, eventInput, options) {
2538
2879
  expectedHashPrefix: currentExpected.slice(0, 8),
2539
2880
  delayMs
2540
2881
  });
2541
- await new Promise((resolve4) => setTimeout(resolve4, delayMs));
2882
+ await new Promise((resolve5) => setTimeout(resolve5, delayMs));
2542
2883
  if (options.verifyValid) {
2543
2884
  const stillValid = await options.verifyValid();
2544
2885
  if (!stillValid)
@@ -2616,6 +2957,14 @@ function computePlanContentHash(plan) {
2616
2957
  swarm: plan.swarm,
2617
2958
  current_phase: plan.current_phase,
2618
2959
  migration_status: plan.migration_status,
2960
+ execution_profile: plan.execution_profile ? {
2961
+ parallelization_enabled: plan.execution_profile.parallelization_enabled,
2962
+ max_concurrent_tasks: plan.execution_profile.max_concurrent_tasks,
2963
+ council_parallel: plan.execution_profile.council_parallel,
2964
+ locked: plan.execution_profile.locked,
2965
+ auto_proceed: plan.execution_profile.auto_proceed,
2966
+ commit_after_each_completed_task: plan.execution_profile.commit_after_each_completed_task === true ? true : undefined
2967
+ } : undefined,
2619
2968
  phases: plan.phases.map((phase) => ({
2620
2969
  id: phase.id,
2621
2970
  name: phase.name,
@@ -2947,6 +3296,23 @@ async function savePlan(directory, plan, options) {
2947
3296
  throw new Error(`Invalid directory: directory must be a non-empty string`);
2948
3297
  }
2949
3298
  assertProjectRoot(directory);
3299
+ if (!options?.planLockAlreadyHeld) {
3300
+ const lockResult = await tryAcquireLock(directory, "plan.json", "plan-manager", `save-plan-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`);
3301
+ if (!lockResult.acquired) {
3302
+ throw new PlanConcurrentModificationError(`Plan write blocked: plan.json is locked by ${lockResult.existing?.agent ?? "another agent"} (task: ${lockResult.existing?.taskId ?? "unknown"})`);
3303
+ }
3304
+ try {
3305
+ await savePlan(directory, plan, {
3306
+ ...options,
3307
+ planLockAlreadyHeld: true
3308
+ });
3309
+ return;
3310
+ } finally {
3311
+ if (lockResult.lock._release) {
3312
+ await lockResult.lock._release().catch(() => {});
3313
+ }
3314
+ }
3315
+ }
2950
3316
  const validated = PlanSchema.parse(plan);
2951
3317
  if (options?.preserveCompletedStatuses !== false) {
2952
3318
  try {
@@ -2991,7 +3357,7 @@ async function savePlan(directory, plan, options) {
2991
3357
  const oldLedgerPath = path8.join(swarmDir2, "plan-ledger.jsonl");
2992
3358
  const oldLedgerBackupPath = path8.join(swarmDir2, `plan-ledger.backup-${Date.now()}-${Math.floor(Math.random() * 1e9)}.jsonl`);
2993
3359
  let backupExists = false;
2994
- if (existsSync3(oldLedgerPath)) {
3360
+ if (existsSync4(oldLedgerPath)) {
2995
3361
  try {
2996
3362
  renameSync2(oldLedgerPath, oldLedgerBackupPath);
2997
3363
  backupExists = true;
@@ -3008,11 +3374,11 @@ async function savePlan(directory, plan, options) {
3008
3374
  const errorMessage2 = String(initErr);
3009
3375
  if (errorMessage2.includes("already initialized")) {
3010
3376
  try {
3011
- if (existsSync3(oldLedgerBackupPath))
3377
+ if (existsSync4(oldLedgerBackupPath))
3012
3378
  unlinkSync2(oldLedgerBackupPath);
3013
3379
  } catch {}
3014
3380
  } else {
3015
- if (existsSync3(oldLedgerBackupPath)) {
3381
+ if (existsSync4(oldLedgerBackupPath)) {
3016
3382
  try {
3017
3383
  renameSync2(oldLedgerBackupPath, oldLedgerPath);
3018
3384
  } catch {
@@ -3034,13 +3400,13 @@ async function savePlan(directory, plan, options) {
3034
3400
  } catch (renameErr) {
3035
3401
  warn(`[savePlan] Could not archive old ledger (rename failed: ${renameErr instanceof Error ? renameErr.message : String(renameErr)}). Old ledger may still exist at ${oldLedgerBackupPath}.`);
3036
3402
  try {
3037
- if (existsSync3(oldLedgerBackupPath))
3403
+ if (existsSync4(oldLedgerBackupPath))
3038
3404
  unlinkSync2(oldLedgerBackupPath);
3039
3405
  } catch {}
3040
3406
  }
3041
3407
  } else if (!initSucceeded && backupExists) {
3042
3408
  try {
3043
- if (existsSync3(oldLedgerBackupPath))
3409
+ if (existsSync4(oldLedgerBackupPath))
3044
3410
  unlinkSync2(oldLedgerBackupPath);
3045
3411
  } catch {}
3046
3412
  }
@@ -3257,6 +3623,37 @@ ${markdown}`;
3257
3623
  });
3258
3624
  await bunWrite(markerPath, marker);
3259
3625
  } catch {}
3626
+ if (currentPlan) {
3627
+ try {
3628
+ const oldIdentityHash = derivePlanIdentityHash(currentPlan);
3629
+ const newIdentityHash = derivePlanIdentityHash(projectedPlan);
3630
+ if (oldIdentityHash === newIdentityHash) {
3631
+ const oldStatuses = new Map;
3632
+ for (const phase of currentPlan.phases) {
3633
+ for (const task of phase.tasks)
3634
+ oldStatuses.set(task.id, task.status);
3635
+ }
3636
+ const newStatuses = new Map;
3637
+ for (const phase of projectedPlan.phases) {
3638
+ for (const task of phase.tasks)
3639
+ newStatuses.set(task.id, task.status);
3640
+ }
3641
+ for (const [taskId, oldStatus] of oldStatuses) {
3642
+ const newStatus = newStatuses.get(taskId);
3643
+ if (oldStatus === "completed" && newStatus !== "completed") {
3644
+ advanceTaskCheckpointReceiptGeneration(directory, oldIdentityHash, taskId);
3645
+ }
3646
+ }
3647
+ for (const [taskId, newStatus] of newStatuses) {
3648
+ if (newStatus === "completed" && oldStatuses.get(taskId) !== "completed") {
3649
+ repairTaskCheckpointReceiptForCompletion(directory, newIdentityHash, taskId);
3650
+ }
3651
+ }
3652
+ }
3653
+ } catch (receiptError) {
3654
+ warn(`[savePlan] task checkpoint receipt lifecycle sync failed (plan remains authoritative): ${receiptError instanceof Error ? receiptError.message : String(receiptError)}`);
3655
+ }
3656
+ }
3260
3657
  }
3261
3658
  async function rebuildPlan(directory, plan, options) {
3262
3659
  assertProjectRoot(directory);
@@ -3266,7 +3663,7 @@ async function rebuildPlan(directory, plan, options) {
3266
3663
  const swarmDir = path8.join(directory, ".swarm");
3267
3664
  const planPath = path8.join(swarmDir, "plan.json");
3268
3665
  const mdPath = path8.join(swarmDir, "plan.md");
3269
- mkdirSync3(swarmDir, { recursive: true });
3666
+ mkdirSync4(swarmDir, { recursive: true });
3270
3667
  const tempPlanPath = path8.join(swarmDir, `plan.json.rebuild.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
3271
3668
  {
3272
3669
  const fd = openSync(tempPlanPath, "w");
@@ -3423,6 +3820,24 @@ function derivePlanMarkdown(plan) {
3423
3820
  Swarm: ${plan.swarm}
3424
3821
  Phase: ${currentPhase} [${phaseStatus}] | Updated: ${now}
3425
3822
  `;
3823
+ if (plan.execution_profile) {
3824
+ const profile = plan.execution_profile;
3825
+ markdown += `
3826
+ ## Execution Profile
3827
+ `;
3828
+ markdown += `- Parallelization: ${profile.parallelization_enabled ? "enabled" : "disabled"}
3829
+ `;
3830
+ markdown += `- Max Concurrent Tasks: ${profile.max_concurrent_tasks}
3831
+ `;
3832
+ markdown += `- Council Parallel: ${profile.council_parallel ? "yes" : "no"}
3833
+ `;
3834
+ markdown += `- Locked: ${profile.locked ? "yes" : "no"}
3835
+ `;
3836
+ markdown += `- Auto Proceed: ${profile.auto_proceed ? "yes" : "no"}
3837
+ `;
3838
+ markdown += `- Commit After Each Completed Task: ${profile.commit_after_each_completed_task ? "yes" : "no"}
3839
+ `;
3840
+ }
3426
3841
  const sortedPhases = [...plan.phases].sort((a, b) => a.id - b.id);
3427
3842
  for (const phase of sortedPhases) {
3428
3843
  const phaseStatusText = statusMap[phase.status] || "PENDING";
@@ -3683,7 +4098,7 @@ function migrateLegacyPlan(planContent, swarmId) {
3683
4098
  // src/evidence/manager.ts
3684
4099
  init_zod();
3685
4100
  import {
3686
- mkdirSync as mkdirSync4,
4101
+ mkdirSync as mkdirSync5,
3687
4102
  readdirSync as readdirSync2,
3688
4103
  realpathSync,
3689
4104
  renameSync as renameSync3,
@@ -3880,7 +4295,7 @@ async function atomicRenameWithRetry(src, dst, renameFn = fsp.rename) {
3880
4295
  throw err;
3881
4296
  }
3882
4297
  if (attempt < RENAME_MAX_ATTEMPTS - 1) {
3883
- await new Promise((resolve4) => setTimeout(resolve4, RENAME_BACKOFF_MS));
4298
+ await new Promise((resolve5) => setTimeout(resolve5, RENAME_BACKOFF_MS));
3884
4299
  }
3885
4300
  }
3886
4301
  }
@@ -4164,7 +4579,7 @@ async function saveEvidence(directory, taskId, evidence, abortSignal) {
4164
4579
  if (bundleJson.length > EVIDENCE_MAX_JSON_BYTES) {
4165
4580
  throw new Error(`Evidence bundle size (${bundleJson.length} bytes) exceeds maximum (${EVIDENCE_MAX_JSON_BYTES} bytes)`);
4166
4581
  }
4167
- mkdirSync4(evidenceDir, { recursive: true });
4582
+ mkdirSync5(evidenceDir, { recursive: true });
4168
4583
  const tempPath = path10.join(evidenceDir, `evidence.json.tmp.${Date.now()}.${process.pid}`);
4169
4584
  try {
4170
4585
  await bunWrite(tempPath, bundleJson);
@@ -4552,4 +4967,4 @@ function mergeDurableGateEntriesFromEvidence(taskId, entries, evidence) {
4552
4967
  return merged;
4553
4968
  }
4554
4969
 
4555
- export { getExecutor, init_executor, MAX_TRANSIENT_RETRIES, isTransientSpawnError, transientBackoff, getGitRepositoryStatus, isGitRepo, getDefaultBaseBranch, resetToRemoteBranch, resetToMainAfterMerge, containsPathTraversal, containsControlChars, isStateUnreadable, loadEpicSessionState, isEpicModeActive, enableEpicMode, disableEpicMode, normalizePath, pathsConflict, isGlobalFile, isProtectedPath, readTaskScopes, computeSpecHash, loadPlanJsonOnly, loadPlan, savePlan, closePlanTerminalState, derivePlanMarkdown, isValidEvidenceType, sanitizeTaskId2 as sanitizeTaskId, validateProjectRoot, saveEvidence, loadEvidence, listEvidenceTaskIds, checkRequirementCoverage, archiveEvidence, readDurableGateEvidence, getDurableGateEvidenceStatus, getDurableGateEvidenceStatusForTask, mergeDurableGateEntriesFromEvidence };
4970
+ export { projectDbExists, getProjectDb, closeProjectDb, buildTaskCompletionDescriptor, ensureTaskCheckpointReceipt, updateTaskCheckpointReceipt, getExecutor, init_executor, MAX_TRANSIENT_RETRIES, isTransientSpawnError, transientBackoff, getGitRepositoryStatus, isGitRepo, getDefaultBaseBranch, resetToRemoteBranch, resetToMainAfterMerge, containsPathTraversal, containsControlChars, isStateUnreadable, loadEpicSessionState, isEpicModeActive, enableEpicMode, disableEpicMode, normalizePath, pathsConflict, isGlobalFile, isProtectedPath, readTaskScopes, computeSpecHash, loadPlanJsonOnly, loadPlan, savePlan, closePlanTerminalState, derivePlanMarkdown, isValidEvidenceType, sanitizeTaskId2 as sanitizeTaskId, validateProjectRoot, saveEvidence, loadEvidence, listEvidenceTaskIds, checkRequirementCoverage, archiveEvidence, readDurableGateEvidence, getDurableGateEvidenceStatus, getDurableGateEvidenceStatusForTask, mergeDurableGateEntriesFromEvidence };