mancode 0.6.3 → 0.6.5

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 (34) hide show
  1. package/README.en.md +87 -10
  2. package/README.md +60 -10
  3. package/dist/{chunk-NG72XZ3R.js → chunk-E2K22WYH.js} +2323 -1008
  4. package/dist/chunk-E2K22WYH.js.map +1 -0
  5. package/dist/chunk-GI24QVXE.js +1099 -0
  6. package/dist/chunk-GI24QVXE.js.map +1 -0
  7. package/dist/chunk-IRZQYMHD.js +334 -0
  8. package/dist/chunk-IRZQYMHD.js.map +1 -0
  9. package/dist/chunk-THOE33LU.js +1124 -0
  10. package/dist/chunk-THOE33LU.js.map +1 -0
  11. package/dist/{chunk-RDPFQODS.js → chunk-WJ6WARGG.js} +495 -250
  12. package/dist/chunk-WJ6WARGG.js.map +1 -0
  13. package/dist/chunk-WRBNOPFA.js +1235 -0
  14. package/dist/chunk-WRBNOPFA.js.map +1 -0
  15. package/dist/cli.js +22917 -21503
  16. package/dist/cli.js.map +1 -1
  17. package/dist/gateway/worker.d.ts +2 -0
  18. package/dist/gateway/worker.js +181 -0
  19. package/dist/gateway/worker.js.map +1 -0
  20. package/dist/privacy-gateway-Q4GD2JXF.js +20 -0
  21. package/dist/store-GSLSLZ7D.js +11 -0
  22. package/dist/{v3-adapter-7KIOYYWS.js → v3-adapter-T3IKK3LU.js} +4 -2
  23. package/dist/v3-adapter-T3IKK3LU.js.map +1 -0
  24. package/docs/README.md +34 -0
  25. package/docs/privacy-guide.md +76 -0
  26. package/docs/privacy-implementation-plan.md +101 -0
  27. package/docs/privacy-rule-sources.md +22 -0
  28. package/docs/privacy-upstream-license.txt +661 -0
  29. package/package.json +8 -3
  30. package/dist/chunk-NG72XZ3R.js.map +0 -1
  31. package/dist/chunk-RDPFQODS.js.map +0 -1
  32. package/dist/store-C3G3HN3N.js +0 -9
  33. /package/dist/{store-C3G3HN3N.js.map → privacy-gateway-Q4GD2JXF.js.map} +0 -0
  34. /package/dist/{v3-adapter-7KIOYYWS.js.map → store-GSLSLZ7D.js.map} +0 -0
@@ -1,239 +1,40 @@
1
- // src/context/store.ts
2
- import { lstat as lstat4, readFile as readFile7, readdir as readdir4 } from "fs/promises";
3
- import path9 from "path";
4
-
5
- // src/runtime/entity-home-store.ts
6
- import path from "path";
7
-
8
- // src/context/ids.ts
9
- import { randomBytes } from "crypto";
10
- var CROCKFORD_BASE32 = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
11
- var ULID_PATTERN = /^[0-7][0-9A-HJKMNPQRSTVWXYZ]{25}$/;
12
- var MAX_ULID_TIMESTAMP = 2 ** 48 - 1;
13
- function isUlid(value) {
14
- return typeof value === "string" && ULID_PATTERN.test(value);
15
- }
16
- function assertUlid(value, label = "ULID") {
17
- if (!isUlid(value)) {
18
- throw new Error(`${label} must be a canonical ULID`);
19
- }
20
- }
21
- function createUlid(now = Date.now(), entropy = randomBytes(10)) {
22
- if (!Number.isSafeInteger(now) || now < 0 || now > MAX_ULID_TIMESTAMP) {
23
- throw new Error("ULID timestamp must fit in 48 bits");
24
- }
25
- if (entropy.length !== 10) {
26
- throw new Error("ULID entropy must contain exactly 10 bytes");
27
- }
28
- const timestamp = encodeBase32(BigInt(now), 10);
29
- const random = encodeBase32(
30
- BigInt(`0x${Buffer.from(entropy).toString("hex")}`),
31
- 16
32
- );
33
- return `${timestamp}${random}`;
34
- }
35
- function encodeBase32(value, length) {
36
- let remaining = value;
37
- let encoded = "";
38
- for (let index = 0; index < length; index += 1) {
39
- encoded = `${CROCKFORD_BASE32[Number(remaining & 31n)]}${encoded}`;
40
- remaining >>= 5n;
41
- }
42
- if (remaining !== 0n) {
43
- throw new Error("value does not fit in requested base32 length");
44
- }
45
- return encoded;
46
- }
47
-
48
- // src/context/validation.ts
49
- function isRecord(value) {
50
- if (typeof value !== "object" || value === null || Array.isArray(value)) {
51
- return false;
52
- }
53
- const prototype = Object.getPrototypeOf(value);
54
- return prototype === Object.prototype || prototype === null;
55
- }
56
- function assertRecord(value, label) {
57
- if (!isRecord(value)) {
58
- throw new Error(`${label} must be an object`);
59
- }
60
- }
61
- function assertKnownKeys(value, allowedKeys, label) {
62
- const allowed = new Set(allowedKeys);
63
- const unknown = Object.keys(value).filter((key) => !allowed.has(key));
64
- if (unknown.length > 0) {
65
- throw new Error(
66
- `${label} contains unknown field(s): ${unknown.join(", ")}`
67
- );
68
- }
69
- }
70
-
71
- // src/context/task-ref.ts
72
- var TASK_REF_PATTERN = /^(local|shared):([0-7][0-9A-HJKMNPQRSTVWXYZ]{25})$/;
73
- function formatTaskRef(taskRef) {
74
- assertTaskRef(taskRef);
75
- return `${taskRef.namespace}:${taskRef.taskId}`;
76
- }
77
- function parseTaskRef(input) {
78
- if (typeof input !== "string") {
79
- throw new Error("TaskRef must be a string in namespace:ULID form");
80
- }
81
- const match = TASK_REF_PATTERN.exec(input);
82
- if (!match) {
83
- throw new Error("TaskRef must use local:<ULID> or shared:<ULID>");
84
- }
85
- return {
86
- namespace: match[1],
87
- taskId: match[2]
88
- };
89
- }
90
- function parseTaskRefValue(value) {
91
- assertRecord(value, "TaskRef");
92
- assertKnownKeys(value, ["namespace", "taskId"], "TaskRef");
93
- if (value.namespace !== "local" && value.namespace !== "shared") {
94
- throw new Error("TaskRef namespace must be local or shared");
95
- }
96
- assertUlid(value.taskId, "TaskRef taskId");
97
- return {
98
- namespace: value.namespace,
99
- taskId: value.taskId
100
- };
101
- }
102
- function assertTaskRef(value) {
103
- parseTaskRefValue(value);
104
- }
105
- function sameTaskRef(left, right) {
106
- return left.namespace === right.namespace && left.taskId === right.taskId;
107
- }
1
+ import {
2
+ assertKnownKeys,
3
+ assertRecord,
4
+ assertUlid,
5
+ claimDirectory,
6
+ createUlid,
7
+ digestCanonicalJson,
8
+ handoffDirectory,
9
+ lockDirectory,
10
+ operationDirectory,
11
+ parseProjectConfig,
12
+ parseTaskRef,
13
+ parseTaskRefValue,
14
+ parseTeamPolicy,
15
+ readProjectRuntimeContext,
16
+ reservationDirectory,
17
+ resolveLocalEntityHomeStore,
18
+ sameTaskRef,
19
+ sortUtf8StringSet,
20
+ taskHeadDirectory
21
+ } from "./chunk-WRBNOPFA.js";
22
+ import {
23
+ DEFAULT_RULE_IDS,
24
+ RULESET_VERSION,
25
+ scanSensitiveText
26
+ } from "./chunk-IRZQYMHD.js";
108
27
 
109
- // src/runtime/entity-home-store.ts
110
- function resolveTaskEntityHomeStore(context, taskRef) {
111
- const normalized = normalizeContext(context);
112
- const task = parseTaskRefValue(taskRef);
113
- return task.namespace === "local" ? localTaskHomeStore(normalized) : coordinationEntityHomeStore(normalized);
114
- }
115
- function resolveLocalEntityHomeStore(context) {
116
- return localTaskHomeStore(normalizeContext(context));
117
- }
118
- function resolveCoordinationEntityHomeStore(context) {
119
- return coordinationEntityHomeStore(normalizeContext(context));
120
- }
121
- function operationDirectory(store) {
122
- return path.join(store.root, "operations");
123
- }
124
- function reservationDirectory(store) {
125
- return path.join(store.root, "reservations");
126
- }
127
- function lockDirectory(store) {
128
- return path.join(store.root, "locks");
129
- }
130
- function claimDirectory(store) {
131
- assertCoordinationStore(store, "claim");
132
- return path.join(store.root, "claims");
133
- }
134
- function handoffDirectory(store) {
135
- assertCoordinationStore(store, "handoff");
136
- return path.join(store.root, "handoffs");
137
- }
138
- function taskHeadDirectory(store) {
139
- assertCoordinationStore(store, "task head fence");
140
- return path.join(store.root, "task-heads");
141
- }
142
- function normalizeContext(context) {
143
- assertUlid(context.workspaceId, "entity home store workspaceId");
144
- assertUlid(context.checkoutId, "entity home store checkoutId");
145
- if (typeof context.projectRoot !== "string" || !context.projectRoot.trim()) {
146
- throw new Error("entity home store projectRoot is required");
147
- }
148
- if (context.gitCommonDir === null) {
149
- if (context.repositoryBindingId !== null) {
150
- assertUlid(
151
- context.repositoryBindingId,
152
- "entity home store repositoryBindingId"
153
- );
154
- }
155
- return {
156
- ...context,
157
- projectRoot: path.resolve(context.projectRoot),
158
- gitCommonDir: null
159
- };
160
- }
161
- if (typeof context.gitCommonDir !== "string" || !context.gitCommonDir.trim()) {
162
- throw new Error("entity home store gitCommonDir must be a path or null");
163
- }
164
- if (context.repositoryBindingId === null) {
165
- throw new Error(
166
- "git coordination requires an entity home store repositoryBindingId"
167
- );
168
- }
169
- assertUlid(
170
- context.repositoryBindingId,
171
- "entity home store repositoryBindingId"
172
- );
173
- return {
174
- ...context,
175
- projectRoot: path.resolve(context.projectRoot),
176
- gitCommonDir: path.resolve(context.gitCommonDir)
177
- };
178
- }
179
- function localTaskHomeStore(context) {
180
- return {
181
- kind: "checkout_local",
182
- storeId: `checkout:${context.checkoutId}:${context.workspaceId}`,
183
- root: path.join(context.projectRoot, ".mancode", "local", "runtime"),
184
- workspaceId: context.workspaceId,
185
- checkoutId: context.checkoutId,
186
- repositoryBindingId: context.repositoryBindingId
187
- };
188
- }
189
- function coordinationEntityHomeStore(context) {
190
- if (context.gitCommonDir === null) {
191
- return {
192
- kind: "non_git_shared",
193
- storeId: `non-git:${context.workspaceId}`,
194
- root: path.join(
195
- context.projectRoot,
196
- ".mancode",
197
- "runtime",
198
- "non-git",
199
- context.workspaceId
200
- ),
201
- workspaceId: context.workspaceId,
202
- checkoutId: null,
203
- repositoryBindingId: context.repositoryBindingId
204
- };
205
- }
206
- if (context.repositoryBindingId === null) {
207
- throw new Error("git coordination requires a repositoryBindingId");
208
- }
209
- return {
210
- kind: "workspace_common_dir",
211
- storeId: `workspace:${context.repositoryBindingId}:${context.workspaceId}`,
212
- root: path.join(
213
- context.gitCommonDir,
214
- "mancode",
215
- "workspaces",
216
- context.workspaceId
217
- ),
218
- workspaceId: context.workspaceId,
219
- checkoutId: null,
220
- repositoryBindingId: context.repositoryBindingId
221
- };
222
- }
223
- function assertCoordinationStore(store, entity) {
224
- if (store.kind === "checkout_local") {
225
- throw new Error(
226
- `${entity} requires a shared coordination entity home store`
227
- );
228
- }
229
- }
28
+ // src/context/store.ts
29
+ import { lstat as lstat6, readFile as readFile10, readdir as readdir7 } from "fs/promises";
30
+ import path12 from "path";
230
31
 
231
32
  // src/runtime/git-ref-workflow-repair-store.ts
232
33
  import { lstat, readFile, readdir } from "fs/promises";
233
- import path2 from "path";
34
+ import path from "path";
234
35
  function gitRefWorkflowRepairJournalDirectory(projectRoot) {
235
- return path2.join(
236
- path2.resolve(projectRoot),
36
+ return path.join(
37
+ path.resolve(projectRoot),
237
38
  ".mancode",
238
39
  "local",
239
40
  "journals",
@@ -242,7 +43,7 @@ function gitRefWorkflowRepairJournalDirectory(projectRoot) {
242
43
  }
243
44
  function gitRefWorkflowRepairJournalPath(projectRoot, operationId) {
244
45
  assertUlid(operationId, "git-ref workflow repair operationId");
245
- return path2.join(
46
+ return path.join(
246
47
  gitRefWorkflowRepairJournalDirectory(projectRoot),
247
48
  `${operationId}.json`
248
49
  );
@@ -253,7 +54,7 @@ async function listGitRefWorkflowRepairJournalSummaries(projectRoot, options = {
253
54
  const summaries = [];
254
55
  for (const entry of entries) {
255
56
  if (!entry.endsWith(".json")) continue;
256
- const journalPath = path2.join(directory, entry);
57
+ const journalPath = path.join(directory, entry);
257
58
  let summary;
258
59
  try {
259
60
  summary = parseSummary(
@@ -372,109 +173,8 @@ function isNotFound(error) {
372
173
  return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
373
174
  }
374
175
 
375
- // src/context/canonical.ts
376
- import { createHash } from "crypto";
377
- function canonicalizeJson(value, options = {}) {
378
- return canonicalizeValue(value, options.numberPolicy ?? "safe-integer");
379
- }
380
- function digestCanonicalJson(value, options = {}) {
381
- const canonical = canonicalizeJson(value, options);
382
- return `sha256:${createHash("sha256").update(canonical, "utf8").digest("hex")}`;
383
- }
384
- function sortUtf8StringSet(values) {
385
- const unique = /* @__PURE__ */ new Set();
386
- for (const value of values) {
387
- assertCanonicalString(value, "set item");
388
- unique.add(value);
389
- }
390
- return [...unique].sort(
391
- (left, right) => Buffer.from(left, "utf8").compare(Buffer.from(right, "utf8"))
392
- );
393
- }
394
- function canonicalizeValue(value, numberPolicy) {
395
- if (value === null) return "null";
396
- if (typeof value === "string") {
397
- assertCanonicalString(value, "string");
398
- return JSON.stringify(value);
399
- }
400
- if (typeof value === "boolean") return value ? "true" : "false";
401
- if (typeof value === "number") {
402
- if (!Number.isFinite(value) || Object.is(value, -0)) {
403
- throw new Error(
404
- "canonical JSON numbers must be finite and must not be negative zero"
405
- );
406
- }
407
- if (numberPolicy === "safe-integer" && !Number.isSafeInteger(value)) {
408
- throw new Error(
409
- "canonical JSON numbers must be safe integers for this schema"
410
- );
411
- }
412
- return JSON.stringify(value);
413
- }
414
- if (Array.isArray(value)) {
415
- assertCanonicalArray(value);
416
- return `[${value.map((item) => canonicalizeValue(item, numberPolicy)).join(",")}]`;
417
- }
418
- if (isPlainObject(value)) {
419
- assertCanonicalObject(value);
420
- const keys = Object.keys(value).sort();
421
- return `{${keys.map((key) => {
422
- assertCanonicalString(key, "object key");
423
- return `${JSON.stringify(key)}:${canonicalizeValue(value[key], numberPolicy)}`;
424
- }).join(",")}}`;
425
- }
426
- throw new Error("canonical JSON only accepts plain JSON values");
427
- }
428
- function assertCanonicalArray(value) {
429
- for (let index = 0; index < value.length; index += 1) {
430
- if (!Object.hasOwn(value, index)) {
431
- throw new Error("canonical JSON arrays must not be sparse");
432
- }
433
- }
434
- const ownKeys = Object.keys(value);
435
- if (ownKeys.some((key) => !/^(0|[1-9]\d*)$/.test(key))) {
436
- throw new Error("canonical JSON arrays must not have non-index properties");
437
- }
438
- if (Object.getOwnPropertyNames(value).some(
439
- (key) => key !== "length" && !ownKeys.includes(key)
440
- ) || Object.getOwnPropertySymbols(value).length > 0) {
441
- throw new Error("canonical JSON arrays must not have hidden properties");
442
- }
443
- }
444
- function assertCanonicalObject(value) {
445
- const ownKeys = Object.keys(value);
446
- if (Object.getOwnPropertyNames(value).some((key) => !ownKeys.includes(key)) || Object.getOwnPropertySymbols(value).length > 0) {
447
- throw new Error("canonical JSON objects must not have hidden properties");
448
- }
449
- }
450
- function isPlainObject(value) {
451
- if (typeof value !== "object" || value === null || Array.isArray(value)) {
452
- return false;
453
- }
454
- const prototype = Object.getPrototypeOf(value);
455
- return prototype === Object.prototype || prototype === null;
456
- }
457
- function assertCanonicalString(value, label) {
458
- if (value.includes("\0")) {
459
- throw new Error(`canonical JSON ${label} must not contain NUL`);
460
- }
461
- for (let index = 0; index < value.length; index += 1) {
462
- const codeUnit = value.charCodeAt(index);
463
- if (codeUnit < 55296 || codeUnit > 57343) continue;
464
- const next = value.charCodeAt(index + 1);
465
- const isHigh = codeUnit <= 56319;
466
- const isLowNext = next >= 56320 && next <= 57343;
467
- if (!isHigh || !isLowNext) {
468
- throw new Error(
469
- `canonical JSON ${label} must not contain a lone surrogate`
470
- );
471
- }
472
- index += 1;
473
- }
474
- }
475
-
476
176
  // src/context/privacy.ts
477
- import path3 from "path";
177
+ import path2 from "path";
478
178
  var FINDING_PATTERNS = [
479
179
  {
480
180
  kind: "private_key",
@@ -523,7 +223,7 @@ function assertSharedTextSafe(value, label) {
523
223
  }
524
224
  }
525
225
  function assertSafeSharedRelativePath(value) {
526
- if (typeof value !== "string" || !value || value.includes("\0") || path3.isAbsolute(value) || /^[A-Za-z][A-Za-z0-9+.-]*:/.test(value)) {
226
+ if (typeof value !== "string" || !value || value.includes("\0") || path2.isAbsolute(value) || /^[A-Za-z][A-Za-z0-9+.-]*:/.test(value)) {
527
227
  throw new Error("MANCODE_ARTIFACT_PATH_UNSAFE");
528
228
  }
529
229
  const normalized = value.split(/[\\/]/);
@@ -1077,6 +777,7 @@ var OPERATION_TYPES = /* @__PURE__ */ new Set([
1077
777
  "greenfield_initialize",
1078
778
  "adapter_upgrade",
1079
779
  "project_policy_upgrade",
780
+ "privacy_policy_update",
1080
781
  "v3_activate"
1081
782
  ]);
1082
783
  var OPERATION_STATES = /* @__PURE__ */ new Set([
@@ -1275,14 +976,14 @@ function parseSecondaryReservations(value) {
1275
976
  function parseExpectedRevisions(value) {
1276
977
  assertRecord(value, "operation expectedRevisions");
1277
978
  const parsed = {};
1278
- for (const [entityKey, revision] of Object.entries(value)) {
979
+ for (const [entityKey, revision2] of Object.entries(value)) {
1279
980
  assertEntityKey(entityKey, "operation expected revision key");
1280
- if (typeof revision !== "number" || !Number.isSafeInteger(revision) || revision < 0) {
981
+ if (typeof revision2 !== "number" || !Number.isSafeInteger(revision2) || revision2 < 0) {
1281
982
  throw new Error(
1282
983
  "operation expected revisions must be non-negative integers"
1283
984
  );
1284
985
  }
1285
- parsed[entityKey] = revision;
986
+ parsed[entityKey] = revision2;
1286
987
  }
1287
988
  return parsed;
1288
989
  }
@@ -1365,9 +1066,9 @@ function assertReframeCheckpointReplacement(previous, next, replacement) {
1365
1066
  throw new Error("MANCODE_REFRAME_CHECKPOINT_REPLACEMENT_INVALID");
1366
1067
  }
1367
1068
  const expectedRevisions = Object.fromEntries(
1368
- Object.entries(previous.expectedRevisions).map(([key, revision]) => [
1069
+ Object.entries(previous.expectedRevisions).map(([key, revision2]) => [
1369
1070
  key === fromKey ? toKey : key,
1370
- revision
1071
+ revision2
1371
1072
  ])
1372
1073
  );
1373
1074
  if (JSON.stringify(next.expectedRevisions) !== JSON.stringify(expectedRevisions)) {
@@ -1426,7 +1127,7 @@ function allowedOperationTransitions(from) {
1426
1127
 
1427
1128
  // src/runtime/operation-reservation.ts
1428
1129
  import { mkdir, readFile as readFile2, unlink, writeFile } from "fs/promises";
1429
- import path4 from "path";
1130
+ import path3 from "path";
1430
1131
  var DIGEST_PATTERN3 = /^sha256:[a-f0-9]{64}$/;
1431
1132
  var ENTITY_KEY_PATTERN2 = /^[a-z][a-z0-9_-]*:[^\0/\\]+$/;
1432
1133
  function parseOperationReservation(value) {
@@ -1545,7 +1246,7 @@ async function removeOperationReservation(store, operationId, primaryStoreId) {
1545
1246
  }
1546
1247
  function reservationPath(store, operationId) {
1547
1248
  assertUlid(operationId, "operation reservation operationId");
1548
- return path4.join(reservationDirectory(store), `${operationId}.json`);
1249
+ return path3.join(reservationDirectory(store), `${operationId}.json`);
1549
1250
  }
1550
1251
  function parseEntityKeySet2(value, label) {
1551
1252
  if (!Array.isArray(value) || value.length === 0) {
@@ -4143,16 +3844,16 @@ function parseImplementationScope(value) {
4143
3844
  "workflow metadata implementationScope modules"
4144
3845
  )
4145
3846
  };
4146
- const digest = parseDigest8(
3847
+ const digest2 = parseDigest8(
4147
3848
  value.digest,
4148
3849
  "workflow metadata implementationScope digest"
4149
3850
  );
4150
- if (digest !== digestCanonicalJson(scope)) {
3851
+ if (digest2 !== digestCanonicalJson(scope)) {
4151
3852
  throw new Error(
4152
3853
  "workflow metadata implementationScope digest does not match scope"
4153
3854
  );
4154
3855
  }
4155
- return { ...scope, digest };
3856
+ return { ...scope, digest: digest2 };
4156
3857
  }
4157
3858
  function parseGovernance2(value) {
4158
3859
  assertRecord(value, "workflow metadata governance");
@@ -5590,228 +5291,2086 @@ function allowedHandoffTransitions(from) {
5590
5291
  }
5591
5292
  }
5592
5293
 
5593
- // src/team/policy.ts
5594
- var V3_LAYOUT_VERSION = 3;
5595
- function parseProjectConfig(value) {
5596
- assertRecord(value, "project config");
5597
- assertKnownKeys(
5598
- value,
5599
- [
5600
- "schemaVersion",
5601
- "revision",
5602
- "workspaceId",
5603
- "transport",
5604
- "lastOperationId",
5605
- "updatedAt"
5606
- ],
5607
- "project config"
5608
- );
5609
- if (value.schemaVersion !== 1) {
5610
- throw new Error("project config schemaVersion must be 1");
5294
+ // src/context/confirmed-decision.ts
5295
+ import { lstat as lstat4, mkdir as mkdir4, readFile as readFile6, readdir as readdir5, writeFile as writeFile4 } from "fs/promises";
5296
+ import path8 from "path";
5297
+
5298
+ // src/context/privacy-guard.ts
5299
+ import { lstat as lstat3, readdir as readdir4 } from "fs/promises";
5300
+ import path7 from "path";
5301
+ import { performance } from "perf_hooks";
5302
+
5303
+ // src/runtime/local-lock.ts
5304
+ import { createHash } from "crypto";
5305
+ import { mkdir as mkdir2, readFile as readFile3, rename, rm, writeFile as writeFile2 } from "fs/promises";
5306
+ import path4 from "path";
5307
+ var ENTITY_LOCK_KEY_PATTERN = /^[a-z][a-z0-9_-]*:[^\0/\\]+$/;
5308
+ var DEFAULT_LOCK_LEASE_MS = 3e4;
5309
+ var MINIMUM_LOCK_LEASE_MS = 1e3;
5310
+ var MAXIMUM_LOCK_LEASE_MS = 5 * 6e4;
5311
+ async function acquireLocalLock(store, input) {
5312
+ const owner = createLockOwner(store, input);
5313
+ const directory = lockPath(store, owner.entityLockKey);
5314
+ await mkdir2(lockDirectory(store), { recursive: true });
5315
+ let acquiredDirectory = false;
5316
+ for (let attempt = 0; attempt < 3; attempt += 1) {
5317
+ try {
5318
+ await mkdir2(directory);
5319
+ acquiredDirectory = true;
5320
+ break;
5321
+ } catch (error) {
5322
+ if (!isAlreadyExists2(error)) throw error;
5323
+ if (!await reclaimExpiredDeadLock(store, owner.entityLockKey, owner)) {
5324
+ throw new Error("MANCODE_LOCK_HELD");
5325
+ }
5326
+ }
5327
+ }
5328
+ if (!acquiredDirectory) throw new Error("MANCODE_LOCK_HELD");
5329
+ try {
5330
+ await writeFile2(
5331
+ path4.join(directory, "owner.json"),
5332
+ `${JSON.stringify(owner, null, 2)}
5333
+ `,
5334
+ { encoding: "utf8", flag: "wx" }
5335
+ );
5336
+ } catch (error) {
5337
+ await rm(directory, { recursive: true, force: true });
5338
+ throw error;
5611
5339
  }
5612
- assertUlid(value.workspaceId, "project config workspaceId");
5340
+ let released = false;
5341
+ let currentOwner = owner;
5613
5342
  return {
5614
- schemaVersion: 1,
5615
- revision: parsePositiveInteger12(value.revision, "project config revision"),
5616
- workspaceId: value.workspaceId,
5617
- transport: parseTransport2(value.transport),
5618
- lastOperationId: parseUlidOrNull8(
5619
- value.lastOperationId,
5620
- "project config lastOperationId"
5621
- ),
5622
- updatedAt: parseTimestamp12(value.updatedAt, "project config updatedAt")
5343
+ storeId: store.storeId,
5344
+ entityLockKey: owner.entityLockKey,
5345
+ get owner() {
5346
+ return currentOwner;
5347
+ },
5348
+ async renew(now = /* @__PURE__ */ new Date()) {
5349
+ if (released) throw new Error("MANCODE_LOCK_OWNERSHIP_LOST");
5350
+ const next = renewLockOwner(currentOwner, now);
5351
+ const current = await readLocalLock(store, currentOwner.entityLockKey);
5352
+ if (!sameLockOwner(current, currentOwner)) {
5353
+ throw new Error("MANCODE_LOCK_OWNERSHIP_LOST");
5354
+ }
5355
+ await atomicWriteLockOwner(directory, next);
5356
+ currentOwner = next;
5357
+ },
5358
+ async release() {
5359
+ if (released) return;
5360
+ const current = await readLocalLock(store, currentOwner.entityLockKey);
5361
+ if (!sameLockOwner(current, currentOwner)) {
5362
+ throw new Error("MANCODE_LOCK_OWNERSHIP_LOST");
5363
+ }
5364
+ await rm(directory, { recursive: true, force: false });
5365
+ released = true;
5366
+ }
5623
5367
  };
5624
5368
  }
5625
- function parseTeamPolicy(value) {
5626
- assertRecord(value, "team policy");
5369
+ async function acquireEntityLocks(store, operationId, entityLockKeys, options = {}) {
5370
+ assertUlid(operationId, "local lock operationId");
5371
+ const orderedKeys = normalizeEntityLockKeys(entityLockKeys);
5372
+ const locks = [];
5373
+ try {
5374
+ for (const entityLockKey of orderedKeys) {
5375
+ locks.push(
5376
+ await acquireLocalLock(store, {
5377
+ operationId,
5378
+ entityLockKey,
5379
+ processId: options.processId,
5380
+ now: options.now,
5381
+ leaseMs: options.leaseMs
5382
+ })
5383
+ );
5384
+ }
5385
+ return locks;
5386
+ } catch (error) {
5387
+ await Promise.allSettled(
5388
+ [...locks].reverse().map((lock) => lock.release())
5389
+ );
5390
+ throw error;
5391
+ }
5392
+ }
5393
+ async function acquireOperationEntityLocks(operationId, targets, options = {}) {
5394
+ assertUlid(operationId, "operation lock operationId");
5395
+ if (!Array.isArray(targets) || targets.length === 0) {
5396
+ throw new Error("operation lock targets must not be empty");
5397
+ }
5398
+ const byStoreId = /* @__PURE__ */ new Map();
5399
+ for (const target of targets) {
5400
+ const normalizedKeys = normalizeEntityLockKeys(target.entityLockKeys);
5401
+ const existing = byStoreId.get(target.store.storeId);
5402
+ if (existing !== void 0) {
5403
+ throw new Error("operation lock targets must not repeat a store");
5404
+ }
5405
+ byStoreId.set(target.store.storeId, {
5406
+ store: target.store,
5407
+ entityLockKeys: normalizedKeys
5408
+ });
5409
+ }
5410
+ const locks = [];
5411
+ try {
5412
+ for (const target of [...byStoreId.values()].sort(
5413
+ (left, right) => compareUtf82(left.store.storeId, right.store.storeId)
5414
+ )) {
5415
+ locks.push(
5416
+ ...await acquireEntityLocks(
5417
+ target.store,
5418
+ operationId,
5419
+ target.entityLockKeys,
5420
+ options
5421
+ )
5422
+ );
5423
+ }
5424
+ return locks;
5425
+ } catch (error) {
5426
+ await Promise.allSettled(
5427
+ [...locks].reverse().map((lock) => lock.release())
5428
+ );
5429
+ throw error;
5430
+ }
5431
+ }
5432
+ async function readLocalLock(store, entityLockKey) {
5433
+ assertEntityLockKey(entityLockKey);
5434
+ try {
5435
+ const raw = await readFile3(
5436
+ path4.join(lockPath(store, entityLockKey), "owner.json"),
5437
+ "utf8"
5438
+ );
5439
+ const owner = parseLocalLockOwner(JSON.parse(raw));
5440
+ if (owner.storeId !== store.storeId || owner.entityLockKey !== entityLockKey) {
5441
+ throw new Error("MANCODE_LOCK_CORRUPT");
5442
+ }
5443
+ return owner;
5444
+ } catch (error) {
5445
+ if (isNotFound3(error)) return null;
5446
+ if (error instanceof SyntaxError) throw new Error("MANCODE_LOCK_CORRUPT");
5447
+ throw error;
5448
+ }
5449
+ }
5450
+ function parseLocalLockOwner(value) {
5451
+ assertRecord(value, "local lock owner");
5627
5452
  assertKnownKeys(
5628
5453
  value,
5629
5454
  [
5630
5455
  "schemaVersion",
5631
- "revision",
5632
- "workspaceId",
5633
- "policy",
5634
- "recentDays",
5635
- "defaultVisibility",
5636
- "shareConfirmedDecisions",
5637
- "retention",
5638
- "lastOperationId",
5639
- "updatedAt"
5456
+ "operationId",
5457
+ "processId",
5458
+ "storeId",
5459
+ "entityLockKey",
5460
+ "acquiredAt",
5461
+ "leaseExpiresAt"
5640
5462
  ],
5641
- "team policy"
5463
+ "local lock owner"
5642
5464
  );
5643
5465
  if (value.schemaVersion !== 1) {
5644
- throw new Error("team policy schemaVersion must be 1");
5466
+ throw new Error("local lock owner schemaVersion must be 1");
5645
5467
  }
5646
- assertUlid(value.workspaceId, "team policy workspaceId");
5647
- if (value.policy !== "on" && value.policy !== "off" && value.policy !== "auto") {
5648
- throw new Error("team policy policy is invalid");
5468
+ assertUlid(value.operationId, "local lock operationId");
5469
+ assertEntityLockKey(value.entityLockKey);
5470
+ if (typeof value.processId !== "number" || !Number.isSafeInteger(value.processId) || value.processId < 1) {
5471
+ throw new Error("local lock processId must be a positive integer");
5649
5472
  }
5650
- if (value.defaultVisibility !== "local" && value.defaultVisibility !== "shared") {
5651
- throw new Error("team policy defaultVisibility is invalid");
5652
- }
5653
- if (typeof value.shareConfirmedDecisions !== "boolean") {
5654
- throw new Error("team policy shareConfirmedDecisions must be boolean");
5473
+ if (typeof value.storeId !== "string" || !value.storeId.trim()) {
5474
+ throw new Error("local lock storeId is required");
5655
5475
  }
5656
5476
  return {
5657
5477
  schemaVersion: 1,
5658
- revision: parsePositiveInteger12(value.revision, "team policy revision"),
5659
- workspaceId: value.workspaceId,
5660
- policy: value.policy,
5661
- recentDays: parseNonNegativeInteger9(
5662
- value.recentDays,
5663
- "team policy recentDays"
5664
- ),
5665
- defaultVisibility: value.defaultVisibility,
5666
- shareConfirmedDecisions: value.shareConfirmedDecisions,
5667
- retention: parseRetention(value.retention),
5668
- lastOperationId: parseUlidOrNull8(
5669
- value.lastOperationId,
5670
- "team policy lastOperationId"
5671
- ),
5672
- updatedAt: parseTimestamp12(value.updatedAt, "team policy updatedAt")
5478
+ operationId: value.operationId,
5479
+ processId: value.processId,
5480
+ storeId: value.storeId,
5481
+ entityLockKey: value.entityLockKey,
5482
+ acquiredAt: parseTimestamp12(value.acquiredAt, "local lock acquiredAt"),
5483
+ leaseExpiresAt: value.leaseExpiresAt === void 0 ? null : value.leaseExpiresAt === null ? null : parseTimestamp12(value.leaseExpiresAt, "local lock leaseExpiresAt")
5673
5484
  };
5674
5485
  }
5675
- function projectConfigIdentityDigest(config) {
5676
- return digestCanonicalJson({
5677
- workspaceId: config.workspaceId,
5678
- configSchemaVersion: config.schemaVersion,
5679
- layoutVersion: V3_LAYOUT_VERSION
5680
- });
5681
- }
5682
- function projectConfigDigest(config) {
5683
- return digestCanonicalJson(parseProjectConfig(config));
5684
- }
5685
- function assertConfigPolicyConsistency(config, policy) {
5686
- if (config.workspaceId !== policy.workspaceId) {
5687
- throw new Error("project config and team policy workspaceId must match");
5486
+ function normalizeEntityLockKeys(entityLockKeys) {
5487
+ if (!Array.isArray(entityLockKeys) || entityLockKeys.length === 0) {
5488
+ throw new Error("entity lock keys must be a non-empty array");
5688
5489
  }
5490
+ const keys = /* @__PURE__ */ new Set();
5491
+ for (const key of entityLockKeys) {
5492
+ assertEntityLockKey(key);
5493
+ if (keys.has(key)) throw new Error("entity lock keys must not repeat");
5494
+ keys.add(key);
5495
+ }
5496
+ return [...keys].sort(compareUtf82);
5497
+ }
5498
+ function createLockOwner(store, input) {
5499
+ assertUlid(input.operationId, "local lock operationId");
5500
+ assertEntityLockKey(input.entityLockKey);
5501
+ const processId = input.processId ?? process.pid;
5502
+ if (!Number.isSafeInteger(processId) || processId < 1) {
5503
+ throw new Error("local lock processId must be a positive integer");
5504
+ }
5505
+ const now = input.now ?? /* @__PURE__ */ new Date();
5506
+ const leaseMs = parseLeaseMs(input.leaseMs);
5507
+ return {
5508
+ schemaVersion: 1,
5509
+ operationId: input.operationId,
5510
+ processId,
5511
+ storeId: store.storeId,
5512
+ entityLockKey: input.entityLockKey,
5513
+ acquiredAt: now.toISOString(),
5514
+ leaseExpiresAt: new Date(now.getTime() + leaseMs).toISOString()
5515
+ };
5689
5516
  }
5690
- function assertProjectConfigTransition(previous, next, kind) {
5691
- assertConfigIdentityStable(previous, next);
5692
- assertRevisionIncrease(
5693
- previous.revision,
5694
- next.revision,
5695
- "project config revision"
5696
- );
5697
- const transportChanged = previous.transport.mode !== next.transport.mode || previous.transport.remote !== next.transport.remote || previous.transport.epoch !== next.transport.epoch;
5698
- if (transportChanged && kind === "ordinary") {
5699
- throw new Error(
5700
- "project config transport may only change through transport_set or transport_migrate"
5701
- );
5517
+ function renewLockOwner(owner, now) {
5518
+ if (owner.leaseExpiresAt === null) {
5519
+ throw new Error("MANCODE_LOCK_LEASE_UNAVAILABLE");
5702
5520
  }
5703
- if (!transportChanged && kind !== "ordinary") {
5704
- throw new Error(
5705
- "transport mutation requires a changed project config transport"
5706
- );
5521
+ return {
5522
+ ...owner,
5523
+ leaseExpiresAt: new Date(now.getTime() + lockLeaseMs(owner)).toISOString()
5524
+ };
5525
+ }
5526
+ async function reclaimExpiredDeadLock(store, entityLockKey, contender) {
5527
+ const existing = await readLocalLock(store, entityLockKey);
5528
+ if (existing === null || existing.leaseExpiresAt === null || Date.parse(existing.leaseExpiresAt) >= Date.parse(contender.acquiredAt) || processIsAlive(existing.processId)) {
5529
+ return false;
5707
5530
  }
5708
- if (kind !== "ordinary" && next.transport.epoch !== previous.transport.epoch + 1) {
5709
- throw new Error(
5710
- "transport mutation must increase the authority epoch exactly once"
5711
- );
5531
+ const directory = lockPath(store, entityLockKey);
5532
+ const staleDirectory = `${directory}.stale.${process.pid}.${Date.now()}`;
5533
+ try {
5534
+ await rename(directory, staleDirectory);
5535
+ } catch (error) {
5536
+ if (isNotFound3(error) || isAlreadyExists2(error)) return true;
5537
+ throw error;
5712
5538
  }
5539
+ await rm(staleDirectory, { recursive: true, force: true });
5540
+ return true;
5713
5541
  }
5714
- function assertTeamPolicyTransition(previous, next) {
5715
- if (previous.schemaVersion !== next.schemaVersion || previous.workspaceId !== next.workspaceId) {
5716
- throw new Error("team policy schemaVersion and workspaceId are immutable");
5542
+ function processIsAlive(processId) {
5543
+ if (processId === process.pid) return true;
5544
+ try {
5545
+ process.kill(processId, 0);
5546
+ return true;
5547
+ } catch (error) {
5548
+ return !(typeof error === "object" && error !== null && "code" in error && error.code === "ESRCH");
5717
5549
  }
5718
- assertRevisionIncrease(
5719
- previous.revision,
5720
- next.revision,
5721
- "team policy revision"
5722
- );
5723
5550
  }
5724
- function parseTransport2(value) {
5725
- assertRecord(value, "project config transport");
5726
- assertKnownKeys(
5727
- value,
5728
- ["mode", "remote", "epoch"],
5729
- "project config transport"
5730
- );
5731
- if (value.mode !== "local" && value.mode !== "git-ref") {
5732
- throw new Error("project config transport mode is invalid");
5733
- }
5734
- const remote = parseNonEmptyStringOrNull5(
5735
- value.remote,
5736
- "project config transport remote"
5551
+ async function atomicWriteLockOwner(directory, owner) {
5552
+ const target = path4.join(directory, "owner.json");
5553
+ const temporary = path4.join(
5554
+ directory,
5555
+ `.owner.${process.pid}.${Date.now()}.tmp`
5737
5556
  );
5738
- if (value.mode === "local" && remote !== null) {
5739
- throw new Error("local project config transport must not set a remote");
5740
- }
5741
- if (value.mode === "git-ref" && remote === null) {
5742
- throw new Error("git-ref project config transport requires a remote");
5557
+ await writeFile2(temporary, `${JSON.stringify(owner, null, 2)}
5558
+ `, {
5559
+ encoding: "utf8",
5560
+ flag: "wx"
5561
+ });
5562
+ await rename(temporary, target);
5563
+ }
5564
+ function parseLeaseMs(value) {
5565
+ if (value === void 0) return DEFAULT_LOCK_LEASE_MS;
5566
+ if (!Number.isSafeInteger(value) || value < MINIMUM_LOCK_LEASE_MS || value > MAXIMUM_LOCK_LEASE_MS) {
5567
+ throw new Error("local lock leaseMs is invalid");
5743
5568
  }
5744
- const epoch = value.epoch === void 0 ? 1 : parsePositiveInteger12(value.epoch, "project config transport epoch");
5745
- return { mode: value.mode, remote, epoch };
5569
+ return value;
5746
5570
  }
5747
- function parseRetention(value) {
5748
- assertRecord(value, "team policy retention");
5749
- assertKnownKeys(
5750
- value,
5751
- ["localRawArtifactDays", "localCacheDays", "completedSessionDays"],
5752
- "team policy retention"
5753
- );
5754
- return {
5755
- localRawArtifactDays: parseNonNegativeInteger9(
5756
- value.localRawArtifactDays,
5757
- "team policy retention localRawArtifactDays"
5758
- ),
5759
- localCacheDays: parseNonNegativeInteger9(
5760
- value.localCacheDays,
5761
- "team policy retention localCacheDays"
5762
- ),
5763
- completedSessionDays: parseNonNegativeInteger9(
5764
- value.completedSessionDays,
5765
- "team policy retention completedSessionDays"
5766
- )
5767
- };
5571
+ function lockLeaseMs(owner) {
5572
+ if (owner.leaseExpiresAt === null) return DEFAULT_LOCK_LEASE_MS;
5573
+ const duration = Date.parse(owner.leaseExpiresAt) - Date.parse(owner.acquiredAt);
5574
+ return duration >= MINIMUM_LOCK_LEASE_MS && duration <= MAXIMUM_LOCK_LEASE_MS ? duration : DEFAULT_LOCK_LEASE_MS;
5768
5575
  }
5769
- function assertConfigIdentityStable(previous, next) {
5770
- if (previous.schemaVersion !== next.schemaVersion || previous.workspaceId !== next.workspaceId) {
5771
- throw new Error(
5772
- "project config schemaVersion and workspaceId are immutable"
5773
- );
5774
- }
5576
+ function lockPath(store, entityLockKey) {
5577
+ assertEntityLockKey(entityLockKey);
5578
+ const fileName = createHash("sha256").update(entityLockKey, "utf8").digest("hex");
5579
+ return path4.join(lockDirectory(store), `${fileName}.lock`);
5775
5580
  }
5776
- function assertRevisionIncrease(previous, next, label) {
5777
- if (next !== previous + 1) {
5778
- throw new Error(`${label} must increase exactly once per mutation`);
5581
+ function assertEntityLockKey(value) {
5582
+ if (typeof value !== "string" || !ENTITY_LOCK_KEY_PATTERN.test(value) || value.includes("..")) {
5583
+ throw new Error("entity lock key is invalid");
5779
5584
  }
5780
5585
  }
5781
- function parsePositiveInteger12(value, label) {
5782
- if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 1) {
5783
- throw new Error(`${label} must be a positive integer`);
5784
- }
5785
- return value;
5586
+ function sameLockOwner(current, expected) {
5587
+ return current !== null && current.operationId === expected.operationId && current.processId === expected.processId && current.storeId === expected.storeId && current.entityLockKey === expected.entityLockKey && current.acquiredAt === expected.acquiredAt && current.leaseExpiresAt === expected.leaseExpiresAt;
5786
5588
  }
5787
- function parseNonNegativeInteger9(value, label) {
5788
- if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) {
5789
- throw new Error(`${label} must be a non-negative integer`);
5589
+ function compareUtf82(left, right) {
5590
+ return Buffer.from(left, "utf8").compare(Buffer.from(right, "utf8"));
5591
+ }
5592
+ function parseTimestamp12(value, label) {
5593
+ if (typeof value !== "string" || Number.isNaN(Date.parse(value))) {
5594
+ throw new Error(`${label} must be an ISO timestamp`);
5790
5595
  }
5791
5596
  return value;
5792
5597
  }
5598
+ function isAlreadyExists2(error) {
5599
+ return typeof error === "object" && error !== null && "code" in error && error.code === "EEXIST";
5600
+ }
5601
+ function isNotFound3(error) {
5602
+ return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
5603
+ }
5604
+
5605
+ // src/runtime/operation-store.ts
5606
+ import { mkdir as mkdir3, readFile as readFile4, readdir as readdir2, writeFile as writeFile3 } from "fs/promises";
5607
+ import path5 from "path";
5608
+
5609
+ // src/runtime/atomic-file.ts
5610
+ import { rename as rename2 } from "fs/promises";
5611
+ var RETRIABLE_WINDOWS_RENAME_CODES = /* @__PURE__ */ new Set(["EACCES", "EBUSY", "EPERM"]);
5612
+ async function replaceFileAtomically(temporary, target, options = {}) {
5613
+ const maxAttempts = options.maxAttempts ?? 12;
5614
+ const retryDelayMs = options.retryDelayMs ?? 25;
5615
+ if (!Number.isSafeInteger(maxAttempts) || maxAttempts < 1) {
5616
+ throw new Error("MANCODE_ATOMIC_REPLACE_ATTEMPTS_INVALID");
5617
+ }
5618
+ if (!Number.isSafeInteger(retryDelayMs) || retryDelayMs < 0) {
5619
+ throw new Error("MANCODE_ATOMIC_REPLACE_DELAY_INVALID");
5620
+ }
5621
+ for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
5622
+ try {
5623
+ await rename2(temporary, target);
5624
+ return;
5625
+ } catch (error) {
5626
+ if (process.platform !== "win32" || !isRetriableWindowsRenameError(error) || attempt === maxAttempts) {
5627
+ throw error;
5628
+ }
5629
+ await delay(retryDelayMs * attempt);
5630
+ }
5631
+ }
5632
+ }
5633
+ function isRetriableWindowsRenameError(error) {
5634
+ return typeof error === "object" && error !== null && "code" in error && typeof error.code === "string" && RETRIABLE_WINDOWS_RENAME_CODES.has(
5635
+ error.code ?? ""
5636
+ );
5637
+ }
5638
+ async function delay(milliseconds) {
5639
+ await new Promise((resolve) => {
5640
+ setTimeout(resolve, milliseconds);
5641
+ });
5642
+ }
5643
+
5644
+ // src/runtime/operation-definition.ts
5645
+ var OPERATION_AUTHORIZATION_ACTIONS = {
5646
+ workflow_create: ["local_workflow_mutation", "shared_create_publish_promote"],
5647
+ workflow_update: ["local_workflow_mutation", "shared_metadata_plan_mutation"],
5648
+ requirements_draft: [
5649
+ "local_workflow_mutation",
5650
+ "shared_metadata_plan_mutation"
5651
+ ],
5652
+ requirements_finalize: [
5653
+ "local_workflow_mutation",
5654
+ "shared_metadata_plan_mutation"
5655
+ ],
5656
+ plan_revision: ["local_workflow_mutation", "shared_metadata_plan_mutation"],
5657
+ review_remediation: [
5658
+ "local_workflow_mutation",
5659
+ "shared_ledger_evidence",
5660
+ "review_skip_or_waiver"
5661
+ ],
5662
+ verification_record: ["local_workflow_mutation", "shared_ledger_evidence"],
5663
+ task_complete: [
5664
+ "local_workflow_mutation",
5665
+ "task_complete_scope_change_child_merge"
5666
+ ],
5667
+ publish_promote: ["shared_create_publish_promote"],
5668
+ handoff_transition: ["handoff_offer_cancel", "handoff_accept_reject"],
5669
+ handoff_accept: ["handoff_accept_reject"],
5670
+ scope_change_reclaim: ["task_complete_scope_change_child_merge"],
5671
+ claim_create: ["claim_create"],
5672
+ claim_renew_release: ["claim_renew_release_transfer"],
5673
+ claim_transfer: ["claim_renew_release_transfer"],
5674
+ claim_reclaim: ["claim_reclaim"],
5675
+ claim_revalidation: ["claim_renew_release_transfer"],
5676
+ checkpoint_create: [
5677
+ "local_workflow_mutation",
5678
+ "shared_metadata_plan_mutation"
5679
+ ],
5680
+ solo_handoff: ["local_workflow_mutation"],
5681
+ reframe: ["local_workflow_mutation", "shared_metadata_plan_mutation"],
5682
+ child_result_merge: [
5683
+ "local_workflow_mutation",
5684
+ "task_complete_scope_change_child_merge"
5685
+ ],
5686
+ task_head_reconcile: ["task_head_reconcile"],
5687
+ transport_migrate: ["team_policy_config_transport"],
5688
+ greenfield_initialize: ["team_policy_config_transport"],
5689
+ adapter_upgrade: ["project_maintenance"],
5690
+ project_policy_upgrade: ["project_maintenance"],
5691
+ privacy_policy_update: ["project_maintenance"],
5692
+ v3_activate: ["team_policy_config_transport"]
5693
+ };
5694
+ var prepare = (id, expectedRevisionPrefixes = [], requiredLockPrefixes = [], optional = {}) => ({
5695
+ id,
5696
+ visibility: "preparation",
5697
+ expectedRevisionPrefixes,
5698
+ requiredLockPrefixes,
5699
+ optionalExpectedRevisionPrefixes: optional.expectedRevisionPrefixes ?? [],
5700
+ optionalRequiredLockPrefixes: optional.requiredLockPrefixes ?? [],
5701
+ crashRecovery: "safe_abort"
5702
+ });
5703
+ var write = (id, expectedRevisionPrefixes, requiredLockPrefixes, optional = {}) => ({
5704
+ id,
5705
+ visibility: "business_write",
5706
+ expectedRevisionPrefixes,
5707
+ requiredLockPrefixes,
5708
+ optionalExpectedRevisionPrefixes: optional.expectedRevisionPrefixes ?? [],
5709
+ optionalRequiredLockPrefixes: optional.requiredLockPrefixes ?? [],
5710
+ crashRecovery: "forward_repair"
5711
+ });
5712
+ var commit = (id, expectedRevisionPrefixes, requiredLockPrefixes, optional = {}) => ({
5713
+ id,
5714
+ visibility: "commit",
5715
+ expectedRevisionPrefixes,
5716
+ requiredLockPrefixes,
5717
+ optionalExpectedRevisionPrefixes: optional.expectedRevisionPrefixes ?? [],
5718
+ optionalRequiredLockPrefixes: optional.requiredLockPrefixes ?? [],
5719
+ crashRecovery: "forward_repair"
5720
+ });
5721
+ var OPERATION_DEFINITIONS = {
5722
+ workflow_create: definition(
5723
+ "workflow_create",
5724
+ "publish-locator",
5725
+ false,
5726
+ false,
5727
+ [
5728
+ prepare("validate"),
5729
+ // The staging directory is private to this operation and has no task
5730
+ // locator/fence visibility. A crash here is compensable by deleting
5731
+ // verified staging, rather than forcing a fictitious forward repair.
5732
+ prepare("write-staging-aggregate", ["task:"], ["task:"]),
5733
+ prepare("validate-aggregate", ["task:"], ["task:"]),
5734
+ write("publish-task-directory", ["task:"], ["task:"]),
5735
+ write("publish-locator", ["locator:"], ["task:", "locator:"]),
5736
+ commit("commit", ["task:", "locator:"], ["task:", "locator:"])
5737
+ ]
5738
+ ),
5739
+ workflow_update: definition(
5740
+ "workflow_update",
5741
+ "write-metadata",
5742
+ true,
5743
+ false,
5744
+ [
5745
+ prepare("validate", ["task:"], ["task:"]),
5746
+ write("write-metadata", ["task:"], ["task:"]),
5747
+ write("update-task-head-fence", ["task_head:", "task:"], ["task:"]),
5748
+ commit("commit", ["task_head:", "task:"], ["task:"])
5749
+ ]
5750
+ ),
5751
+ requirements_draft: definition(
5752
+ "requirements_draft",
5753
+ "update-metadata",
5754
+ true,
5755
+ false,
5756
+ [
5757
+ prepare("validate", ["task:"], ["task:"]),
5758
+ write("write-requirements", ["requirements:", "task:"], ["task:"]),
5759
+ write(
5760
+ "mark-review-verification-stale",
5761
+ ["review:", "verification:", "task:"],
5762
+ ["task:"]
5763
+ ),
5764
+ write("update-metadata", ["task:"], ["task:"]),
5765
+ write("update-task-head-fence", ["task_head:", "task:"], ["task:"]),
5766
+ commit("commit", ["task_head:", "task:"], ["task:"])
5767
+ ]
5768
+ ),
5769
+ requirements_finalize: definition(
5770
+ "requirements_finalize",
5771
+ "update-metadata",
5772
+ true,
5773
+ false,
5774
+ [
5775
+ prepare("validate", ["task:"], ["task:"]),
5776
+ write("write-requirements", ["requirements:", "task:"], ["task:"]),
5777
+ write(
5778
+ "mark-review-verification-stale",
5779
+ ["review:", "verification:", "task:"],
5780
+ ["task:"]
5781
+ ),
5782
+ write("update-metadata", ["task:"], ["task:"]),
5783
+ write("update-task-head-fence", ["task_head:", "task:"], ["task:"]),
5784
+ commit("commit", ["task_head:", "task:"], ["task:"])
5785
+ ]
5786
+ ),
5787
+ plan_revision: definition("plan_revision", "update-metadata", true, false, [
5788
+ prepare("validate", ["task:"], ["task:"]),
5789
+ write("write-plan", ["plan:", "task:"], ["task:"]),
5790
+ write("update-metadata", ["task:"], ["task:"]),
5791
+ write(
5792
+ "mark-review-verification-stale",
5793
+ ["review:", "verification:", "task:"],
5794
+ ["task:"]
5795
+ ),
5796
+ write("update-task-head-fence", ["task_head:", "task:"], ["task:"]),
5797
+ commit("commit", ["task_head:", "task:"], ["task:"])
5798
+ ]),
5799
+ review_remediation: definition(
5800
+ "review_remediation",
5801
+ "update-metadata",
5802
+ true,
5803
+ false,
5804
+ [
5805
+ prepare("validate", ["task:", "review:"], ["task:"]),
5806
+ write("write-review-ledger", ["review:", "task:"], ["task:"]),
5807
+ write("mark-verification-stale", ["verification:", "task:"], ["task:"]),
5808
+ write("update-metadata", ["task:"], ["task:"]),
5809
+ write("update-task-head-fence", ["task_head:", "task:"], ["task:"]),
5810
+ commit("commit", ["task_head:", "task:"], ["task:"])
5811
+ ]
5812
+ ),
5813
+ verification_record: definition(
5814
+ "verification_record",
5815
+ "update-metadata",
5816
+ true,
5817
+ false,
5818
+ [
5819
+ prepare("validate", ["task:", "verification:"], ["task:"]),
5820
+ write("write-verification-ledger", ["verification:", "task:"], ["task:"]),
5821
+ write("update-metadata", ["task:"], ["task:"]),
5822
+ write("update-task-head-fence", ["task_head:", "task:"], ["task:"]),
5823
+ commit("commit", ["task_head:", "task:"], ["task:"])
5824
+ ]
5825
+ ),
5826
+ task_complete: definition(
5827
+ "task_complete",
5828
+ "write-completed-metadata",
5829
+ true,
5830
+ false,
5831
+ [
5832
+ prepare("validate-completion-gate", ["task:"], ["task:"], {
5833
+ expectedRevisionPrefixes: ["claim:"]
5834
+ }),
5835
+ write("mark-operation-pending", ["task:"], ["task:"]),
5836
+ write("release-or-transfer-claims", ["task:"], ["task:"], {
5837
+ expectedRevisionPrefixes: ["claim:"],
5838
+ requiredLockPrefixes: ["claim:"]
5839
+ }),
5840
+ write("write-completed-metadata", ["task:"], ["task:"]),
5841
+ write("update-task-head-fence", ["task_head:", "task:"], ["task:"]),
5842
+ commit("commit", ["task_head:", "task:"], ["task:"])
5843
+ ]
5844
+ ),
5845
+ publish_promote: definition(
5846
+ "publish_promote",
5847
+ "publish-destination",
5848
+ true,
5849
+ true,
5850
+ [
5851
+ prepare("validate-privacy-and-paths", ["task:"], ["task:"]),
5852
+ // The destination staging directory remains private until its atomic
5853
+ // rename, so it is safely compensable just like workflow creation.
5854
+ prepare("stage-destination", ["task:"], ["task:"]),
5855
+ write("mark-source-operation-pending", ["task:"], ["task:"]),
5856
+ write(
5857
+ "publish-destination",
5858
+ ["task:", "locator:"],
5859
+ ["task:", "locator:"]
5860
+ ),
5861
+ write("write-source-successor", ["task:"], ["task:"]),
5862
+ write("publish-destination-locator", ["locator:"], ["locator:"]),
5863
+ write("update-task-head-fence", ["task_head:", "task:"], ["task:"]),
5864
+ commit("commit", ["task_head:", "task:"], ["task:"])
5865
+ ]
5866
+ ),
5867
+ handoff_transition: definition(
5868
+ "handoff_transition",
5869
+ "write-handoff",
5870
+ false,
5871
+ false,
5872
+ [
5873
+ prepare("validate", ["task:", "handoff:"], ["task:", "handoff:"]),
5874
+ write("write-handoff", ["task:", "handoff:"], ["task:", "handoff:"]),
5875
+ commit("commit", ["task:", "handoff:"], ["task:", "handoff:"])
5876
+ ]
5877
+ ),
5878
+ handoff_accept: definition("handoff_accept", "accept-handoff", true, false, [
5879
+ prepare(
5880
+ "validate",
5881
+ ["task:", "handoff:", "checkpoint:"],
5882
+ ["task:", "handoff:", "checkpoint:"],
5883
+ {
5884
+ expectedRevisionPrefixes: ["claim:"],
5885
+ requiredLockPrefixes: ["claim:"]
5886
+ }
5887
+ ),
5888
+ write("mark-task-operation-pending", ["task:"], ["task:"]),
5889
+ write("create-pending-successor-claims", ["task:"], ["task:"], {
5890
+ expectedRevisionPrefixes: ["claim:"],
5891
+ requiredLockPrefixes: ["claim:"]
5892
+ }),
5893
+ write("transfer-old-claims", ["task:"], ["task:"], {
5894
+ expectedRevisionPrefixes: ["claim:"],
5895
+ requiredLockPrefixes: ["claim:"]
5896
+ }),
5897
+ write("update-owner-and-checkpoint", ["task:", "checkpoint:"], ["task:"]),
5898
+ write("activate-successor-claims", ["task:"], ["task:"], {
5899
+ expectedRevisionPrefixes: ["claim:"],
5900
+ requiredLockPrefixes: ["claim:"]
5901
+ }),
5902
+ write("accept-handoff", ["handoff:", "task:"], ["task:", "handoff:"]),
5903
+ write("update-task-head-fence", ["task_head:", "task:"], ["task:"]),
5904
+ commit("commit", ["task_head:", "task:"], ["task:"])
5905
+ ]),
5906
+ scope_change_reclaim: definition(
5907
+ "scope_change_reclaim",
5908
+ "activate-successor-claims",
5909
+ true,
5910
+ false,
5911
+ [
5912
+ prepare("validate", ["task:"], ["task:"], {
5913
+ expectedRevisionPrefixes: ["claim:"],
5914
+ requiredLockPrefixes: ["claim:"]
5915
+ }),
5916
+ write("mark-task-operation-pending", ["task:"], ["task:"]),
5917
+ write(
5918
+ "write-scope-changed-checkpoint",
5919
+ ["checkpoint:", "task:"],
5920
+ ["task:"]
5921
+ ),
5922
+ write("create-pending-successor-claims", ["task:"], ["task:"], {
5923
+ expectedRevisionPrefixes: ["claim:"],
5924
+ requiredLockPrefixes: ["claim:"]
5925
+ }),
5926
+ write("terminate-old-claims", ["task:"], ["task:"], {
5927
+ expectedRevisionPrefixes: ["claim:"],
5928
+ requiredLockPrefixes: ["claim:"]
5929
+ }),
5930
+ write(
5931
+ "mark-review-verification-stale",
5932
+ ["review:", "verification:", "task:"],
5933
+ ["task:"]
5934
+ ),
5935
+ write("update-metadata-scope", ["task:"], ["task:"]),
5936
+ write("activate-successor-claims", ["task:"], ["task:"], {
5937
+ expectedRevisionPrefixes: ["claim:"],
5938
+ requiredLockPrefixes: ["claim:"]
5939
+ }),
5940
+ write("update-task-head-fence", ["task_head:", "task:"], ["task:"]),
5941
+ commit("commit", ["task_head:", "task:"], ["task:"])
5942
+ ]
5943
+ ),
5944
+ claim_create: definition(
5945
+ "claim_create",
5946
+ "create-active-claim",
5947
+ false,
5948
+ false,
5949
+ [
5950
+ prepare("validate", ["task:"], ["task:"]),
5951
+ write("create-active-claim", ["claim:", "task:"], ["task:", "claim:"]),
5952
+ commit("commit", ["claim:", "task:"], ["task:", "claim:"])
5953
+ ]
5954
+ ),
5955
+ claim_renew_release: definition(
5956
+ "claim_renew_release",
5957
+ "update-claim",
5958
+ false,
5959
+ false,
5960
+ [
5961
+ prepare("validate", ["task:", "claim:"], ["task:", "claim:"]),
5962
+ write("update-claim", ["task:", "claim:"], ["task:", "claim:"]),
5963
+ commit("commit", ["task:", "claim:"], ["task:", "claim:"])
5964
+ ]
5965
+ ),
5966
+ claim_transfer: definition(
5967
+ "claim_transfer",
5968
+ "activate-successor-claim",
5969
+ false,
5970
+ false,
5971
+ [
5972
+ prepare("validate", ["task:", "claim:"], ["task:", "claim:"]),
5973
+ write(
5974
+ "create-pending-successor-claim",
5975
+ ["task:", "claim:"],
5976
+ ["task:", "claim:"]
5977
+ ),
5978
+ write(
5979
+ "transfer-predecessor-claim",
5980
+ ["task:", "claim:"],
5981
+ ["task:", "claim:"]
5982
+ ),
5983
+ write(
5984
+ "activate-successor-claim",
5985
+ ["task:", "claim:"],
5986
+ ["task:", "claim:"]
5987
+ ),
5988
+ commit("commit", ["task:", "claim:"], ["task:", "claim:"])
5989
+ ]
5990
+ ),
5991
+ claim_reclaim: definition("claim_reclaim", "expire-claim", false, false, [
5992
+ prepare("validate", ["task:", "claim:"], ["task:", "claim:"]),
5993
+ write("expire-claim", ["task:", "claim:"], ["task:", "claim:"]),
5994
+ commit("commit", ["task:", "claim:"], ["task:", "claim:"])
5995
+ ]),
5996
+ claim_revalidation: definition(
5997
+ "claim_revalidation",
5998
+ "update-claim-validation",
5999
+ true,
6000
+ false,
6001
+ [
6002
+ prepare("validate", ["task:", "claim:"], ["task:", "claim:"]),
6003
+ write("mark-task-operation-pending", ["task:"], ["task:"]),
6004
+ write("write-base-changed-checkpoint", ["task:"], ["task:"], {
6005
+ expectedRevisionPrefixes: ["checkpoint:"]
6006
+ }),
6007
+ write(
6008
+ "update-claim-validation",
6009
+ ["claim:", "task:"],
6010
+ ["task:", "claim:"]
6011
+ ),
6012
+ write("complete-task-validation", ["task:"], ["task:"]),
6013
+ write("update-task-head-fence", ["task_head:", "task:"], ["task:"]),
6014
+ commit("commit", ["task_head:", "task:"], ["task:"])
6015
+ ]
6016
+ ),
6017
+ checkpoint_create: definition(
6018
+ "checkpoint_create",
6019
+ "update-metadata-checkpoint-ref",
6020
+ true,
6021
+ false,
6022
+ [
6023
+ prepare("validate", ["task:"], ["task:"]),
6024
+ write("mark-task-operation-pending", ["task:"], ["task:"]),
6025
+ write("write-checkpoint", ["checkpoint:", "task:"], ["task:"]),
6026
+ write("update-metadata-checkpoint-ref", ["task:"], ["task:"]),
6027
+ write("update-task-head-fence", ["task_head:", "task:"], ["task:"]),
6028
+ commit("commit", ["task_head:", "task:"], ["task:"])
6029
+ ]
6030
+ ),
6031
+ solo_handoff: definition(
6032
+ "solo_handoff",
6033
+ "write-workflow-assignment",
6034
+ false,
6035
+ false,
6036
+ [
6037
+ prepare("validate", ["task:"], ["task:"]),
6038
+ write("write-workflow-assignment", ["task:"], ["task:"]),
6039
+ // The session pointer is a compensable local projection, rather than
6040
+ // journaled workflow authority. It is intentionally not part of the
6041
+ // operation CAS/lock set: a failed or later-replaced pointer must never
6042
+ // block repair of the durable solo assignment.
6043
+ write("update-session-pointer", ["task:"], ["task:"]),
6044
+ commit("commit", ["task:"], ["task:"])
6045
+ ]
6046
+ ),
6047
+ reframe: definition("reframe", "commit-reframed-metadata", true, false, [
6048
+ prepare(
6049
+ "validate",
6050
+ [
6051
+ "task:",
6052
+ "requirements:",
6053
+ "plan:",
6054
+ "review:",
6055
+ "verification:",
6056
+ "archive:",
6057
+ "checkpoint:"
6058
+ ],
6059
+ ["task:", "archive:", "checkpoint:"],
6060
+ {
6061
+ expectedRevisionPrefixes: ["claim:"],
6062
+ requiredLockPrefixes: ["claim:"]
6063
+ }
6064
+ ),
6065
+ write("mark-task-operation-pending", ["task:"], ["task:"]),
6066
+ write(
6067
+ "archive-requirements-plan",
6068
+ ["archive:", "requirements:", "plan:", "task:"],
6069
+ ["archive:", "task:"]
6070
+ ),
6071
+ write("release-active-claims", ["task:"], ["task:"], {
6072
+ expectedRevisionPrefixes: ["claim:"],
6073
+ requiredLockPrefixes: ["claim:"]
6074
+ }),
6075
+ write("write-requirements-draft", ["requirements:", "task:"], ["task:"]),
6076
+ write(
6077
+ "mark-review-verification-stale",
6078
+ ["review:", "verification:", "task:"],
6079
+ ["task:"]
6080
+ ),
6081
+ write(
6082
+ "write-reframe-checkpoint",
6083
+ ["checkpoint:", "task:"],
6084
+ ["checkpoint:", "task:"]
6085
+ ),
6086
+ write("commit-reframed-metadata", ["task:"], ["task:"]),
6087
+ write("update-task-head-fence", ["task_head:", "task:"], ["task:"]),
6088
+ commit("commit", ["task_head:", "task:"], ["task:"])
6089
+ ]),
6090
+ child_result_merge: definition(
6091
+ "child_result_merge",
6092
+ "update-parent-metadata",
6093
+ true,
6094
+ false,
6095
+ [
6096
+ prepare("validate-parent-snapshot", ["task:", "checkpoint:"], ["task:"]),
6097
+ write("mark-parent-operation-pending", ["task:"], ["task:"]),
6098
+ write("write-merge-checkpoint", ["checkpoint:", "task:"], ["task:"]),
6099
+ write("update-parent-metadata", ["task:"], ["task:"]),
6100
+ write("update-task-head-fence", ["task_head:", "task:"], ["task:"]),
6101
+ commit("commit", ["task_head:", "task:"], ["task:"])
6102
+ ]
6103
+ ),
6104
+ task_head_reconcile: definition(
6105
+ "task_head_reconcile",
6106
+ "adopt-task-head-fence",
6107
+ true,
6108
+ false,
6109
+ [
6110
+ prepare(
6111
+ "validate-clean-store-and-git-reachability",
6112
+ ["task:", "task_head:"],
6113
+ ["task:", "task_head:"]
6114
+ ),
6115
+ prepare(
6116
+ "confirm-adoption",
6117
+ ["task:", "task_head:"],
6118
+ ["task:", "task_head:"]
6119
+ ),
6120
+ write(
6121
+ "adopt-task-head-fence",
6122
+ ["task_head:", "task:"],
6123
+ ["task:", "task_head:"]
6124
+ ),
6125
+ commit("commit", ["task_head:", "task:"], ["task:", "task_head:"])
6126
+ ]
6127
+ ),
6128
+ transport_migrate: definition(
6129
+ "transport_migrate",
6130
+ "switch-config-authority",
6131
+ true,
6132
+ true,
6133
+ [
6134
+ prepare(
6135
+ "freeze-shared-coordination-writes",
6136
+ ["config:", "task_head:"],
6137
+ ["config:"]
6138
+ ),
6139
+ prepare(
6140
+ "validate-old-authority",
6141
+ ["config:", "task_head:", "claim:", "handoff:"],
6142
+ ["config:"]
6143
+ ),
6144
+ write(
6145
+ "stage-new-authority",
6146
+ ["config:", "task_head:", "claim:", "handoff:"],
6147
+ ["config:"]
6148
+ ),
6149
+ write(
6150
+ "establish-new-epoch",
6151
+ ["config:", "task_head:", "claim:", "handoff:"],
6152
+ ["config:"]
6153
+ ),
6154
+ write("switch-config-authority", ["config:"], ["config:"]),
6155
+ commit("commit", ["config:"], ["config:"])
6156
+ ]
6157
+ ),
6158
+ greenfield_initialize: definition(
6159
+ "greenfield_initialize",
6160
+ "publish-v3-root",
6161
+ false,
6162
+ false,
6163
+ [
6164
+ prepare("verify-no-legacy-authority"),
6165
+ // The named staging root is still private and can be verified then
6166
+ // deleted before the atomic .mancode publication boundary.
6167
+ prepare(
6168
+ "write-initializing-staging-root",
6169
+ ["schema:", "config:"],
6170
+ ["schema:"]
6171
+ ),
6172
+ prepare(
6173
+ "write-v3-config-policy-adapters",
6174
+ ["schema:", "config:"],
6175
+ ["schema:"]
6176
+ ),
6177
+ write("publish-v3-root", ["schema:", "config:"], ["schema:"]),
6178
+ write("register-workspace-binding", ["binding:"], ["binding:"]),
6179
+ write("publish-managed-adapters", ["adapter:"], ["adapter:"]),
6180
+ write("activate-v3-manifest", ["schema:"], ["schema:"]),
6181
+ commit("commit", ["schema:"], ["schema:"])
6182
+ ]
6183
+ ),
6184
+ adapter_upgrade: definition(
6185
+ "adapter_upgrade",
6186
+ "replace-managed-adapters",
6187
+ false,
6188
+ false,
6189
+ [
6190
+ prepare("validate", ["adapter:"], ["adapter:"], {
6191
+ expectedRevisionPrefixes: ["schema:"],
6192
+ requiredLockPrefixes: ["schema:"]
6193
+ }),
6194
+ write("replace-managed-adapters", ["adapter:"], ["adapter:"]),
6195
+ write("update-adapter-inventory", [], [], {
6196
+ expectedRevisionPrefixes: ["schema:"],
6197
+ requiredLockPrefixes: ["schema:"]
6198
+ }),
6199
+ commit("verify", ["adapter:"], ["adapter:"], {
6200
+ expectedRevisionPrefixes: ["schema:"],
6201
+ requiredLockPrefixes: ["schema:"]
6202
+ }),
6203
+ commit("commit", ["adapter:"], ["adapter:"], {
6204
+ expectedRevisionPrefixes: ["schema:"],
6205
+ requiredLockPrefixes: ["schema:"]
6206
+ })
6207
+ ]
6208
+ ),
6209
+ project_policy_upgrade: definition(
6210
+ "project_policy_upgrade",
6211
+ "write-manifest",
6212
+ false,
6213
+ false,
6214
+ [
6215
+ prepare("validate", ["schema:"], ["schema:"]),
6216
+ write("write-manifest", ["schema:"], ["schema:"]),
6217
+ write("verify-manifest", ["schema:"], ["schema:"]),
6218
+ commit("commit", ["schema:"], ["schema:"])
6219
+ ]
6220
+ ),
6221
+ privacy_policy_update: definition(
6222
+ "privacy_policy_update",
6223
+ "write-manifest",
6224
+ false,
6225
+ false,
6226
+ [
6227
+ prepare("validate", ["schema:"], ["schema:"]),
6228
+ write("write-remote-policy", ["schema:"], ["schema:"]),
6229
+ write("write-exclusions", ["schema:"], ["schema:"]),
6230
+ write("write-policy", ["schema:"], ["schema:"]),
6231
+ write("write-manifest", ["schema:"], ["schema:"]),
6232
+ commit("commit", ["schema:"], ["schema:"])
6233
+ ]
6234
+ ),
6235
+ v3_activate: definition("v3_activate", "activate-manifest", true, true, [
6236
+ prepare(
6237
+ "validate-staged-migration",
6238
+ ["schema:", "config:", "stage:"],
6239
+ ["schema:", "config:", "stage:"],
6240
+ {
6241
+ expectedRevisionPrefixes: ["task:", "task_head:", "adapter:"],
6242
+ requiredLockPrefixes: ["task:", "task_head:", "adapter:"]
6243
+ }
6244
+ ),
6245
+ write(
6246
+ "mark-manifest-activating",
6247
+ ["schema:", "stage:"],
6248
+ ["schema:", "stage:"]
6249
+ ),
6250
+ write("replace-managed-adapters", ["adapter:"], ["adapter:"]),
6251
+ write("promote-staged-tasks", [], [], {
6252
+ expectedRevisionPrefixes: ["task:", "task_head:"],
6253
+ requiredLockPrefixes: ["task:", "task_head:"]
6254
+ }),
6255
+ write(
6256
+ "record-adapter-inventory-and-baseline",
6257
+ ["schema:", "config:"],
6258
+ ["schema:", "config:"]
6259
+ ),
6260
+ write("activate-manifest", ["schema:", "stage:"], ["schema:", "stage:"]),
6261
+ commit("commit", ["schema:", "stage:"], ["schema:", "stage:"])
6262
+ ])
6263
+ };
6264
+ var OPERATION_CRASH_FIXTURES = Object.fromEntries(
6265
+ Object.values(OPERATION_DEFINITIONS).map((definition2) => [
6266
+ definition2.type,
6267
+ [
6268
+ {
6269
+ operationType: definition2.type,
6270
+ crashAfter: "prepared",
6271
+ expectedRecovery: "safe_abort"
6272
+ },
6273
+ ...definition2.steps.map((step) => ({
6274
+ operationType: definition2.type,
6275
+ crashAfter: step.id,
6276
+ expectedRecovery: step.crashRecovery
6277
+ }))
6278
+ ]
6279
+ ])
6280
+ );
6281
+ function getOperationDefinition(operationType) {
6282
+ return OPERATION_DEFINITIONS[operationType];
6283
+ }
6284
+ function assertOperationJournalMatchesDefinition(journal) {
6285
+ const definition2 = getOperationDefinition(journal.type);
6286
+ assertOperationAuthorizationAction(journal);
6287
+ const actualSteps = journal.steps.map((step) => step.id);
6288
+ const expectedSteps = definition2.steps.map((step) => step.id);
6289
+ if (actualSteps.length !== expectedSteps.length || actualSteps.some((step, index) => step !== expectedSteps[index])) {
6290
+ throw new Error(
6291
+ `operation ${journal.type} steps do not match its machine-readable definition`
6292
+ );
6293
+ }
6294
+ const expectedRevisionPrefixes = new Set(
6295
+ definition2.steps.flatMap((step) => step.expectedRevisionPrefixes)
6296
+ );
6297
+ const lockPrefixes = new Set(
6298
+ definition2.steps.flatMap((step) => step.requiredLockPrefixes)
6299
+ );
6300
+ const optionalExpectedRevisionPrefixes = new Set(
6301
+ definition2.steps.flatMap((step) => step.optionalExpectedRevisionPrefixes)
6302
+ );
6303
+ const optionalLockPrefixes = new Set(
6304
+ definition2.steps.flatMap((step) => step.optionalRequiredLockPrefixes)
6305
+ );
6306
+ const sharedTask = journal.entityLocks.some(
6307
+ (key) => key.startsWith("task:shared:")
6308
+ );
6309
+ if (!sharedTask) {
6310
+ expectedRevisionPrefixes.delete("task_head:");
6311
+ }
6312
+ assertPrefixCoverage(
6313
+ Object.keys(journal.expectedRevisions),
6314
+ expectedRevisionPrefixes,
6315
+ "expected revisions"
6316
+ );
6317
+ assertPrefixCoverage(journal.entityLocks, lockPrefixes, "entity locks");
6318
+ assertOptionalPrefixPairs(
6319
+ Object.keys(journal.expectedRevisions),
6320
+ journal.entityLocks,
6321
+ optionalExpectedRevisionPrefixes,
6322
+ optionalLockPrefixes
6323
+ );
6324
+ if (definition2.requiresSharedTaskHeadFence && sharedTask) {
6325
+ assertPrefixCoverage(
6326
+ Object.keys(journal.expectedRevisions),
6327
+ /* @__PURE__ */ new Set(["task_head:"]),
6328
+ "expected revisions"
6329
+ );
6330
+ assertPrefixCoverage(
6331
+ journal.entityLocks,
6332
+ /* @__PURE__ */ new Set(["task_head:"]),
6333
+ "entity locks"
6334
+ );
6335
+ }
6336
+ }
6337
+ function assertOperationAuthorizationAction(journal) {
6338
+ const definition2 = getOperationDefinition(journal.type);
6339
+ if (!definition2.authorizationActions.includes(journal.authorizationBasis.action)) {
6340
+ throw new Error("MANCODE_OPERATION_AUTHORIZATION_ACTION_MISMATCH");
6341
+ }
6342
+ }
6343
+ function definition(type, primaryCommitStep, requiresSharedTaskHeadFence, allowsSecondaryReservations, steps) {
6344
+ if (!steps.some((step) => step.id === primaryCommitStep)) {
6345
+ throw new Error(`operation ${type} has no primary commit step`);
6346
+ }
6347
+ for (const step of steps) {
6348
+ for (const prefix of step.optionalExpectedRevisionPrefixes) {
6349
+ if (step.expectedRevisionPrefixes.includes(prefix)) {
6350
+ throw new Error(
6351
+ `operation ${type} repeats optional expected revision prefix ${prefix}`
6352
+ );
6353
+ }
6354
+ }
6355
+ for (const prefix of step.optionalRequiredLockPrefixes) {
6356
+ if (step.requiredLockPrefixes.includes(prefix)) {
6357
+ throw new Error(
6358
+ `operation ${type} repeats optional lock prefix ${prefix}`
6359
+ );
6360
+ }
6361
+ }
6362
+ }
6363
+ return {
6364
+ schemaVersion: 1,
6365
+ type,
6366
+ authorizationActions: OPERATION_AUTHORIZATION_ACTIONS[type],
6367
+ primaryCommitStep,
6368
+ requiresSharedTaskHeadFence,
6369
+ allowsSecondaryReservations,
6370
+ steps
6371
+ };
6372
+ }
6373
+ function assertPrefixCoverage(values, prefixes, label) {
6374
+ for (const prefix of prefixes) {
6375
+ if (!values.some((value) => value.startsWith(prefix))) {
6376
+ throw new Error(`operation journal ${label} are missing ${prefix}`);
6377
+ }
6378
+ }
6379
+ }
6380
+ function assertOptionalPrefixPairs(expectedRevisions, entityLocks, optionalExpectedRevisionPrefixes, optionalLockPrefixes) {
6381
+ for (const prefix of optionalExpectedRevisionPrefixes) {
6382
+ if (expectedRevisions.some((value) => value.startsWith(prefix)) && !entityLocks.some((value) => value.startsWith(prefix))) {
6383
+ throw new Error(
6384
+ `operation journal entity locks are missing optional ${prefix}`
6385
+ );
6386
+ }
6387
+ }
6388
+ for (const prefix of optionalLockPrefixes) {
6389
+ if (entityLocks.some((value) => value.startsWith(prefix)) && !expectedRevisions.some((value) => value.startsWith(prefix))) {
6390
+ throw new Error(
6391
+ `operation journal expected revisions are missing optional ${prefix}`
6392
+ );
6393
+ }
6394
+ }
6395
+ }
6396
+
6397
+ // src/runtime/operation-store.ts
6398
+ async function createPreparedOperationJournal(store, journal) {
6399
+ assertOperationAuthorizationAction(journal);
6400
+ if (journal.state !== "prepared") {
6401
+ throw new Error("only a prepared operation journal may be created");
6402
+ }
6403
+ if (journal.primaryStoreId !== store.storeId) {
6404
+ throw new Error(
6405
+ "operation journal primaryStoreId does not match its home store"
6406
+ );
6407
+ }
6408
+ assertOperationReservationTopology(journal);
6409
+ if (journal.type !== "transport_migrate" && store.kind !== "checkout_local") {
6410
+ const migrationInProgress = (await listUnfinishedOperationJournals(store)).some((candidate) => candidate.type === "transport_migrate");
6411
+ if (migrationInProgress) {
6412
+ throw new Error("MANCODE_TRANSPORT_MIGRATION_FROZEN");
6413
+ }
6414
+ }
6415
+ const directory = operationDirectory(store);
6416
+ const target = operationJournalPath(store, journal.operationId);
6417
+ await mkdir3(directory, { recursive: true });
6418
+ try {
6419
+ await writeFile3(target, serialize2(journal), {
6420
+ encoding: "utf8",
6421
+ flag: "wx"
6422
+ });
6423
+ return journal;
6424
+ } catch (error) {
6425
+ if (!isAlreadyExists3(error)) throw error;
6426
+ const existing = await readOperationJournal(store, journal.operationId);
6427
+ if (existing !== null && operationJournalDigest(existing) === operationJournalDigest(journal)) {
6428
+ return existing;
6429
+ }
6430
+ throw new Error("MANCODE_OPERATION_JOURNAL_CONFLICT");
6431
+ }
6432
+ }
6433
+ async function prepareOperationStores(input) {
6434
+ const { primaryStore, journal } = input;
6435
+ validateSecondaryStores(primaryStore, journal, input.secondaryStores);
6436
+ await createPreparedOperationJournal(primaryStore, journal);
6437
+ const createdAt = (input.now ?? /* @__PURE__ */ new Date()).toISOString();
6438
+ const reservations = [];
6439
+ for (const secondaryStore of input.secondaryStores) {
6440
+ const reservation = createOperationReservation(
6441
+ journal,
6442
+ secondaryStore.storeId,
6443
+ createdAt
6444
+ );
6445
+ reservations.push(
6446
+ await writeOperationReservation(secondaryStore, reservation)
6447
+ );
6448
+ }
6449
+ return reservations;
6450
+ }
6451
+ async function readOperationJournal(store, operationId) {
6452
+ try {
6453
+ assertUlid(operationId, "operation journal operationId");
6454
+ const raw = await readFile4(
6455
+ operationJournalPath(store, operationId),
6456
+ "utf8"
6457
+ );
6458
+ return parseOperationJournal(JSON.parse(raw));
6459
+ } catch (error) {
6460
+ if (isNotFound4(error)) return null;
6461
+ if (error instanceof SyntaxError) {
6462
+ throw new Error("MANCODE_OPERATION_JOURNAL_CORRUPT");
6463
+ }
6464
+ throw error;
6465
+ }
6466
+ }
6467
+ async function updateOperationJournal(store, next, options) {
6468
+ const previous = await readOperationJournal(store, next.operationId);
6469
+ if (previous === null) throw new Error("MANCODE_OPERATION_JOURNAL_NOT_FOUND");
6470
+ if (previous.primaryStoreId !== store.storeId || next.primaryStoreId !== store.storeId) {
6471
+ throw new Error(
6472
+ "operation journal must be updated in its primary home store"
6473
+ );
6474
+ }
6475
+ assertOperationJournalTransition(previous, next, options);
6476
+ await atomicWriteOperationJournal(store, next);
6477
+ return next;
6478
+ }
6479
+ async function listUnfinishedOperationJournals(store) {
6480
+ let entries;
6481
+ try {
6482
+ entries = await readdir2(operationDirectory(store));
6483
+ } catch (error) {
6484
+ if (isNotFound4(error)) return [];
6485
+ throw error;
6486
+ }
6487
+ const journals = [];
6488
+ for (const entry of entries) {
6489
+ if (!entry.endsWith(".json")) continue;
6490
+ const operationId = entry.slice(0, -".json".length);
6491
+ try {
6492
+ assertUlid(operationId, "operation journal filename");
6493
+ } catch {
6494
+ throw new Error("MANCODE_OPERATION_JOURNAL_CORRUPT");
6495
+ }
6496
+ const journal = await readOperationJournal(store, operationId);
6497
+ if (journal !== null && journal.state !== "committed" && journal.state !== "aborted") {
6498
+ journals.push(journal);
6499
+ }
6500
+ }
6501
+ return journals.sort(
6502
+ (left, right) => Buffer.from(left.operationId, "utf8").compare(
6503
+ Buffer.from(right.operationId, "utf8")
6504
+ )
6505
+ );
6506
+ }
6507
+ function operationJournalPath(store, operationId) {
6508
+ assertUlid(operationId, "operation journal operationId");
6509
+ return path5.join(operationDirectory(store), `${operationId}.json`);
6510
+ }
6511
+ async function atomicWriteOperationJournal(store, journal) {
6512
+ const target = operationJournalPath(store, journal.operationId);
6513
+ const temporary = path5.join(
6514
+ operationDirectory(store),
6515
+ `.${journal.operationId}.${process.pid}.${Date.now()}.tmp`
6516
+ );
6517
+ await writeFile3(temporary, serialize2(journal), {
6518
+ encoding: "utf8",
6519
+ flag: "wx"
6520
+ });
6521
+ await replaceFileAtomically(temporary, target);
6522
+ }
6523
+ function validateSecondaryStores(primaryStore, journal, secondaryStores) {
6524
+ if (journal.primaryStoreId !== primaryStore.storeId) {
6525
+ throw new Error(
6526
+ "operation journal primaryStoreId does not match primaryStore"
6527
+ );
6528
+ }
6529
+ const expectedStoreIds = new Set(
6530
+ journal.secondaryReservations.map((reservation) => reservation.storeId)
6531
+ );
6532
+ const suppliedStoreIds = /* @__PURE__ */ new Set();
6533
+ for (const store of secondaryStores) {
6534
+ if (store.storeId === primaryStore.storeId || suppliedStoreIds.has(store.storeId)) {
6535
+ throw new Error(
6536
+ "operation secondary stores must be unique and exclude primaryStore"
6537
+ );
6538
+ }
6539
+ suppliedStoreIds.add(store.storeId);
6540
+ }
6541
+ if (expectedStoreIds.size !== suppliedStoreIds.size || [...expectedStoreIds].some((storeId) => !suppliedStoreIds.has(storeId))) {
6542
+ throw new Error(
6543
+ "operation secondary stores do not match the journal reservations"
6544
+ );
6545
+ }
6546
+ }
6547
+ function serialize2(journal) {
6548
+ return `${JSON.stringify(journal, null, 2)}
6549
+ `;
6550
+ }
6551
+ function isAlreadyExists3(error) {
6552
+ return typeof error === "object" && error !== null && "code" in error && error.code === "EEXIST";
6553
+ }
6554
+ function isNotFound4(error) {
6555
+ return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
6556
+ }
6557
+
6558
+ // src/runtime/project-write-barrier.ts
6559
+ var PROJECT_SCHEMA_LOCK = "schema:project";
6560
+ async function acquireProjectWriteBarrier(runtime, operationId, now) {
6561
+ const localStore = resolveLocalEntityHomeStore(
6562
+ runtime.entityHomeStoreContext
6563
+ );
6564
+ const [lock] = await acquireEntityLocks(
6565
+ localStore,
6566
+ operationId,
6567
+ [PROJECT_SCHEMA_LOCK],
6568
+ { now }
6569
+ );
6570
+ if (lock === void 0) {
6571
+ throw new Error("MANCODE_LOCK_HELD");
6572
+ }
6573
+ try {
6574
+ const unfinished = await listUnfinishedOperationJournals(localStore);
6575
+ if (unfinished.some(
6576
+ (journal) => journal.entityLocks.includes(PROJECT_SCHEMA_LOCK)
6577
+ )) {
6578
+ throw new Error("MANCODE_OPERATION_REPAIR_REQUIRED");
6579
+ }
6580
+ return lock;
6581
+ } catch (error) {
6582
+ await lock.release().catch(() => void 0);
6583
+ throw error;
6584
+ }
6585
+ }
6586
+
6587
+ // src/context/privacy-policy.ts
6588
+ import { lstat as lstat2, readFile as readFile5, readdir as readdir3 } from "fs/promises";
6589
+ import path6 from "path";
6590
+
6591
+ // src/context/manifest.ts
6592
+ var ACTIVATION_STATES = /* @__PURE__ */ new Set([
6593
+ "initializing",
6594
+ "dual_read",
6595
+ "activating",
6596
+ "v3_active",
6597
+ "repair_required"
6598
+ ]);
6599
+ var MANAGED_ADAPTERS = [
6600
+ "claude-code",
6601
+ "codex",
6602
+ "cursor",
6603
+ "copilot",
6604
+ "zcode",
6605
+ "kimi-code",
6606
+ "qoder",
6607
+ "dsh"
6608
+ ];
6609
+ var VERSION_PATTERN = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/;
6610
+ var DIGEST_PATTERN13 = /^sha256:[a-f0-9]{64}$/;
6611
+ function parseSchemaManifest(value) {
6612
+ assertRecord(value, "schema manifest");
6613
+ if (value.manifestVersion !== 1 && value.manifestVersion !== 2 && value.manifestVersion !== 3) {
6614
+ throw new Error(
6615
+ `MANCODE_MANIFEST_VERSION_UNSUPPORTED: observed=${String(value.manifestVersion)} supported=1,2,3 requiredWriter=0.6.5`
6616
+ );
6617
+ }
6618
+ const manifestVersion = value.manifestVersion;
6619
+ assertKnownKeys(
6620
+ value,
6621
+ [
6622
+ "manifestVersion",
6623
+ "layoutVersion",
6624
+ "epoch",
6625
+ "activationState",
6626
+ "minReaderVersion",
6627
+ "minWriterVersion",
6628
+ "activatedAt",
6629
+ "legacyBaseline",
6630
+ "managedAdapters",
6631
+ "lastOperationId",
6632
+ ...manifestVersion >= 2 ? ["workflowPolicyDefaults"] : [],
6633
+ ...manifestVersion === 3 ? ["privacyPolicy"] : []
6634
+ ],
6635
+ "schema manifest"
6636
+ );
6637
+ if (value.layoutVersion !== 3) {
6638
+ throw new Error("schema manifest must use layoutVersion 3");
6639
+ }
6640
+ assertUlid(value.epoch, "schema manifest epoch");
6641
+ if (typeof value.activationState !== "string" || !ACTIVATION_STATES.has(value.activationState)) {
6642
+ throw new Error("schema manifest activationState is invalid");
6643
+ }
6644
+ const common = {
6645
+ layoutVersion: 3,
6646
+ epoch: value.epoch,
6647
+ activationState: value.activationState,
6648
+ minReaderVersion: parseVersion(value.minReaderVersion, "minReaderVersion"),
6649
+ minWriterVersion: parseVersion(value.minWriterVersion, "minWriterVersion"),
6650
+ activatedAt: parseTimestampOrNull3(value.activatedAt, "activatedAt"),
6651
+ legacyBaseline: parseLegacyBaseline(value.legacyBaseline),
6652
+ managedAdapters: parseManagedAdapters(value.managedAdapters),
6653
+ lastOperationId: parseUlidOrNull8(value.lastOperationId, "lastOperationId")
6654
+ };
6655
+ const manifest = manifestVersion === 1 ? { manifestVersion: 1, ...common } : manifestVersion === 2 ? {
6656
+ manifestVersion: 2,
6657
+ ...common,
6658
+ workflowPolicyDefaults: parseWorkflowPolicyDefaults(
6659
+ value.workflowPolicyDefaults
6660
+ )
6661
+ } : {
6662
+ manifestVersion: 3,
6663
+ ...common,
6664
+ workflowPolicyDefaults: parsePrivacyManifestPlanningDefaults(
6665
+ value.workflowPolicyDefaults
6666
+ ),
6667
+ privacyPolicy: parsePrivacyPolicyReference(value.privacyPolicy)
6668
+ };
6669
+ assertManifestStateShape(manifest);
6670
+ if (manifest.manifestVersion >= 2 && (compareVersions(manifest.minReaderVersion, "0.4.0") < 0 || compareVersions(manifest.minWriterVersion, "0.4.0") < 0)) {
6671
+ throw new Error(
6672
+ "schema manifest V2 requires minReaderVersion and minWriterVersion 0.4.0 or newer"
6673
+ );
6674
+ }
6675
+ if (manifest.manifestVersion === 3 && (compareVersions(manifest.minReaderVersion, "0.6.5") < 0 || compareVersions(manifest.minWriterVersion, "0.6.5") < 0))
6676
+ throw new Error(
6677
+ "schema manifest V3 requires minReaderVersion and minWriterVersion 0.6.5 or newer"
6678
+ );
6679
+ return manifest;
6680
+ }
6681
+ function serializeSchemaManifest(value) {
6682
+ return `${JSON.stringify(parseSchemaManifest(value), null, 2)}
6683
+ `;
6684
+ }
6685
+ function managedAdapterNames(inventory) {
6686
+ return MANAGED_ADAPTERS.filter((adapter) => inventory[adapter] !== void 0);
6687
+ }
6688
+ function assertSchemaManifestTransition(previous, next) {
6689
+ if (previous.manifestVersion !== next.manifestVersion || previous.layoutVersion !== next.layoutVersion || previous.epoch !== next.epoch || !sameLegacyBaseline(previous.legacyBaseline, next.legacyBaseline) || compareVersions(next.minReaderVersion, previous.minReaderVersion) < 0 || compareVersions(next.minWriterVersion, previous.minWriterVersion) < 0) {
6690
+ throw new Error(
6691
+ "schema manifest identity and legacy baseline are immutable"
6692
+ );
6693
+ }
6694
+ if (previous.manifestVersion === 3 && next.manifestVersion === 3 && (previous.privacyPolicy.revision !== next.privacyPolicy.revision || previous.privacyPolicy.digest !== next.privacyPolicy.digest || previous.workflowPolicyDefaults.planning !== next.workflowPolicyDefaults.planning))
6695
+ throw new Error("MANCODE_PRIVACY_MANIFEST_TRANSITION_REQUIRED");
6696
+ if (previous.activationState === next.activationState) return;
6697
+ if (!allowedManifestTransitions(previous.activationState).has(
6698
+ next.activationState
6699
+ )) {
6700
+ throw new Error(
6701
+ `invalid schema manifest transition: ${previous.activationState} -> ${next.activationState}`
6702
+ );
6703
+ }
6704
+ }
6705
+ function assertSchemaManifestPrivacyTransition(previous, next) {
6706
+ if (next.manifestVersion !== 3 || previous.epoch !== next.epoch || previous.layoutVersion !== next.layoutVersion || previous.activationState !== "v3_active" || next.activationState !== "v3_active" || previous.activatedAt !== next.activatedAt || !sameLegacyBaseline(previous.legacyBaseline, next.legacyBaseline) || !managedAdapterInventoriesMatch(
6707
+ previous.managedAdapters,
6708
+ next.managedAdapters
6709
+ ) || compareVersions(next.minReaderVersion, previous.minReaderVersion) < 0 || compareVersions(next.minWriterVersion, previous.minWriterVersion) < 0 || next.lastOperationId === null || next.lastOperationId === previous.lastOperationId || next.privacyPolicy.revision !== (previous.manifestVersion === 3 ? previous.privacyPolicy.revision : 0) + 1 || next.workflowPolicyDefaults.planning !== (previous.manifestVersion === 1 ? 1 : previous.workflowPolicyDefaults.planning))
6710
+ throw new Error("MANCODE_PRIVACY_MANIFEST_TRANSITION_INVALID");
6711
+ }
6712
+ function parsePrivacyManifestPlanningDefaults(value) {
6713
+ assertRecord(value, "workflow policy defaults");
6714
+ assertKnownKeys(value, ["planning"], "workflow policy defaults");
6715
+ if (value.planning !== 1 && value.planning !== 2)
6716
+ throw new Error("schema manifest planning policy must be 1 or 2");
6717
+ return { planning: value.planning };
6718
+ }
6719
+ function parsePrivacyPolicyReference(value) {
6720
+ assertRecord(value, "privacy policy reference");
6721
+ assertKnownKeys(value, ["revision", "digest"], "privacy policy reference");
6722
+ if (!Number.isSafeInteger(value.revision) || value.revision < 1 || typeof value.digest !== "string" || !DIGEST_PATTERN13.test(value.digest))
6723
+ throw new Error("MANCODE_PRIVACY_POLICY_REFERENCE_INVALID");
6724
+ return { revision: value.revision, digest: value.digest };
6725
+ }
6726
+ function assertSchemaManifestPolicyUpgrade(previous, next) {
6727
+ const legacyUpgrade = previous.manifestVersion === 1 && next.manifestVersion === 2;
6728
+ const privacyUpgrade = previous.manifestVersion === 3 && next.manifestVersion === 3 && previous.workflowPolicyDefaults.planning === 1 && previous.privacyPolicy.revision === next.privacyPolicy.revision && previous.privacyPolicy.digest === next.privacyPolicy.digest;
6729
+ if (!legacyUpgrade && !privacyUpgrade || previous.layoutVersion !== next.layoutVersion || previous.epoch !== next.epoch || previous.activationState !== "v3_active" || next.activationState !== "v3_active" || previous.activatedAt !== next.activatedAt || !sameLegacyBaseline(previous.legacyBaseline, next.legacyBaseline) || !managedAdapterInventoriesMatch(
6730
+ previous.managedAdapters,
6731
+ next.managedAdapters
6732
+ ) || compareVersions(next.minReaderVersion, previous.minReaderVersion) < 0 || compareVersions(next.minWriterVersion, previous.minWriterVersion) < 0 || next.workflowPolicyDefaults.planning !== 2 || next.lastOperationId === null || next.lastOperationId === previous.lastOperationId) {
6733
+ throw new Error("invalid schema manifest Policy 2 upgrade");
6734
+ }
6735
+ }
6736
+ function assertActivationRollbackManifestTransition(previous, next) {
6737
+ if (previous.manifestVersion !== next.manifestVersion || previous.layoutVersion !== next.layoutVersion || previous.epoch !== next.epoch || !sameLegacyBaseline(previous.legacyBaseline, next.legacyBaseline) || previous.activationState !== "v3_active" || next.activationState !== "dual_read" || previous.activatedAt === null || next.activatedAt !== null) {
6738
+ throw new Error("invalid activation rollback manifest transition");
6739
+ }
6740
+ }
6741
+ function parseWorkflowPolicyDefaults(value) {
6742
+ assertRecord(value, "schema manifest workflowPolicyDefaults");
6743
+ assertKnownKeys(
6744
+ value,
6745
+ ["planning"],
6746
+ "schema manifest workflowPolicyDefaults"
6747
+ );
6748
+ if (value.planning !== 2) {
6749
+ throw new Error(
6750
+ `MANCODE_POLICY_VERSION_UNSUPPORTED: component=planning observed=${String(value.planning)} supported=2 requiredWriter=0.4.0`
6751
+ );
6752
+ }
6753
+ return { planning: 2 };
6754
+ }
6755
+ function parseVersion(value, label) {
6756
+ if (typeof value !== "string" || !VERSION_PATTERN.test(value)) {
6757
+ throw new Error(`schema manifest ${label} must be a semantic version`);
6758
+ }
6759
+ return value;
6760
+ }
6761
+ function parseTimestampOrNull3(value, label) {
6762
+ if (value === null) return null;
6763
+ if (typeof value !== "string" || Number.isNaN(Date.parse(value))) {
6764
+ throw new Error(
6765
+ `schema manifest ${label} must be an ISO timestamp or null`
6766
+ );
6767
+ }
6768
+ return value;
6769
+ }
6770
+ function parseLegacyBaseline(value) {
6771
+ if (value === null) return null;
6772
+ assertRecord(value, "schema manifest legacyBaseline");
6773
+ assertKnownKeys(
6774
+ value,
6775
+ ["stateDigest", "workflowIndexDigest"],
6776
+ "schema manifest legacyBaseline"
6777
+ );
6778
+ if (typeof value.stateDigest !== "string" || !DIGEST_PATTERN13.test(value.stateDigest) || typeof value.workflowIndexDigest !== "string" || !DIGEST_PATTERN13.test(value.workflowIndexDigest)) {
6779
+ throw new Error(
6780
+ "schema manifest legacyBaseline must contain sha256 digests"
6781
+ );
6782
+ }
6783
+ return {
6784
+ stateDigest: value.stateDigest,
6785
+ workflowIndexDigest: value.workflowIndexDigest
6786
+ };
6787
+ }
6788
+ function parseManagedAdapters(value) {
6789
+ assertRecord(value, "schema manifest managedAdapters");
6790
+ assertKnownKeys(value, MANAGED_ADAPTERS, "schema manifest managedAdapters");
6791
+ const adapters = {};
6792
+ for (const adapter of MANAGED_ADAPTERS) {
6793
+ const version = value[adapter];
6794
+ if (version === void 0) continue;
6795
+ if (typeof version !== "string" || !version.trim()) {
6796
+ throw new Error(
6797
+ `schema manifest managedAdapters.${adapter} must be a non-empty version`
6798
+ );
6799
+ }
6800
+ adapters[adapter] = version;
6801
+ }
6802
+ return adapters;
6803
+ }
5793
6804
  function parseUlidOrNull8(value, label) {
5794
6805
  if (value === null) return null;
5795
- assertUlid(value, label);
6806
+ assertUlid(value, `schema manifest ${label}`);
6807
+ return value;
6808
+ }
6809
+ function assertManifestStateShape(manifest) {
6810
+ if (manifest.activationState === "initializing" && manifest.legacyBaseline !== null) {
6811
+ throw new Error(
6812
+ "greenfield initializing manifests must not have a legacy baseline"
6813
+ );
6814
+ }
6815
+ if ((manifest.activationState === "dual_read" || manifest.activationState === "activating") && manifest.legacyBaseline === null) {
6816
+ throw new Error(
6817
+ `${manifest.activationState} manifests require a legacy baseline`
6818
+ );
6819
+ }
6820
+ if (manifest.activationState === "v3_active" && manifest.activatedAt === null) {
6821
+ throw new Error("v3_active manifests require activatedAt");
6822
+ }
6823
+ if ((manifest.activationState === "initializing" || manifest.activationState === "dual_read" || manifest.activationState === "activating") && manifest.activatedAt !== null) {
6824
+ throw new Error(
6825
+ `${manifest.activationState} manifests must not have activatedAt`
6826
+ );
6827
+ }
6828
+ }
6829
+ function managedAdapterInventoriesMatch(left, right) {
6830
+ const leftKeys = managedAdapterNames(left);
6831
+ const rightKeys = managedAdapterNames(right);
6832
+ return leftKeys.length === rightKeys.length && leftKeys.every(
6833
+ (adapter, index) => adapter === rightKeys[index] && left[adapter] === right[adapter]
6834
+ );
6835
+ }
6836
+ function compareVersions(left, right) {
6837
+ const [leftCore = "", leftPrerelease] = left.split("-", 2);
6838
+ const [rightCore = "", rightPrerelease] = right.split("-", 2);
6839
+ const leftParts = leftCore.split(".").map(Number);
6840
+ const rightParts = rightCore.split(".").map(Number);
6841
+ for (let index = 0; index < 3; index += 1) {
6842
+ const delta = (leftParts[index] ?? 0) - (rightParts[index] ?? 0);
6843
+ if (delta !== 0) return delta;
6844
+ }
6845
+ if (leftPrerelease === rightPrerelease) return 0;
6846
+ if (leftPrerelease === void 0) return 1;
6847
+ if (rightPrerelease === void 0) return -1;
6848
+ return comparePrerelease(leftPrerelease, rightPrerelease);
6849
+ }
6850
+ function comparePrerelease(left, right) {
6851
+ const leftParts = left.split(".");
6852
+ const rightParts = right.split(".");
6853
+ for (let index = 0; index < Math.max(leftParts.length, rightParts.length); index += 1) {
6854
+ const leftPart = leftParts[index];
6855
+ const rightPart = rightParts[index];
6856
+ if (leftPart === void 0) return -1;
6857
+ if (rightPart === void 0) return 1;
6858
+ if (leftPart === rightPart) continue;
6859
+ const leftNumeric = /^\d+$/.test(leftPart);
6860
+ const rightNumeric = /^\d+$/.test(rightPart);
6861
+ if (leftNumeric && rightNumeric)
6862
+ return Number(leftPart) - Number(rightPart);
6863
+ if (leftNumeric) return -1;
6864
+ if (rightNumeric) return 1;
6865
+ return leftPart.localeCompare(rightPart, "en");
6866
+ }
6867
+ return 0;
6868
+ }
6869
+ function sameLegacyBaseline(left, right) {
6870
+ return left === right || left !== null && right !== null && left.stateDigest === right.stateDigest && left.workflowIndexDigest === right.workflowIndexDigest;
6871
+ }
6872
+ function allowedManifestTransitions(from) {
6873
+ switch (from) {
6874
+ case "initializing":
6875
+ return /* @__PURE__ */ new Set(["v3_active", "repair_required"]);
6876
+ case "dual_read":
6877
+ return /* @__PURE__ */ new Set(["activating", "repair_required"]);
6878
+ case "activating":
6879
+ return /* @__PURE__ */ new Set(["v3_active", "repair_required"]);
6880
+ case "repair_required":
6881
+ return /* @__PURE__ */ new Set(["v3_active"]);
6882
+ case "v3_active":
6883
+ return /* @__PURE__ */ new Set(["repair_required"]);
6884
+ }
6885
+ }
6886
+
6887
+ // src/context/privacy-policy.ts
6888
+ var PRIVACY_MIN_VERSION = "0.6.5";
6889
+ var PRIVACY_POLICY_FILE = "shared/context/privacy-policy.json";
6890
+ var PRIVACY_EXCLUSIONS_FILE = "shared/context/privacy-exclusions.json";
6891
+ function parsePrivacyPolicyCandidate(value) {
6892
+ assertRecord(value, "privacy policy candidate");
6893
+ assertKnownKeys(
6894
+ value,
6895
+ ["schemaVersion", "enabled", "rulesetVersion", "enabledRuleIds"],
6896
+ "privacy policy candidate"
6897
+ );
6898
+ if (value.schemaVersion !== 1 || typeof value.enabled !== "boolean")
6899
+ throw new Error("MANCODE_PRIVACY_POLICY_INVALID");
6900
+ if (value.rulesetVersion !== RULESET_VERSION)
6901
+ throw new Error("MANCODE_PRIVACY_RULESET_UNSUPPORTED");
6902
+ if (!Array.isArray(value.enabledRuleIds) || value.enabledRuleIds.some(
6903
+ (id) => typeof id !== "string" || !DEFAULT_RULE_IDS.includes(id)
6904
+ ) || new Set(value.enabledRuleIds).size !== value.enabledRuleIds.length)
6905
+ throw new Error("MANCODE_PRIVACY_RULE_UNSUPPORTED");
6906
+ if (value.enabled && value.enabledRuleIds.length === 0)
6907
+ throw new Error("MANCODE_PRIVACY_EMPTY_RULESET");
6908
+ return {
6909
+ schemaVersion: 1,
6910
+ enabled: value.enabled,
6911
+ rulesetVersion: value.rulesetVersion,
6912
+ enabledRuleIds: [...value.enabledRuleIds].sort()
6913
+ };
6914
+ }
6915
+ function parsePrivacyPolicy(value) {
6916
+ assertRecord(value, "privacy policy");
6917
+ assertKnownKeys(
6918
+ value,
6919
+ [
6920
+ "schemaVersion",
6921
+ "revision",
6922
+ "workspaceId",
6923
+ "enabled",
6924
+ "rulesetVersion",
6925
+ "enabledRuleIds",
6926
+ "targets",
6927
+ "exclusions",
6928
+ "lastOperationId",
6929
+ "updatedAt"
6930
+ ],
6931
+ "privacy policy"
6932
+ );
6933
+ const candidate = parsePrivacyPolicyCandidate({
6934
+ schemaVersion: value.schemaVersion,
6935
+ enabled: value.enabled,
6936
+ rulesetVersion: value.rulesetVersion,
6937
+ enabledRuleIds: value.enabledRuleIds
6938
+ });
6939
+ assertUlid(value.workspaceId, "privacy workspaceId");
6940
+ assertUlid(value.lastOperationId, "privacy lastOperationId");
6941
+ if (!Array.isArray(value.targets) || value.targets.length !== 2 || value.targets[0] !== "shared_write" || value.targets[1] !== "context_output")
6942
+ throw new Error("MANCODE_PRIVACY_POLICY_INVALID");
6943
+ assertRecord(value.exclusions, "privacy exclusions reference");
6944
+ assertKnownKeys(
6945
+ value.exclusions,
6946
+ ["revision", "digest"],
6947
+ "privacy exclusions reference"
6948
+ );
6949
+ return {
6950
+ ...candidate,
6951
+ revision: revision(value.revision),
6952
+ workspaceId: value.workspaceId,
6953
+ targets: ["shared_write", "context_output"],
6954
+ exclusions: {
6955
+ revision: revision(value.exclusions.revision),
6956
+ digest: digest(value.exclusions.digest)
6957
+ },
6958
+ lastOperationId: value.lastOperationId,
6959
+ updatedAt: timestamp(value.updatedAt)
6960
+ };
6961
+ }
6962
+ function parsePrivacyExclusions(value) {
6963
+ assertRecord(value, "privacy exclusions");
6964
+ assertKnownKeys(
6965
+ value,
6966
+ [
6967
+ "schemaVersion",
6968
+ "revision",
6969
+ "workspaceId",
6970
+ "entries",
6971
+ "lastOperationId",
6972
+ "updatedAt"
6973
+ ],
6974
+ "privacy exclusions"
6975
+ );
6976
+ if (value.schemaVersion !== 1 || !Array.isArray(value.entries))
6977
+ throw new Error("MANCODE_PRIVACY_EXCLUSIONS_INVALID");
6978
+ assertUlid(value.workspaceId, "privacy exclusions workspaceId");
6979
+ assertUlid(value.lastOperationId, "privacy exclusions operationId");
6980
+ const entries = value.entries.map((entry) => {
6981
+ assertRecord(entry, "privacy exclusion");
6982
+ assertKnownKeys(
6983
+ entry,
6984
+ ["kind", "relativePath", "entityDigest"],
6985
+ "privacy exclusion"
6986
+ );
6987
+ const ulid = "[0-7][0-9A-HJKMNPQRSTVWXYZ]{25}";
6988
+ const pattern = entry.kind === "confirmed_decision" ? new RegExp(`^shared/memory/decisions/${ulid}\\.json$`) : entry.kind === "checkpoint" ? new RegExp(`^shared/workflows/${ulid}/checkpoints/${ulid}\\.json$`) : null;
6989
+ if (pattern === null || typeof entry.relativePath !== "string" || !pattern.test(entry.relativePath))
6990
+ throw new Error("MANCODE_PRIVACY_EXCLUSION_PATH_INVALID");
6991
+ return {
6992
+ kind: entry.kind,
6993
+ relativePath: entry.relativePath,
6994
+ entityDigest: digest(entry.entityDigest)
6995
+ };
6996
+ });
6997
+ if (new Set(entries.map((entry) => entry.relativePath)).size !== entries.length)
6998
+ throw new Error("MANCODE_PRIVACY_EXCLUSIONS_DUPLICATE");
6999
+ return {
7000
+ schemaVersion: 1,
7001
+ revision: revision(value.revision),
7002
+ workspaceId: value.workspaceId,
7003
+ entries: entries.sort(
7004
+ (a, b) => a.relativePath.localeCompare(b.relativePath, "en")
7005
+ ),
7006
+ lastOperationId: value.lastOperationId,
7007
+ updatedAt: timestamp(value.updatedAt)
7008
+ };
7009
+ }
7010
+ function createInitialPrivacyPolicy(input) {
7011
+ const exclusions = parsePrivacyExclusions({
7012
+ schemaVersion: 1,
7013
+ revision: 1,
7014
+ workspaceId: input.workspaceId,
7015
+ entries: [],
7016
+ lastOperationId: input.operationId,
7017
+ updatedAt: input.now
7018
+ });
7019
+ const policy = parsePrivacyPolicy({
7020
+ schemaVersion: 1,
7021
+ revision: 1,
7022
+ workspaceId: input.workspaceId,
7023
+ enabled: input.enabled ?? true,
7024
+ rulesetVersion: RULESET_VERSION,
7025
+ enabledRuleIds: [...DEFAULT_RULE_IDS],
7026
+ targets: ["shared_write", "context_output"],
7027
+ exclusions: { revision: 1, digest: digestCanonicalJson(exclusions) },
7028
+ lastOperationId: input.operationId,
7029
+ updatedAt: input.now
7030
+ });
7031
+ return { policy, exclusions, digest: digestCanonicalJson(policy) };
7032
+ }
7033
+ function assertPrivacyPolicyTransition(previous, next) {
7034
+ if (next.revision !== (previous?.revision ?? 0) + 1 || previous !== null && (previous.workspaceId !== next.workspaceId || previous.lastOperationId === next.lastOperationId))
7035
+ throw new Error("MANCODE_PRIVACY_POLICY_REVISION_CONFLICT");
7036
+ }
7037
+ function assertPrivacyExclusionsTransition(previous, next) {
7038
+ if (next.revision !== (previous?.revision ?? 0) + 1 || previous !== null && previous.workspaceId !== next.workspaceId)
7039
+ throw new Error("MANCODE_PRIVACY_POLICY_REVISION_CONFLICT");
7040
+ if (previous?.entries.some(
7041
+ (entry) => !next.entries.some(
7042
+ (nextEntry) => nextEntry.relativePath === entry.relativePath && nextEntry.entityDigest === entry.entityDigest
7043
+ )
7044
+ ))
7045
+ throw new Error("MANCODE_PRIVACY_EXCLUSION_REMOVAL_FORBIDDEN");
7046
+ }
7047
+ async function readPrivacyPolicySnapshot(root, manifest) {
7048
+ await assertNoPendingPrivacyPolicyOperation(root);
7049
+ const current = manifest ?? parseSchemaManifest(
7050
+ JSON.parse(await readPrivacyAuthorityFile(root, "schema.json"))
7051
+ );
7052
+ if (current.manifestVersion !== 3) return null;
7053
+ const [policy, exclusions, config] = await Promise.all([
7054
+ readPrivacyAuthorityFile(root, PRIVACY_POLICY_FILE).then(
7055
+ (content) => parsePrivacyPolicy(JSON.parse(content))
7056
+ ),
7057
+ readPrivacyAuthorityFile(root, PRIVACY_EXCLUSIONS_FILE).then(
7058
+ (content) => parsePrivacyExclusions(JSON.parse(content))
7059
+ ),
7060
+ readPrivacyAuthorityFile(root, "shared/config.json").then(
7061
+ (content) => parseProjectConfig(JSON.parse(content))
7062
+ )
7063
+ ]);
7064
+ const policyDigest = digestCanonicalJson(policy);
7065
+ if (policy.workspaceId !== config.workspaceId)
7066
+ throw new Error("MANCODE_PRIVACY_POLICY_WORKSPACE_MISMATCH");
7067
+ if (current.privacyPolicy.revision !== policy.revision || current.privacyPolicy.digest !== policyDigest || policy.workspaceId !== exclusions.workspaceId || policy.exclusions.revision !== exclusions.revision || policy.exclusions.digest !== digestCanonicalJson(exclusions))
7068
+ throw new Error("MANCODE_PRIVACY_POLICY_DIGEST_MISMATCH");
7069
+ return { policy, exclusions, digest: policyDigest };
7070
+ }
7071
+ async function assertNoPendingPrivacyPolicyOperation(root) {
7072
+ const relative = "local/runtime/operations";
7073
+ let entries;
7074
+ try {
7075
+ const directory = path6.join(root, ".mancode", relative);
7076
+ const stat2 = await lstat2(directory);
7077
+ if (!stat2.isDirectory() || stat2.isSymbolicLink())
7078
+ throw new Error("MANCODE_ARTIFACT_PATH_UNSAFE");
7079
+ entries = await readdir3(directory);
7080
+ } catch (error) {
7081
+ if (error !== null && typeof error === "object" && "code" in error && error.code === "ENOENT")
7082
+ return;
7083
+ throw error;
7084
+ }
7085
+ for (const entry of entries) {
7086
+ if (!/^[0-7][0-9A-HJKMNPQRSTVWXYZ]{25}\.json$/.test(entry)) continue;
7087
+ const journal = JSON.parse(
7088
+ await readPrivacyAuthorityFile(root, `${relative}/${entry}`)
7089
+ );
7090
+ if (journal.type === "privacy_policy_update" && journal.state !== "committed" && journal.state !== "aborted")
7091
+ throw new Error("MANCODE_PRIVACY_POLICY_REPAIR_REQUIRED");
7092
+ }
7093
+ }
7094
+ async function readPrivacyPolicyStatus(root) {
7095
+ try {
7096
+ const snapshot = await readPrivacyPolicySnapshot(root);
7097
+ return {
7098
+ schemaVersion: 1,
7099
+ state: snapshot === null ? "unconfigured" : snapshot.policy.enabled ? "enabled" : "disabled",
7100
+ enabled: snapshot?.policy.enabled ?? false,
7101
+ revision: snapshot?.policy.revision ?? 0,
7102
+ digest: snapshot?.digest ?? null,
7103
+ rulesetVersion: snapshot?.policy.rulesetVersion ?? RULESET_VERSION,
7104
+ excludedEntities: snapshot?.exclusions.entries.length ?? 0,
7105
+ scope: "project",
7106
+ error: null
7107
+ };
7108
+ } catch {
7109
+ return {
7110
+ schemaVersion: 1,
7111
+ state: "error",
7112
+ enabled: null,
7113
+ revision: null,
7114
+ digest: null,
7115
+ rulesetVersion: null,
7116
+ excludedEntities: null,
7117
+ scope: "project",
7118
+ error: "MANCODE_PRIVACY_POLICY_UNAVAILABLE"
7119
+ };
7120
+ }
7121
+ }
7122
+ async function readPrivacyAuthorityFile(root, relative) {
7123
+ let current = path6.join(path6.resolve(root), ".mancode");
7124
+ for (const part of relative.split("/")) {
7125
+ const parent = await lstat2(current);
7126
+ if (!parent.isDirectory() || parent.isSymbolicLink())
7127
+ throw new Error("MANCODE_ARTIFACT_PATH_UNSAFE");
7128
+ current = path6.join(current, part);
7129
+ }
7130
+ const stat2 = await lstat2(current);
7131
+ if (!stat2.isFile() || stat2.isSymbolicLink() || stat2.size > 8 * 1024 * 1024)
7132
+ throw new Error("MANCODE_ARTIFACT_PATH_UNSAFE");
7133
+ return readFile5(current, "utf8");
7134
+ }
7135
+ function revision(value) {
7136
+ if (!Number.isSafeInteger(value) || value < 1)
7137
+ throw new Error("MANCODE_PRIVACY_POLICY_INVALID");
7138
+ return value;
7139
+ }
7140
+ function digest(value) {
7141
+ if (typeof value !== "string" || !/^sha256:[a-f0-9]{64}$/.test(value))
7142
+ throw new Error("MANCODE_PRIVACY_POLICY_INVALID");
7143
+ return value;
7144
+ }
7145
+ function timestamp(value) {
7146
+ if (typeof value !== "string" || !Number.isFinite(Date.parse(value)))
7147
+ throw new Error("MANCODE_PRIVACY_POLICY_INVALID");
5796
7148
  return value;
5797
7149
  }
5798
- function parseNonEmptyStringOrNull5(value, label) {
5799
- if (value === null) return null;
5800
- if (typeof value !== "string" || !value.trim()) {
5801
- throw new Error(`${label} must be a non-empty string or null`);
7150
+
7151
+ // src/context/privacy-guard.ts
7152
+ function containsEnhancedSensitiveText(value, ruleIds) {
7153
+ const stack = [[value]];
7154
+ let visited = 0;
7155
+ while (stack.length > 0) {
7156
+ if (++visited > 1e5) throw new Error("MANCODE_PRIVACY_SCAN_INCOMPLETE");
7157
+ const [item, key] = stack.pop();
7158
+ if (typeof item === "string") {
7159
+ const scan = scanSensitiveText(
7160
+ key === void 0 || item.length === 0 ? item : `${key}: ${item}`,
7161
+ ruleIds
7162
+ );
7163
+ if (scan.status !== "complete")
7164
+ throw new Error("MANCODE_PRIVACY_SCAN_INCOMPLETE");
7165
+ if (scan.findings.length > 0) return true;
7166
+ } else if (Array.isArray(item)) {
7167
+ stack.push(...item.map((content) => [content, key]));
7168
+ } else if (item !== null && typeof item === "object") {
7169
+ for (const [key2, content] of Object.entries(item))
7170
+ stack.push([key2], [content, key2]);
7171
+ }
7172
+ }
7173
+ return false;
7174
+ }
7175
+ function isPrivacyExcluded(snapshot, value) {
7176
+ if (snapshot === null || snapshot === void 0 || snapshot.exclusions.entries.length === 0)
7177
+ return false;
7178
+ const entityDigest = digestCanonicalJson(value);
7179
+ return snapshot.exclusions.entries.some(
7180
+ (entry) => entry.entityDigest === entityDigest
7181
+ );
7182
+ }
7183
+ function assertPrivacyValueAllowed(snapshot, value) {
7184
+ if (isPrivacyExcluded(snapshot, value))
7185
+ throw new Error("MANCODE_PRIVACY_ENTITY_EXCLUDED");
7186
+ if (snapshot?.policy.enabled && containsEnhancedSensitiveText(value, snapshot.policy.enabledRuleIds))
7187
+ throw new Error("MANCODE_PRIVACY_BLOCKED");
7188
+ }
7189
+ async function assertSharedPrivacyValue(root, value) {
7190
+ assertPrivacyValueAllowed(await readSharedPrivacySnapshot(root), value);
7191
+ }
7192
+ async function readSharedPrivacySnapshot(root) {
7193
+ if (!await hasProjectAuthority(root)) return null;
7194
+ return readPrivacyPolicySnapshot(root);
7195
+ }
7196
+ async function acquireSharedPrivacyWriteBarrier(root, operationId) {
7197
+ if (!await hasProjectAuthority(root)) return null;
7198
+ const runtime = await readProjectRuntimeContext(root);
7199
+ const deadline = performance.now() + 5e3;
7200
+ for (; ; ) {
7201
+ try {
7202
+ return await acquireProjectWriteBarrier(runtime, operationId, /* @__PURE__ */ new Date());
7203
+ } catch (error) {
7204
+ if (!(error instanceof Error) || error.message !== "MANCODE_LOCK_HELD" || performance.now() >= deadline)
7205
+ throw error;
7206
+ await new Promise((resolve) => setTimeout(resolve, 25));
7207
+ }
5802
7208
  }
5803
- return value;
5804
7209
  }
5805
- function parseTimestamp12(value, label) {
5806
- if (typeof value !== "string" || Number.isNaN(Date.parse(value))) {
5807
- throw new Error(`${label} must be an ISO timestamp`);
7210
+ async function withSharedPrivacyWrite(root, operationId, value, write2) {
7211
+ await assertSharedPrivacyValue(root, value);
7212
+ const barrier = await acquireSharedPrivacyWriteBarrier(root, operationId);
7213
+ try {
7214
+ await assertSharedPrivacyValue(root, value);
7215
+ return await write2();
7216
+ } finally {
7217
+ await barrier?.release();
7218
+ }
7219
+ }
7220
+ async function assertSharedTaskWriteAtRoot(taskRoot, fileName, content) {
7221
+ const workflows = path7.dirname(taskRoot);
7222
+ const shared = path7.dirname(workflows);
7223
+ const authority = path7.dirname(shared);
7224
+ if (path7.basename(workflows) !== "workflows" || path7.basename(shared) !== "shared" || path7.basename(authority) !== ".mancode")
7225
+ return;
7226
+ await assertSharedPrivacyValue(
7227
+ path7.dirname(authority),
7228
+ privacyContentValue(fileName, content)
7229
+ );
7230
+ }
7231
+ function assertPrivacyRecoveryActionsAllowed(snapshot, actions) {
7232
+ for (const action of actions) {
7233
+ switch (action.kind) {
7234
+ case "task_authority_file":
7235
+ if (action.taskRef.namespace === "shared")
7236
+ assertPrivacyValueAllowed(
7237
+ snapshot,
7238
+ privacyContentValue(action.fileName, action.targetContent)
7239
+ );
7240
+ break;
7241
+ case "workflow_task_directory":
7242
+ case "migration_task_directory":
7243
+ if (action.taskRef.namespace === "shared") {
7244
+ for (const file of action.files)
7245
+ assertPrivacyValueAllowed(
7246
+ snapshot,
7247
+ privacyContentValue(file.fileName, file.content)
7248
+ );
7249
+ if (action.kind === "migration_task_directory")
7250
+ for (const report of action.reports)
7251
+ assertPrivacyValueAllowed(snapshot, report.content);
7252
+ }
7253
+ break;
7254
+ case "task_archive":
7255
+ if (action.taskRef.namespace === "shared") {
7256
+ assertPrivacyValueAllowed(
7257
+ snapshot,
7258
+ privacyContentValue(
7259
+ "requirements.json",
7260
+ action.requirementsContent
7261
+ )
7262
+ );
7263
+ if (action.planContent !== null)
7264
+ assertPrivacyValueAllowed(snapshot, action.planContent);
7265
+ }
7266
+ break;
7267
+ case "checkpoint":
7268
+ if (action.checkpoint.taskRef.namespace === "shared")
7269
+ assertPrivacyValueAllowed(snapshot, action.checkpoint);
7270
+ break;
7271
+ case "claim":
7272
+ if (action.claim.taskRef.namespace === "shared")
7273
+ assertPrivacyValueAllowed(snapshot, action.claim);
7274
+ break;
7275
+ case "handoff":
7276
+ if (action.handoff.taskRef.namespace === "shared")
7277
+ assertPrivacyValueAllowed(snapshot, action.handoff);
7278
+ break;
7279
+ default:
7280
+ break;
7281
+ }
5808
7282
  }
5809
- return value;
7283
+ }
7284
+ async function hasProjectAuthority(root) {
7285
+ try {
7286
+ const stat2 = await lstat3(path7.join(root, ".mancode", "schema.json"));
7287
+ if (!stat2.isFile() || stat2.isSymbolicLink())
7288
+ throw new Error("MANCODE_ARTIFACT_PATH_UNSAFE");
7289
+ return true;
7290
+ } catch (error) {
7291
+ if (error === null || typeof error !== "object" || !("code" in error) || error.code !== "ENOENT")
7292
+ throw error;
7293
+ try {
7294
+ await lstat3(path7.join(root, ".mancode", "shared", "config.json"));
7295
+ } catch (configError) {
7296
+ if (configError !== null && typeof configError === "object" && "code" in configError && configError.code === "ENOENT")
7297
+ return false;
7298
+ throw configError;
7299
+ }
7300
+ throw new Error("MANCODE_PRIVACY_POLICY_UNAVAILABLE");
7301
+ }
7302
+ }
7303
+ function privacyContentValue(fileName, content) {
7304
+ if (!fileName.endsWith(".json")) return content;
7305
+ try {
7306
+ return JSON.parse(content);
7307
+ } catch {
7308
+ throw new Error("MANCODE_PRIVACY_SHARED_JSON_INVALID");
7309
+ }
7310
+ }
7311
+ async function scanPrivacyActivation(root, ruleIds) {
7312
+ const files = await collectSharedFiles(root, "shared");
7313
+ const digests = [];
7314
+ const blockers = [];
7315
+ const exclusions = [];
7316
+ let scannedFiles = 0;
7317
+ for (const relative of files) {
7318
+ if (relative === PRIVACY_POLICY_FILE || relative === PRIVACY_EXCLUSIONS_FILE)
7319
+ continue;
7320
+ const content = await readPrivacyAuthorityFile(root, relative);
7321
+ digests.push({ path: relative, digest: digestCanonicalJson(content) });
7322
+ const fileIndex = scannedFiles++;
7323
+ let value;
7324
+ try {
7325
+ value = privacyContentValue(relative, content);
7326
+ } catch {
7327
+ blockers.push({ fileIndex, reason: "invalid_json" });
7328
+ continue;
7329
+ }
7330
+ if (!containsEnhancedSensitiveText(value, ruleIds)) continue;
7331
+ const ulid = "[0-7][0-9A-HJKMNPQRSTVWXYZ]{25}";
7332
+ const kind = new RegExp(`^shared/memory/decisions/${ulid}\\.json$`).test(
7333
+ relative
7334
+ ) ? "confirmed_decision" : new RegExp(
7335
+ `^shared/workflows/${ulid}/checkpoints/${ulid}\\.json$`
7336
+ ).test(relative) ? "checkpoint" : null;
7337
+ if (kind !== null)
7338
+ exclusions.push({
7339
+ kind,
7340
+ relativePath: relative,
7341
+ entityDigest: digestCanonicalJson(value)
7342
+ });
7343
+ else blockers.push({ fileIndex, reason: "sensitive_content" });
7344
+ }
7345
+ return {
7346
+ fingerprint: digestCanonicalJson(digests),
7347
+ scannedFiles,
7348
+ blockers,
7349
+ exclusions
7350
+ };
7351
+ }
7352
+ async function collectSharedFiles(root, relative) {
7353
+ const target = path7.join(root, ".mancode", relative);
7354
+ const stat2 = await lstat3(target);
7355
+ if (!stat2.isDirectory() || stat2.isSymbolicLink())
7356
+ throw new Error("MANCODE_ARTIFACT_PATH_UNSAFE");
7357
+ const files = [];
7358
+ for (const entry of (await readdir4(target, { withFileTypes: true })).sort(
7359
+ (a, b) => a.name.localeCompare(b.name, "en")
7360
+ )) {
7361
+ if (entry.isSymbolicLink()) throw new Error("MANCODE_ARTIFACT_PATH_UNSAFE");
7362
+ const child = `${relative}/${entry.name}`;
7363
+ if (entry.isDirectory())
7364
+ files.push(...await collectSharedFiles(root, child));
7365
+ else if (entry.isFile()) files.push(child);
7366
+ else throw new Error("MANCODE_ARTIFACT_PATH_UNSAFE");
7367
+ if (files.length > 1e5)
7368
+ throw new Error("MANCODE_PRIVACY_SCAN_INCOMPLETE");
7369
+ }
7370
+ return files;
5810
7371
  }
5811
7372
 
5812
7373
  // src/context/confirmed-decision.ts
5813
- import { lstat as lstat2, mkdir as mkdir2, readFile as readFile3, readdir as readdir2, writeFile as writeFile2 } from "fs/promises";
5814
- import path5 from "path";
5815
7374
  var DECISION_FILENAME = /^[0-7][0-9A-HJKMNPQRSTVWXYZ]{25}\.json$/;
5816
7375
  function createConfirmedDecision(input) {
5817
7376
  assertUlid(input.decisionId, "confirmed decision decisionId");
@@ -5891,8 +7450,8 @@ function confirmedDecisionDigest(decision) {
5891
7450
  return digestCanonicalJson(parseConfirmedDecision(decision));
5892
7451
  }
5893
7452
  function confirmedDecisionDirectory(projectRoot) {
5894
- return path5.join(
5895
- path5.resolve(projectRoot),
7453
+ return path8.join(
7454
+ path8.resolve(projectRoot),
5896
7455
  ".mancode",
5897
7456
  "shared",
5898
7457
  "memory",
@@ -5901,430 +7460,162 @@ function confirmedDecisionDirectory(projectRoot) {
5901
7460
  }
5902
7461
  function confirmedDecisionPath(projectRoot, decisionId) {
5903
7462
  assertUlid(decisionId, "confirmed decision decisionId");
5904
- return path5.join(
7463
+ return path8.join(
5905
7464
  confirmedDecisionDirectory(projectRoot),
5906
7465
  `${decisionId}.json`
5907
7466
  );
5908
7467
  }
5909
7468
  async function publishConfirmedDecision(projectRoot, decision) {
7469
+ const parsed = parseConfirmedDecision(decision);
7470
+ return withSharedPrivacyWrite(
7471
+ projectRoot,
7472
+ parsed.operationId,
7473
+ parsed,
7474
+ () => publishConfirmedDecisionUnlocked(projectRoot, parsed)
7475
+ );
7476
+ }
7477
+ async function publishConfirmedDecisionUnlocked(projectRoot, decision) {
5910
7478
  const parsed = parseConfirmedDecision(decision);
5911
7479
  const directory = confirmedDecisionDirectory(projectRoot);
5912
7480
  await ensureSafeDirectory(projectRoot, directory);
5913
7481
  const target = confirmedDecisionPath(projectRoot, parsed.decisionId);
5914
7482
  try {
5915
- await writeFile2(target, `${JSON.stringify(parsed, null, 2)}
7483
+ await writeFile4(target, `${JSON.stringify(parsed, null, 2)}
5916
7484
  `, {
5917
7485
  encoding: "utf8",
5918
7486
  flag: "wx"
5919
7487
  });
5920
7488
  return parsed;
5921
7489
  } catch (error) {
5922
- if (!isAlreadyExists2(error)) throw error;
7490
+ if (!isAlreadyExists4(error)) throw error;
5923
7491
  const existing = await readConfirmedDecision(
5924
- projectRoot,
5925
- parsed.decisionId
5926
- );
5927
- if (existing !== null && confirmedDecisionDigest(existing) === confirmedDecisionDigest(parsed)) {
5928
- return existing;
5929
- }
5930
- throw new Error("MANCODE_CONFIRMED_DECISION_ID_CONFLICT");
5931
- }
5932
- }
5933
- async function readConfirmedDecision(projectRoot, decisionId) {
5934
- assertUlid(decisionId, "confirmed decision decisionId");
5935
- try {
5936
- return parseConfirmedDecision(
5937
- JSON.parse(
5938
- await readSafeText(
5939
- confirmedDecisionDirectory(projectRoot),
5940
- `${decisionId}.json`
5941
- )
5942
- )
5943
- );
5944
- } catch (error) {
5945
- if (isNotFound3(error)) return null;
5946
- if (error instanceof SyntaxError) {
5947
- throw new Error("MANCODE_CONFIRMED_DECISION_CORRUPT");
5948
- }
5949
- throw error;
5950
- }
5951
- }
5952
- async function listConfirmedDecisions(projectRoot) {
5953
- const directory = confirmedDecisionDirectory(projectRoot);
5954
- let entries;
5955
- try {
5956
- await assertSafeDirectory(directory);
5957
- entries = await readdir2(directory);
5958
- } catch (error) {
5959
- if (isNotFound3(error)) return [];
5960
- throw error;
5961
- }
5962
- const decisions = [];
5963
- for (const entry of entries.sort(compareUtf82)) {
5964
- if (!entry.endsWith(".json")) continue;
5965
- if (!DECISION_FILENAME.test(entry)) {
5966
- throw new Error("MANCODE_CONTEXT_COLLECTION_ENTRY_INVALID");
5967
- }
5968
- const decision = await readConfirmedDecision(
5969
- projectRoot,
5970
- entry.slice(0, -".json".length)
5971
- );
5972
- if (decision === null) {
5973
- throw new Error("MANCODE_CONTEXT_COLLECTION_CHANGED_DURING_READ");
5974
- }
5975
- decisions.push(decision);
5976
- }
5977
- return decisions.sort(
5978
- (left, right) => compareUtf82(left.decisionId, right.decisionId)
5979
- );
5980
- }
5981
- function parseDecisionText(value, label, maxLength) {
5982
- if (typeof value !== "string" || !value.trim() || value.includes("\0") || value.length > maxLength) {
5983
- throw new Error(`${label} is invalid`);
5984
- }
5985
- assertSharedTextSafe(value, label);
5986
- return value;
5987
- }
5988
- function parseTimestamp13(value, label) {
5989
- if (typeof value !== "string" || Number.isNaN(Date.parse(value))) {
5990
- throw new Error(`${label} must be an ISO timestamp`);
5991
- }
5992
- return value;
5993
- }
5994
- async function ensureSafeDirectory(projectRoot, directory) {
5995
- const root = path5.resolve(projectRoot);
5996
- const target = path5.resolve(directory);
5997
- const relative = path5.relative(root, target);
5998
- if (!relative || path5.isAbsolute(relative) || relative.split(path5.sep).some((segment) => segment === "..")) {
5999
- throw new Error("MANCODE_CONTEXT_PATH_UNSAFE");
6000
- }
6001
- await assertSafeDirectory(root);
6002
- let current = root;
6003
- for (const segment of relative.split(path5.sep)) {
6004
- current = path5.join(current, segment);
6005
- try {
6006
- await mkdir2(current);
6007
- } catch (error) {
6008
- if (!isAlreadyExists2(error)) throw error;
6009
- }
6010
- await assertSafeDirectory(current);
6011
- }
6012
- }
6013
- async function assertSafeDirectory(directory) {
6014
- const stat2 = await lstat2(directory);
6015
- if (!stat2.isDirectory() || stat2.isSymbolicLink()) {
6016
- throw new Error("MANCODE_CONTEXT_PATH_UNSAFE");
6017
- }
6018
- }
6019
- async function readSafeText(directory, filename) {
6020
- await assertSafeDirectory(directory);
6021
- const target = path5.join(directory, filename);
6022
- const before = await lstat2(target);
6023
- if (!before.isFile() || before.isSymbolicLink()) {
6024
- throw new Error("MANCODE_CONTEXT_PATH_UNSAFE");
6025
- }
6026
- const content = await readFile3(target, "utf8");
6027
- await assertSafeDirectory(directory);
6028
- const after = await lstat2(target);
6029
- if (!after.isFile() || after.isSymbolicLink() || before.dev !== after.dev || before.ino !== after.ino) {
6030
- throw new Error("MANCODE_CONTEXT_PATH_UNSAFE");
6031
- }
6032
- return content;
6033
- }
6034
- function compareUtf82(left, right) {
6035
- return Buffer.from(left, "utf8").compare(Buffer.from(right, "utf8"));
6036
- }
6037
- function isAlreadyExists2(error) {
6038
- return typeof error === "object" && error !== null && "code" in error && error.code === "EEXIST";
6039
- }
6040
- function isNotFound3(error) {
6041
- return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
6042
- }
6043
-
6044
- // src/context/manifest.ts
6045
- var ACTIVATION_STATES = /* @__PURE__ */ new Set([
6046
- "initializing",
6047
- "dual_read",
6048
- "activating",
6049
- "v3_active",
6050
- "repair_required"
6051
- ]);
6052
- var MANAGED_ADAPTERS = [
6053
- "claude-code",
6054
- "codex",
6055
- "cursor",
6056
- "copilot",
6057
- "zcode",
6058
- "kimi-code",
6059
- "qoder",
6060
- "dsh"
6061
- ];
6062
- var VERSION_PATTERN = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/;
6063
- var DIGEST_PATTERN13 = /^sha256:[a-f0-9]{64}$/;
6064
- function parseSchemaManifest(value) {
6065
- assertRecord(value, "schema manifest");
6066
- if (value.manifestVersion !== 1 && value.manifestVersion !== 2) {
6067
- throw new Error(
6068
- `MANCODE_MANIFEST_VERSION_UNSUPPORTED: observed=${String(value.manifestVersion)} supported=1,2 requiredWriter=0.4.0`
6069
- );
6070
- }
6071
- const manifestVersion = value.manifestVersion;
6072
- assertKnownKeys(
6073
- value,
6074
- [
6075
- "manifestVersion",
6076
- "layoutVersion",
6077
- "epoch",
6078
- "activationState",
6079
- "minReaderVersion",
6080
- "minWriterVersion",
6081
- "activatedAt",
6082
- "legacyBaseline",
6083
- "managedAdapters",
6084
- "lastOperationId",
6085
- ...manifestVersion === 2 ? ["workflowPolicyDefaults"] : []
6086
- ],
6087
- "schema manifest"
6088
- );
6089
- if (value.layoutVersion !== 3) {
6090
- throw new Error("schema manifest must use layoutVersion 3");
6091
- }
6092
- assertUlid(value.epoch, "schema manifest epoch");
6093
- if (typeof value.activationState !== "string" || !ACTIVATION_STATES.has(value.activationState)) {
6094
- throw new Error("schema manifest activationState is invalid");
6095
- }
6096
- const common = {
6097
- layoutVersion: 3,
6098
- epoch: value.epoch,
6099
- activationState: value.activationState,
6100
- minReaderVersion: parseVersion(value.minReaderVersion, "minReaderVersion"),
6101
- minWriterVersion: parseVersion(value.minWriterVersion, "minWriterVersion"),
6102
- activatedAt: parseTimestampOrNull3(value.activatedAt, "activatedAt"),
6103
- legacyBaseline: parseLegacyBaseline(value.legacyBaseline),
6104
- managedAdapters: parseManagedAdapters(value.managedAdapters),
6105
- lastOperationId: parseUlidOrNull9(value.lastOperationId, "lastOperationId")
6106
- };
6107
- const manifest = manifestVersion === 1 ? { manifestVersion: 1, ...common } : {
6108
- manifestVersion: 2,
6109
- ...common,
6110
- workflowPolicyDefaults: parseWorkflowPolicyDefaults(
6111
- value.workflowPolicyDefaults
6112
- )
6113
- };
6114
- assertManifestStateShape(manifest);
6115
- if (manifest.manifestVersion === 2 && (compareVersions(manifest.minReaderVersion, "0.4.0") < 0 || compareVersions(manifest.minWriterVersion, "0.4.0") < 0)) {
6116
- throw new Error(
6117
- "schema manifest V2 requires minReaderVersion and minWriterVersion 0.4.0 or newer"
6118
- );
6119
- }
6120
- return manifest;
6121
- }
6122
- function serializeSchemaManifest(value) {
6123
- return `${JSON.stringify(parseSchemaManifest(value), null, 2)}
6124
- `;
6125
- }
6126
- function managedAdapterNames(inventory) {
6127
- return MANAGED_ADAPTERS.filter((adapter) => inventory[adapter] !== void 0);
6128
- }
6129
- function parseSchemaManifestV1(value) {
6130
- const manifest = parseSchemaManifest(value);
6131
- if (manifest.manifestVersion !== 1) {
6132
- throw new Error(
6133
- `MANCODE_MANIFEST_VERSION_UNSUPPORTED: observed=${manifest.manifestVersion} supported=1 requiredWriter=0.3.x`
6134
- );
6135
- }
6136
- return manifest;
6137
- }
6138
- function parseSchemaManifestV2(value) {
6139
- const manifest = parseSchemaManifest(value);
6140
- if (manifest.manifestVersion !== 2) {
6141
- throw new Error(
6142
- `MANCODE_MANIFEST_VERSION_UNSUPPORTED: observed=${manifest.manifestVersion} supported=2 requiredWriter=0.4.0`
7492
+ projectRoot,
7493
+ parsed.decisionId
6143
7494
  );
7495
+ if (existing !== null && confirmedDecisionDigest(existing) === confirmedDecisionDigest(parsed)) {
7496
+ return existing;
7497
+ }
7498
+ throw new Error("MANCODE_CONFIRMED_DECISION_ID_CONFLICT");
6144
7499
  }
6145
- return manifest;
6146
7500
  }
6147
- function assertSchemaManifestTransition(previous, next) {
6148
- if (previous.manifestVersion !== next.manifestVersion || previous.layoutVersion !== next.layoutVersion || previous.epoch !== next.epoch || !sameLegacyBaseline(previous.legacyBaseline, next.legacyBaseline) || compareVersions(next.minReaderVersion, previous.minReaderVersion) < 0 || compareVersions(next.minWriterVersion, previous.minWriterVersion) < 0) {
6149
- throw new Error(
6150
- "schema manifest identity and legacy baseline are immutable"
6151
- );
6152
- }
6153
- if (previous.activationState === next.activationState) return;
6154
- if (!allowedManifestTransitions(previous.activationState).has(
6155
- next.activationState
6156
- )) {
6157
- throw new Error(
6158
- `invalid schema manifest transition: ${previous.activationState} -> ${next.activationState}`
7501
+ async function readConfirmedDecision(projectRoot, decisionId) {
7502
+ assertUlid(decisionId, "confirmed decision decisionId");
7503
+ try {
7504
+ return parseConfirmedDecision(
7505
+ JSON.parse(
7506
+ await readSafeText(
7507
+ confirmedDecisionDirectory(projectRoot),
7508
+ `${decisionId}.json`
7509
+ )
7510
+ )
6159
7511
  );
7512
+ } catch (error) {
7513
+ if (isNotFound5(error)) return null;
7514
+ if (error instanceof SyntaxError) {
7515
+ throw new Error("MANCODE_CONFIRMED_DECISION_CORRUPT");
7516
+ }
7517
+ throw error;
6160
7518
  }
6161
7519
  }
6162
- function assertSchemaManifestPolicyUpgrade(previous, next) {
6163
- if (previous.manifestVersion !== 1 || next.manifestVersion !== 2 || previous.layoutVersion !== next.layoutVersion || previous.epoch !== next.epoch || previous.activationState !== "v3_active" || next.activationState !== "v3_active" || previous.activatedAt !== next.activatedAt || !sameLegacyBaseline(previous.legacyBaseline, next.legacyBaseline) || !managedAdapterInventoriesMatch(
6164
- previous.managedAdapters,
6165
- next.managedAdapters
6166
- ) || compareVersions(next.minReaderVersion, previous.minReaderVersion) < 0 || compareVersions(next.minWriterVersion, previous.minWriterVersion) < 0 || next.workflowPolicyDefaults.planning !== 2 || next.lastOperationId === null || next.lastOperationId === previous.lastOperationId) {
6167
- throw new Error("invalid schema manifest Policy 2 upgrade");
6168
- }
6169
- }
6170
- function assertActivationRollbackManifestTransition(previous, next) {
6171
- if (previous.manifestVersion !== next.manifestVersion || previous.layoutVersion !== next.layoutVersion || previous.epoch !== next.epoch || !sameLegacyBaseline(previous.legacyBaseline, next.legacyBaseline) || previous.activationState !== "v3_active" || next.activationState !== "dual_read" || previous.activatedAt === null || next.activatedAt !== null) {
6172
- throw new Error("invalid activation rollback manifest transition");
7520
+ async function listConfirmedDecisions(projectRoot) {
7521
+ const directory = confirmedDecisionDirectory(projectRoot);
7522
+ let entries;
7523
+ try {
7524
+ await assertSafeDirectory(directory);
7525
+ entries = await readdir5(directory);
7526
+ } catch (error) {
7527
+ if (isNotFound5(error)) return [];
7528
+ throw error;
6173
7529
  }
6174
- }
6175
- function parseWorkflowPolicyDefaults(value) {
6176
- assertRecord(value, "schema manifest workflowPolicyDefaults");
6177
- assertKnownKeys(
6178
- value,
6179
- ["planning"],
6180
- "schema manifest workflowPolicyDefaults"
6181
- );
6182
- if (value.planning !== 2) {
6183
- throw new Error(
6184
- `MANCODE_POLICY_VERSION_UNSUPPORTED: component=planning observed=${String(value.planning)} supported=2 requiredWriter=0.4.0`
7530
+ const decisions = [];
7531
+ for (const entry of entries.sort(compareUtf83)) {
7532
+ if (!entry.endsWith(".json")) continue;
7533
+ if (!DECISION_FILENAME.test(entry)) {
7534
+ throw new Error("MANCODE_CONTEXT_COLLECTION_ENTRY_INVALID");
7535
+ }
7536
+ const decision = await readConfirmedDecision(
7537
+ projectRoot,
7538
+ entry.slice(0, -".json".length)
6185
7539
  );
7540
+ if (decision === null) {
7541
+ throw new Error("MANCODE_CONTEXT_COLLECTION_CHANGED_DURING_READ");
7542
+ }
7543
+ decisions.push(decision);
6186
7544
  }
6187
- return { planning: 2 };
7545
+ return decisions.sort(
7546
+ (left, right) => compareUtf83(left.decisionId, right.decisionId)
7547
+ );
6188
7548
  }
6189
- function parseVersion(value, label) {
6190
- if (typeof value !== "string" || !VERSION_PATTERN.test(value)) {
6191
- throw new Error(`schema manifest ${label} must be a semantic version`);
7549
+ function parseDecisionText(value, label, maxLength) {
7550
+ if (typeof value !== "string" || !value.trim() || value.includes("\0") || value.length > maxLength) {
7551
+ throw new Error(`${label} is invalid`);
6192
7552
  }
7553
+ assertSharedTextSafe(value, label);
6193
7554
  return value;
6194
7555
  }
6195
- function parseTimestampOrNull3(value, label) {
6196
- if (value === null) return null;
7556
+ function parseTimestamp13(value, label) {
6197
7557
  if (typeof value !== "string" || Number.isNaN(Date.parse(value))) {
6198
- throw new Error(
6199
- `schema manifest ${label} must be an ISO timestamp or null`
6200
- );
7558
+ throw new Error(`${label} must be an ISO timestamp`);
6201
7559
  }
6202
7560
  return value;
6203
7561
  }
6204
- function parseLegacyBaseline(value) {
6205
- if (value === null) return null;
6206
- assertRecord(value, "schema manifest legacyBaseline");
6207
- assertKnownKeys(
6208
- value,
6209
- ["stateDigest", "workflowIndexDigest"],
6210
- "schema manifest legacyBaseline"
6211
- );
6212
- if (typeof value.stateDigest !== "string" || !DIGEST_PATTERN13.test(value.stateDigest) || typeof value.workflowIndexDigest !== "string" || !DIGEST_PATTERN13.test(value.workflowIndexDigest)) {
6213
- throw new Error(
6214
- "schema manifest legacyBaseline must contain sha256 digests"
6215
- );
7562
+ async function ensureSafeDirectory(projectRoot, directory) {
7563
+ const root = path8.resolve(projectRoot);
7564
+ const target = path8.resolve(directory);
7565
+ const relative = path8.relative(root, target);
7566
+ if (!relative || path8.isAbsolute(relative) || relative.split(path8.sep).some((segment) => segment === "..")) {
7567
+ throw new Error("MANCODE_CONTEXT_PATH_UNSAFE");
6216
7568
  }
6217
- return {
6218
- stateDigest: value.stateDigest,
6219
- workflowIndexDigest: value.workflowIndexDigest
6220
- };
6221
- }
6222
- function parseManagedAdapters(value) {
6223
- assertRecord(value, "schema manifest managedAdapters");
6224
- assertKnownKeys(value, MANAGED_ADAPTERS, "schema manifest managedAdapters");
6225
- const adapters = {};
6226
- for (const adapter of MANAGED_ADAPTERS) {
6227
- const version = value[adapter];
6228
- if (version === void 0) continue;
6229
- if (typeof version !== "string" || !version.trim()) {
6230
- throw new Error(
6231
- `schema manifest managedAdapters.${adapter} must be a non-empty version`
6232
- );
7569
+ await assertSafeDirectory(root);
7570
+ let current = root;
7571
+ for (const segment of relative.split(path8.sep)) {
7572
+ current = path8.join(current, segment);
7573
+ try {
7574
+ await mkdir4(current);
7575
+ } catch (error) {
7576
+ if (!isAlreadyExists4(error)) throw error;
6233
7577
  }
6234
- adapters[adapter] = version;
7578
+ await assertSafeDirectory(current);
6235
7579
  }
6236
- return adapters;
6237
- }
6238
- function parseUlidOrNull9(value, label) {
6239
- if (value === null) return null;
6240
- assertUlid(value, `schema manifest ${label}`);
6241
- return value;
6242
7580
  }
6243
- function assertManifestStateShape(manifest) {
6244
- if (manifest.activationState === "initializing" && manifest.legacyBaseline !== null) {
6245
- throw new Error(
6246
- "greenfield initializing manifests must not have a legacy baseline"
6247
- );
6248
- }
6249
- if ((manifest.activationState === "dual_read" || manifest.activationState === "activating") && manifest.legacyBaseline === null) {
6250
- throw new Error(
6251
- `${manifest.activationState} manifests require a legacy baseline`
6252
- );
6253
- }
6254
- if (manifest.activationState === "v3_active" && manifest.activatedAt === null) {
6255
- throw new Error("v3_active manifests require activatedAt");
6256
- }
6257
- if ((manifest.activationState === "initializing" || manifest.activationState === "dual_read" || manifest.activationState === "activating") && manifest.activatedAt !== null) {
6258
- throw new Error(
6259
- `${manifest.activationState} manifests must not have activatedAt`
6260
- );
7581
+ async function assertSafeDirectory(directory) {
7582
+ const stat2 = await lstat4(directory);
7583
+ if (!stat2.isDirectory() || stat2.isSymbolicLink()) {
7584
+ throw new Error("MANCODE_CONTEXT_PATH_UNSAFE");
6261
7585
  }
6262
7586
  }
6263
- function managedAdapterInventoriesMatch(left, right) {
6264
- const leftKeys = managedAdapterNames(left);
6265
- const rightKeys = managedAdapterNames(right);
6266
- return leftKeys.length === rightKeys.length && leftKeys.every(
6267
- (adapter, index) => adapter === rightKeys[index] && left[adapter] === right[adapter]
6268
- );
6269
- }
6270
- function compareVersions(left, right) {
6271
- const [leftCore = "", leftPrerelease] = left.split("-", 2);
6272
- const [rightCore = "", rightPrerelease] = right.split("-", 2);
6273
- const leftParts = leftCore.split(".").map(Number);
6274
- const rightParts = rightCore.split(".").map(Number);
6275
- for (let index = 0; index < 3; index += 1) {
6276
- const delta = (leftParts[index] ?? 0) - (rightParts[index] ?? 0);
6277
- if (delta !== 0) return delta;
7587
+ async function readSafeText(directory, filename) {
7588
+ await assertSafeDirectory(directory);
7589
+ const target = path8.join(directory, filename);
7590
+ const before = await lstat4(target);
7591
+ if (!before.isFile() || before.isSymbolicLink()) {
7592
+ throw new Error("MANCODE_CONTEXT_PATH_UNSAFE");
6278
7593
  }
6279
- if (leftPrerelease === rightPrerelease) return 0;
6280
- if (leftPrerelease === void 0) return 1;
6281
- if (rightPrerelease === void 0) return -1;
6282
- return comparePrerelease(leftPrerelease, rightPrerelease);
6283
- }
6284
- function comparePrerelease(left, right) {
6285
- const leftParts = left.split(".");
6286
- const rightParts = right.split(".");
6287
- for (let index = 0; index < Math.max(leftParts.length, rightParts.length); index += 1) {
6288
- const leftPart = leftParts[index];
6289
- const rightPart = rightParts[index];
6290
- if (leftPart === void 0) return -1;
6291
- if (rightPart === void 0) return 1;
6292
- if (leftPart === rightPart) continue;
6293
- const leftNumeric = /^\d+$/.test(leftPart);
6294
- const rightNumeric = /^\d+$/.test(rightPart);
6295
- if (leftNumeric && rightNumeric)
6296
- return Number(leftPart) - Number(rightPart);
6297
- if (leftNumeric) return -1;
6298
- if (rightNumeric) return 1;
6299
- return leftPart.localeCompare(rightPart, "en");
7594
+ const content = await readFile6(target, "utf8");
7595
+ await assertSafeDirectory(directory);
7596
+ const after = await lstat4(target);
7597
+ if (!after.isFile() || after.isSymbolicLink() || before.dev !== after.dev || before.ino !== after.ino) {
7598
+ throw new Error("MANCODE_CONTEXT_PATH_UNSAFE");
6300
7599
  }
6301
- return 0;
7600
+ return content;
6302
7601
  }
6303
- function sameLegacyBaseline(left, right) {
6304
- return left === right || left !== null && right !== null && left.stateDigest === right.stateDigest && left.workflowIndexDigest === right.workflowIndexDigest;
7602
+ function compareUtf83(left, right) {
7603
+ return Buffer.from(left, "utf8").compare(Buffer.from(right, "utf8"));
6305
7604
  }
6306
- function allowedManifestTransitions(from) {
6307
- switch (from) {
6308
- case "initializing":
6309
- return /* @__PURE__ */ new Set(["v3_active", "repair_required"]);
6310
- case "dual_read":
6311
- return /* @__PURE__ */ new Set(["activating", "repair_required"]);
6312
- case "activating":
6313
- return /* @__PURE__ */ new Set(["v3_active", "repair_required"]);
6314
- case "repair_required":
6315
- return /* @__PURE__ */ new Set(["v3_active"]);
6316
- case "v3_active":
6317
- return /* @__PURE__ */ new Set(["repair_required"]);
6318
- }
7605
+ function isAlreadyExists4(error) {
7606
+ return typeof error === "object" && error !== null && "code" in error && error.code === "EEXIST";
7607
+ }
7608
+ function isNotFound5(error) {
7609
+ return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
6319
7610
  }
6320
7611
 
6321
7612
  // src/context/project-facts.ts
6322
- import { mkdir as mkdir3, readFile as readFile5, rename, rm, writeFile as writeFile3 } from "fs/promises";
6323
- import path7 from "path";
7613
+ import { mkdir as mkdir5, readFile as readFile8, rename as rename3, rm as rm2, writeFile as writeFile5 } from "fs/promises";
7614
+ import path10 from "path";
6324
7615
 
6325
7616
  // src/system/project-profile.ts
6326
- import { readFile as readFile4, readdir as readdir3, stat } from "fs/promises";
6327
- import path6 from "path";
7617
+ import { readFile as readFile7, readdir as readdir6, stat } from "fs/promises";
7618
+ import path9 from "path";
6328
7619
  var PROJECT_MANIFESTS = [
6329
7620
  "package.json",
6330
7621
  "pyproject.toml",
@@ -6382,29 +7673,29 @@ var PROJECT_SOURCE_ROOTS = [
6382
7673
  var PROJECT_MANIFEST_NAMES = new Set(PROJECT_MANIFESTS);
6383
7674
  var PROJECT_SOURCE_ROOT_NAMES = new Set(PROJECT_SOURCE_ROOTS);
6384
7675
  async function hasProjectEvidence(projectRoot) {
6385
- const entries = await readdir3(projectRoot, { withFileTypes: true }).catch(
7676
+ const entries = await readdir6(projectRoot, { withFileTypes: true }).catch(
6386
7677
  () => []
6387
7678
  );
6388
7679
  return entries.some((entry) => {
6389
7680
  if (entry.isDirectory()) return PROJECT_SOURCE_ROOT_NAMES.has(entry.name);
6390
7681
  if (!entry.isFile()) return false;
6391
- return PROJECT_MANIFEST_NAMES.has(entry.name) || PROJECT_SOURCE_EXTENSIONS.has(path6.extname(entry.name).toLowerCase());
7682
+ return PROJECT_MANIFEST_NAMES.has(entry.name) || PROJECT_SOURCE_EXTENSIONS.has(path9.extname(entry.name).toLowerCase());
6392
7683
  });
6393
7684
  }
6394
7685
  async function detectProjectProfile(projectRoot) {
6395
- const entries = await readdir3(projectRoot).catch(() => []);
7686
+ const entries = await readdir6(projectRoot).catch(() => []);
6396
7687
  const manifests = PROJECT_MANIFESTS.filter((file) => entries.includes(file));
6397
7688
  const sourceRoots = await existingDirs(projectRoot, [
6398
7689
  ...PROJECT_SOURCE_ROOTS,
6399
7690
  "data",
6400
7691
  "notebooks"
6401
7692
  ]);
6402
- const packageJson = await readJson(path6.join(projectRoot, "package.json"));
7693
+ const packageJson = await readJson(path9.join(projectRoot, "package.json"));
6403
7694
  const [pubspec, pythonManifests] = await Promise.all([
6404
- readText(path6.join(projectRoot, "pubspec.yaml")),
7695
+ readText(path9.join(projectRoot, "pubspec.yaml")),
6405
7696
  Promise.all([
6406
- readText(path6.join(projectRoot, "pyproject.toml")),
6407
- readText(path6.join(projectRoot, "requirements.txt"))
7697
+ readText(path9.join(projectRoot, "pyproject.toml")),
7698
+ readText(path9.join(projectRoot, "requirements.txt"))
6408
7699
  ]).then((parts) => parts.filter(Boolean).join("\n"))
6409
7700
  ]);
6410
7701
  const deps = Object.keys({
@@ -6413,7 +7704,7 @@ async function detectProjectProfile(projectRoot) {
6413
7704
  ...packageJson?.peerDependencies
6414
7705
  });
6415
7706
  const flutter = isFlutterProject(pubspec);
6416
- const shadcn = entries.includes("components.json") || deps.some((dep) => dep.startsWith("@radix-ui/")) && await isDirectory(path6.join(projectRoot, "src", "components", "ui"));
7707
+ const shadcn = entries.includes("components.json") || deps.some((dep) => dep.startsWith("@radix-ui/")) && await isDirectory(path9.join(projectRoot, "src", "components", "ui"));
6417
7708
  const languages = inferLanguages(manifests, entries);
6418
7709
  const frameworks = inferFrameworks(
6419
7710
  deps,
@@ -6456,7 +7747,7 @@ async function existingDirs(root, candidates) {
6456
7747
  const found = [];
6457
7748
  for (const candidate of candidates) {
6458
7749
  try {
6459
- if ((await stat(path6.join(root, candidate))).isDirectory())
7750
+ if ((await stat(path9.join(root, candidate))).isDirectory())
6460
7751
  found.push(candidate);
6461
7752
  } catch {
6462
7753
  }
@@ -6472,14 +7763,14 @@ async function isDirectory(candidate) {
6472
7763
  }
6473
7764
  async function readJson(file) {
6474
7765
  try {
6475
- return JSON.parse(await readFile4(file, "utf-8"));
7766
+ return JSON.parse(await readFile7(file, "utf-8"));
6476
7767
  } catch {
6477
7768
  return null;
6478
7769
  }
6479
7770
  }
6480
7771
  async function readText(file) {
6481
7772
  try {
6482
- return await readFile4(file, "utf-8");
7773
+ return await readFile7(file, "utf-8");
6483
7774
  } catch {
6484
7775
  return "";
6485
7776
  }
@@ -6702,8 +7993,8 @@ function parseProjectFacts(value) {
6702
7993
  if (value.schemaVersion !== 1) {
6703
7994
  throw new Error("project facts schemaVersion must be 1");
6704
7995
  }
6705
- const revision = value.revision;
6706
- if (typeof revision !== "number" || !Number.isSafeInteger(revision) || revision < 1) {
7996
+ const revision2 = value.revision;
7997
+ if (typeof revision2 !== "number" || !Number.isSafeInteger(revision2) || revision2 < 1) {
6707
7998
  throw new Error("project facts revision must be a positive integer");
6708
7999
  }
6709
8000
  if (value.trust !== "detected") {
@@ -6712,20 +8003,20 @@ function parseProjectFacts(value) {
6712
8003
  const uiLibrary = parseTextOrNull(value.uiLibrary, "project facts uiLibrary");
6713
8004
  return {
6714
8005
  schemaVersion: 1,
6715
- revision,
8006
+ revision: revision2,
6716
8007
  trust: "detected",
6717
8008
  profile: parseProjectProfile(value.profile),
6718
8009
  uiLibrary,
6719
8010
  detectedAt: parseTimestamp14(value.detectedAt, "project facts detectedAt"),
6720
- lastOperationId: parseUlidOrNull10(
8011
+ lastOperationId: parseUlidOrNull9(
6721
8012
  value.lastOperationId,
6722
8013
  "project facts lastOperationId"
6723
8014
  )
6724
8015
  };
6725
8016
  }
6726
8017
  function projectFactsPath(projectRoot) {
6727
- return path7.join(
6728
- path7.resolve(projectRoot),
8018
+ return path10.join(
8019
+ path10.resolve(projectRoot),
6729
8020
  ".mancode",
6730
8021
  "shared",
6731
8022
  "context",
@@ -6733,22 +8024,31 @@ function projectFactsPath(projectRoot) {
6733
8024
  );
6734
8025
  }
6735
8026
  async function writeProjectFacts(projectRoot, facts) {
8027
+ const parsed = parseProjectFacts(facts);
8028
+ return withSharedPrivacyWrite(
8029
+ projectRoot,
8030
+ parsed.lastOperationId ?? createUlid(),
8031
+ parsed,
8032
+ () => writeProjectFactsUnlocked(projectRoot, parsed)
8033
+ );
8034
+ }
8035
+ async function writeProjectFactsUnlocked(projectRoot, facts) {
6736
8036
  const parsed = parseProjectFacts(facts);
6737
8037
  const target = projectFactsPath(projectRoot);
6738
- await mkdir3(path7.dirname(target), { recursive: true });
6739
- const temporary = path7.join(
6740
- path7.dirname(target),
6741
- `.${path7.basename(target)}.${process.pid}.${Date.now()}.tmp`
8038
+ await mkdir5(path10.dirname(target), { recursive: true });
8039
+ const temporary = path10.join(
8040
+ path10.dirname(target),
8041
+ `.${path10.basename(target)}.${process.pid}.${Date.now()}.tmp`
6742
8042
  );
6743
8043
  try {
6744
- await writeFile3(temporary, `${JSON.stringify(parsed, null, 2)}
8044
+ await writeFile5(temporary, `${JSON.stringify(parsed, null, 2)}
6745
8045
  `, {
6746
8046
  encoding: "utf8",
6747
8047
  flag: "wx"
6748
8048
  });
6749
- await rename(temporary, target);
8049
+ await rename3(temporary, target);
6750
8050
  } finally {
6751
- await rm(temporary, { force: true }).catch(() => void 0);
8051
+ await rm2(temporary, { force: true }).catch(() => void 0);
6752
8052
  }
6753
8053
  return parsed;
6754
8054
  }
@@ -6821,7 +8121,7 @@ function parseTextArray(value, label) {
6821
8121
  }
6822
8122
  function parseSafeRelativePathArray(value, label) {
6823
8123
  return parseTextArray(value, label).map((item) => {
6824
- if (path7.isAbsolute(item) || item.includes("\\") || item.split("/").some((part) => !part || part === "." || part === "..")) {
8124
+ if (path10.isAbsolute(item) || item.includes("\\") || item.split("/").some((part) => !part || part === "." || part === "..")) {
6825
8125
  throw new Error(`${label} must contain safe relative paths`);
6826
8126
  }
6827
8127
  return item;
@@ -6844,17 +8144,17 @@ function parseTimestamp14(value, label) {
6844
8144
  }
6845
8145
  return value;
6846
8146
  }
6847
- function parseUlidOrNull10(value, label) {
8147
+ function parseUlidOrNull9(value, label) {
6848
8148
  if (value === null) return null;
6849
8149
  assertUlid(value, label);
6850
8150
  return value;
6851
8151
  }
6852
8152
 
6853
8153
  // src/context/task-locator.ts
6854
- import { lstat as lstat3, readFile as readFile6, realpath } from "fs/promises";
6855
- import path8 from "path";
8154
+ import { lstat as lstat5, readFile as readFile9, realpath } from "fs/promises";
8155
+ import path11 from "path";
6856
8156
  async function locateTask(projectRoot, requested) {
6857
- const root = path8.resolve(projectRoot);
8157
+ const root = path11.resolve(projectRoot);
6858
8158
  if (typeof requested !== "string") {
6859
8159
  const taskRef = parseTaskRefValue(requested);
6860
8160
  return locateExplicitTask(root, taskRef);
@@ -6881,8 +8181,8 @@ async function locateTask(projectRoot, requested) {
6881
8181
  }
6882
8182
  function taskRootPath(projectRoot, taskRef) {
6883
8183
  const parsed = parseTaskRefValue(taskRef);
6884
- return path8.join(
6885
- path8.resolve(projectRoot),
8184
+ return path11.join(
8185
+ path11.resolve(projectRoot),
6886
8186
  ".mancode",
6887
8187
  parsed.namespace,
6888
8188
  "workflows",
@@ -6898,7 +8198,7 @@ async function locateExplicitTask(projectRoot, taskRef) {
6898
8198
  }
6899
8199
  async function isDirectoryWithoutSymlink(target) {
6900
8200
  try {
6901
- const entry = await lstat3(target);
8201
+ const entry = await lstat5(target);
6902
8202
  return entry.isDirectory() && !entry.isSymbolicLink();
6903
8203
  } catch {
6904
8204
  return false;
@@ -6916,13 +8216,13 @@ var V3ContextStore = class {
6916
8216
  if (typeof projectRoot !== "string" || !projectRoot.trim()) {
6917
8217
  throw new Error("context store projectRoot is required");
6918
8218
  }
6919
- this.projectRoot = path9.resolve(projectRoot);
8219
+ this.projectRoot = path12.resolve(projectRoot);
6920
8220
  }
6921
8221
  async locateTask(requested) {
6922
8222
  const location = await locateTask(this.projectRoot, requested);
6923
8223
  await assertSafeDirectoryWithin(
6924
8224
  this.projectRoot,
6925
- path9.relative(this.projectRoot, location.taskRoot)
8225
+ path12.relative(this.projectRoot, location.taskRoot)
6926
8226
  );
6927
8227
  return location;
6928
8228
  }
@@ -7001,33 +8301,45 @@ var V3ContextStore = class {
7001
8301
  ),
7002
8302
  this.readRequiredJson(
7003
8303
  this.mancodeRoot(),
7004
- path9.join("shared", "config.json"),
8304
+ path12.join("shared", "config.json"),
7005
8305
  parseProjectConfig
7006
8306
  ),
7007
8307
  this.readRequiredJson(
7008
8308
  this.mancodeRoot(),
7009
- path9.join("shared", "team", "policy.json"),
8309
+ path12.join("shared", "team", "policy.json"),
7010
8310
  parseTeamPolicy
7011
8311
  ),
7012
8312
  readOptionalJsonWithin(
7013
8313
  this.mancodeRoot(),
7014
- path9.join("shared", "context", "project.json"),
8314
+ path12.join("shared", "context", "project.json"),
7015
8315
  parseProjectFacts
7016
8316
  ),
7017
8317
  listConfirmedDecisions(this.projectRoot)
7018
8318
  ]);
8319
+ const privacy = await readPrivacyPolicySnapshot(this.projectRoot, manifest);
8320
+ if (privacy !== null && privacy.policy.workspaceId !== config.workspaceId)
8321
+ throw new Error("MANCODE_PRIVACY_POLICY_WORKSPACE_MISMATCH");
8322
+ const verifiedManifest = await this.readRequiredJson(
8323
+ this.mancodeRoot(),
8324
+ "schema.json",
8325
+ parseSchemaManifest
8326
+ );
8327
+ if (digestCanonicalJson(manifest) !== digestCanonicalJson(verifiedManifest))
8328
+ throw new Error("MANCODE_PROJECT_SNAPSHOT_CHANGED");
7019
8329
  return {
7020
8330
  manifest,
7021
8331
  config,
7022
8332
  policy,
7023
8333
  projectFacts,
7024
8334
  confirmedDecisions,
8335
+ privacy,
7025
8336
  fingerprint: digestCanonicalJson({
7026
8337
  manifest,
7027
8338
  config,
7028
8339
  policy,
7029
8340
  projectFacts,
7030
- confirmedDecisions
8341
+ confirmedDecisions,
8342
+ ...privacy === null ? {} : { privacy }
7031
8343
  })
7032
8344
  };
7033
8345
  }
@@ -7037,7 +8349,7 @@ var V3ContextStore = class {
7037
8349
  const parentRoot = taskRootPath(this.projectRoot, parentRef);
7038
8350
  await assertSafeDirectoryWithin(
7039
8351
  this.projectRoot,
7040
- path9.relative(this.projectRoot, parentRoot)
8352
+ path12.relative(this.projectRoot, parentRoot)
7041
8353
  );
7042
8354
  const metadata = await this.readRequiredJson(
7043
8355
  parentRoot,
@@ -7103,12 +8415,12 @@ var V3ContextStore = class {
7103
8415
  async listWorkflowMetadata() {
7104
8416
  const workflows = [];
7105
8417
  for (const namespace of ["local", "shared"]) {
7106
- const directory = path9.join(".mancode", namespace, "workflows");
8418
+ const directory = path12.join(".mancode", namespace, "workflows");
7107
8419
  const entries = await readDirectoryEntriesWithin(
7108
8420
  this.projectRoot,
7109
8421
  directory
7110
8422
  );
7111
- for (const taskId of entries.sort(compareUtf83)) {
8423
+ for (const taskId of entries.sort(compareUtf84)) {
7112
8424
  try {
7113
8425
  assertUlid(taskId, "workflow directory");
7114
8426
  } catch {
@@ -7118,7 +8430,7 @@ var V3ContextStore = class {
7118
8430
  const taskRoot = taskRootPath(this.projectRoot, taskRef);
7119
8431
  await assertSafeDirectoryWithin(
7120
8432
  this.projectRoot,
7121
- path9.relative(this.projectRoot, taskRoot)
8433
+ path12.relative(this.projectRoot, taskRoot)
7122
8434
  );
7123
8435
  const metadata = await this.readRequiredJson(
7124
8436
  taskRoot,
@@ -7132,7 +8444,7 @@ var V3ContextStore = class {
7132
8444
  }
7133
8445
  }
7134
8446
  return workflows.sort(
7135
- (left, right) => Date.parse(right.updatedAt) - Date.parse(left.updatedAt) || compareUtf83(left.taskRef.namespace, right.taskRef.namespace) || compareUtf83(left.taskRef.taskId, right.taskRef.taskId)
8447
+ (left, right) => Date.parse(right.updatedAt) - Date.parse(left.updatedAt) || compareUtf84(left.taskRef.namespace, right.taskRef.namespace) || compareUtf84(left.taskRef.taskId, right.taskRef.taskId)
7136
8448
  );
7137
8449
  }
7138
8450
  /**
@@ -7145,11 +8457,11 @@ var V3ContextStore = class {
7145
8457
  return (await this.listWorkflowMetadata()).filter(
7146
8458
  (metadata) => metadata.parent !== null && sameTaskRef(metadata.parent.taskRef, parent) && !isTerminalWorkflowStatus(metadata.status)
7147
8459
  ).map((metadata) => metadata.taskRef).sort(
7148
- (left, right) => compareUtf83(left.namespace, right.namespace) || compareUtf83(left.taskId, right.taskId)
8460
+ (left, right) => compareUtf84(left.namespace, right.namespace) || compareUtf84(left.taskId, right.taskId)
7149
8461
  );
7150
8462
  }
7151
8463
  mancodeRoot() {
7152
- return path9.join(this.projectRoot, ".mancode");
8464
+ return path12.join(this.projectRoot, ".mancode");
7153
8465
  }
7154
8466
  async readOptionalPlan(taskRef, taskRoot) {
7155
8467
  const content = await readOptionalTextWithin(taskRoot, "plan.md");
@@ -7169,7 +8481,7 @@ var V3ContextStore = class {
7169
8481
  }
7170
8482
  const checkpoint = await this.readRequiredJson(
7171
8483
  taskRoot,
7172
- path9.join("checkpoints", `${ref.artifactId}.json`),
8484
+ path12.join("checkpoints", `${ref.artifactId}.json`),
7173
8485
  parseCheckpoint
7174
8486
  );
7175
8487
  if (!sameTaskRef(checkpoint.taskRef, metadata.taskRef)) {
@@ -7180,8 +8492,8 @@ var V3ContextStore = class {
7180
8492
  async readTaskHeadFence(homeStore, taskRef) {
7181
8493
  const value = await readOptionalJsonWithin(
7182
8494
  homeStore.root,
7183
- path9.join(
7184
- path9.relative(homeStore.root, taskHeadDirectory(homeStore)),
8495
+ path12.join(
8496
+ path12.relative(homeStore.root, taskHeadDirectory(homeStore)),
7185
8497
  `${taskRef.taskId}.json`
7186
8498
  ),
7187
8499
  parseTaskHeadFence
@@ -7193,16 +8505,16 @@ var V3ContextStore = class {
7193
8505
  return value;
7194
8506
  }
7195
8507
  async readClaims(homeStore, taskRef) {
7196
- const directory = path9.relative(homeStore.root, claimDirectory(homeStore));
8508
+ const directory = path12.relative(homeStore.root, claimDirectory(homeStore));
7197
8509
  const claims = await readJsonCollectionWithin(
7198
8510
  homeStore.root,
7199
8511
  directory,
7200
8512
  parseClaim
7201
8513
  );
7202
- return claims.filter((claim) => sameTaskRef(claim.taskRef, taskRef)).sort((left, right) => compareUtf83(left.claimId, right.claimId));
8514
+ return claims.filter((claim) => sameTaskRef(claim.taskRef, taskRef)).sort((left, right) => compareUtf84(left.claimId, right.claimId));
7203
8515
  }
7204
8516
  async readHandoffs(homeStore, taskRef) {
7205
- const directory = path9.relative(
8517
+ const directory = path12.relative(
7206
8518
  homeStore.root,
7207
8519
  handoffDirectory(homeStore)
7208
8520
  );
@@ -7212,7 +8524,7 @@ var V3ContextStore = class {
7212
8524
  parseHandoff
7213
8525
  );
7214
8526
  return handoffs.filter((handoff) => sameTaskRef(handoff.taskRef, taskRef)).sort(
7215
- (left, right) => Date.parse(right.updatedAt) - Date.parse(left.updatedAt) || compareUtf83(left.handoffId, right.handoffId)
8527
+ (left, right) => Date.parse(right.updatedAt) - Date.parse(left.updatedAt) || compareUtf84(left.handoffId, right.handoffId)
7216
8528
  );
7217
8529
  }
7218
8530
  async readPendingOperations(taskRef, homeStore) {
@@ -7220,12 +8532,12 @@ var V3ContextStore = class {
7220
8532
  const [journals, reservations, gitRefWorkflowRepairs] = await Promise.all([
7221
8533
  readJsonCollectionWithin(
7222
8534
  homeStore.root,
7223
- path9.relative(homeStore.root, operationDirectory(homeStore)),
8535
+ path12.relative(homeStore.root, operationDirectory(homeStore)),
7224
8536
  parseOperationJournal
7225
8537
  ),
7226
8538
  readJsonCollectionWithin(
7227
8539
  homeStore.root,
7228
- path9.relative(homeStore.root, reservationDirectory(homeStore)),
8540
+ path12.relative(homeStore.root, reservationDirectory(homeStore)),
7229
8541
  parseOperationReservation
7230
8542
  ),
7231
8543
  listUnfinishedGitRefWorkflowRepairs(this.projectRoot)
@@ -7251,14 +8563,14 @@ var V3ContextStore = class {
7251
8563
  entityKeys: [taskKey]
7252
8564
  }));
7253
8565
  return [...primary, ...secondary, ...gitRefWorkflow].sort(
7254
- (left, right) => compareUtf83(left.operationId, right.operationId) || left.source.localeCompare(right.source, "en")
8566
+ (left, right) => compareUtf84(left.operationId, right.operationId) || left.source.localeCompare(right.source, "en")
7255
8567
  );
7256
8568
  }
7257
8569
  async readRequiredJson(root, relativePath, parser) {
7258
8570
  try {
7259
8571
  return parser(JSON.parse(await readTextWithin(root, relativePath)));
7260
8572
  } catch (error) {
7261
- if (isNotFound4(error)) {
8573
+ if (isNotFound6(error)) {
7262
8574
  throw new Error(`MANCODE_CONTEXT_ENTITY_UNAVAILABLE: ${relativePath}`);
7263
8575
  }
7264
8576
  if (error instanceof SyntaxError) {
@@ -7293,7 +8605,7 @@ async function readOptionalJsonWithin(root, relativePath, parser) {
7293
8605
  try {
7294
8606
  return parser(JSON.parse(await readTextWithin(root, relativePath)));
7295
8607
  } catch (error) {
7296
- if (isNotFound4(error)) return null;
8608
+ if (isNotFound6(error)) return null;
7297
8609
  if (error instanceof SyntaxError) {
7298
8610
  throw new Error(`MANCODE_CONTEXT_ENTITY_CORRUPT: ${relativePath}`);
7299
8611
  }
@@ -7304,14 +8616,14 @@ async function readJsonCollectionWithin(root, relativeDirectory, parser) {
7304
8616
  const entries = await readDirectoryEntriesWithin(root, relativeDirectory);
7305
8617
  const jsonEntries = entries.filter((entry) => entry.endsWith(JSON_SUFFIX));
7306
8618
  const values = [];
7307
- for (const entry of jsonEntries.sort(compareUtf83)) {
8619
+ for (const entry of jsonEntries.sort(compareUtf84)) {
7308
8620
  const stem = entry.slice(0, -JSON_SUFFIX.length);
7309
8621
  if (!/^[0-7][0-9A-HJKMNPQRSTVWXYZ]{25}$/.test(stem)) {
7310
8622
  throw new Error("MANCODE_CONTEXT_COLLECTION_ENTRY_INVALID");
7311
8623
  }
7312
8624
  const value = await readOptionalJsonWithin(
7313
8625
  root,
7314
- path9.join(relativeDirectory, entry),
8626
+ path12.join(relativeDirectory, entry),
7315
8627
  parser
7316
8628
  );
7317
8629
  if (value === null) {
@@ -7325,31 +8637,31 @@ async function readOptionalTextWithin(root, relativePath) {
7325
8637
  try {
7326
8638
  return await readTextWithin(root, relativePath);
7327
8639
  } catch (error) {
7328
- if (isNotFound4(error)) return null;
8640
+ if (isNotFound6(error)) return null;
7329
8641
  throw error;
7330
8642
  }
7331
8643
  }
7332
8644
  async function readTextWithin(root, relativePath) {
7333
- const absoluteRoot = path9.resolve(root);
8645
+ const absoluteRoot = path12.resolve(root);
7334
8646
  const segments = safeRelativeSegments(relativePath);
7335
8647
  await assertSafeDirectoryWithin(absoluteRoot, ".");
7336
8648
  const parentSegments = segments.slice(0, -1);
7337
8649
  let current = absoluteRoot;
7338
8650
  for (const segment of parentSegments) {
7339
- current = path9.join(current, segment);
8651
+ current = path12.join(current, segment);
7340
8652
  await assertSafeDirectoryAt(current);
7341
8653
  }
7342
- const target = path9.join(absoluteRoot, ...segments);
7343
- const before = await lstat4(target);
8654
+ const target = path12.join(absoluteRoot, ...segments);
8655
+ const before = await lstat6(target);
7344
8656
  if (!before.isFile() || before.isSymbolicLink()) {
7345
8657
  throw new Error("MANCODE_CONTEXT_PATH_UNSAFE");
7346
8658
  }
7347
- const content = await readFile7(target, "utf8");
8659
+ const content = await readFile10(target, "utf8");
7348
8660
  await assertSafeDirectoryWithin(
7349
8661
  absoluteRoot,
7350
- parentSegments.length === 0 ? "." : path9.join(...parentSegments)
8662
+ parentSegments.length === 0 ? "." : path12.join(...parentSegments)
7351
8663
  );
7352
- const after = await lstat4(target);
8664
+ const after = await lstat6(target);
7353
8665
  if (!after.isFile() || after.isSymbolicLink() || before.dev !== after.dev || before.ino !== after.ino) {
7354
8666
  throw new Error("MANCODE_CONTEXT_PATH_UNSAFE");
7355
8667
  }
@@ -7358,43 +8670,43 @@ async function readTextWithin(root, relativePath) {
7358
8670
  async function readDirectoryEntriesWithin(root, relativeDirectory) {
7359
8671
  try {
7360
8672
  const directory = await assertSafeDirectoryWithin(root, relativeDirectory);
7361
- return await readdir4(directory);
8673
+ return await readdir7(directory);
7362
8674
  } catch (error) {
7363
- if (isNotFound4(error)) return [];
8675
+ if (isNotFound6(error)) return [];
7364
8676
  throw error;
7365
8677
  }
7366
8678
  }
7367
8679
  async function assertSafeDirectoryWithin(root, relativeDirectory) {
7368
- const absoluteRoot = path9.resolve(root);
8680
+ const absoluteRoot = path12.resolve(root);
7369
8681
  const segments = relativeDirectory === "." ? [] : safeRelativeSegments(relativeDirectory);
7370
8682
  await assertSafeDirectoryAt(absoluteRoot);
7371
8683
  let current = absoluteRoot;
7372
8684
  for (const segment of segments) {
7373
- current = path9.join(current, segment);
8685
+ current = path12.join(current, segment);
7374
8686
  await assertSafeDirectoryAt(current);
7375
8687
  }
7376
8688
  return current;
7377
8689
  }
7378
8690
  async function assertSafeDirectoryAt(target) {
7379
- const stat2 = await lstat4(target);
8691
+ const stat2 = await lstat6(target);
7380
8692
  if (!stat2.isDirectory() || stat2.isSymbolicLink()) {
7381
8693
  throw new Error("MANCODE_CONTEXT_PATH_UNSAFE");
7382
8694
  }
7383
8695
  }
7384
8696
  function safeRelativeSegments(relativePath) {
7385
- if (typeof relativePath !== "string" || !relativePath || relativePath.includes("\0") || path9.isAbsolute(relativePath)) {
8697
+ if (typeof relativePath !== "string" || !relativePath || relativePath.includes("\0") || path12.isAbsolute(relativePath)) {
7386
8698
  throw new Error("MANCODE_CONTEXT_PATH_UNSAFE");
7387
8699
  }
7388
- const segments = relativePath.split(path9.sep);
8700
+ const segments = relativePath.split(path12.sep);
7389
8701
  if (segments.some((segment) => !segment || segment === "." || segment === "..")) {
7390
8702
  throw new Error("MANCODE_CONTEXT_PATH_UNSAFE");
7391
8703
  }
7392
8704
  return segments;
7393
8705
  }
7394
- function compareUtf83(left, right) {
8706
+ function compareUtf84(left, right) {
7395
8707
  return Buffer.from(left, "utf8").compare(Buffer.from(right, "utf8"));
7396
8708
  }
7397
- function isNotFound4(error) {
8709
+ function isNotFound6(error) {
7398
8710
  return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
7399
8711
  }
7400
8712
  function isTerminalWorkflowStatus(status) {
@@ -7405,38 +8717,17 @@ function storedTaskAggregateDigest(snapshot) {
7405
8717
  }
7406
8718
 
7407
8719
  export {
7408
- isUlid,
7409
- assertUlid,
7410
- createUlid,
7411
- assertRecord,
7412
- assertKnownKeys,
7413
8720
  parseSchemaManifest,
7414
8721
  serializeSchemaManifest,
7415
8722
  managedAdapterNames,
7416
- parseSchemaManifestV1,
7417
- parseSchemaManifestV2,
7418
8723
  assertSchemaManifestTransition,
8724
+ assertSchemaManifestPrivacyTransition,
7419
8725
  assertSchemaManifestPolicyUpgrade,
7420
8726
  assertActivationRollbackManifestTransition,
7421
8727
  managedAdapterInventoriesMatch,
7422
- formatTaskRef,
7423
- parseTaskRef,
7424
- parseTaskRefValue,
7425
- sameTaskRef,
7426
- resolveTaskEntityHomeStore,
7427
- resolveLocalEntityHomeStore,
7428
- resolveCoordinationEntityHomeStore,
7429
- operationDirectory,
7430
- lockDirectory,
7431
- claimDirectory,
7432
- handoffDirectory,
7433
- taskHeadDirectory,
7434
8728
  gitRefWorkflowRepairJournalPath,
7435
8729
  listGitRefWorkflowRepairJournalSummaries,
7436
8730
  listUnfinishedGitRefWorkflowRepairs,
7437
- canonicalizeJson,
7438
- digestCanonicalJson,
7439
- sortUtf8StringSet,
7440
8731
  scanSharedText,
7441
8732
  assertSharedTextSafe,
7442
8733
  assertSafeSharedRelativePath,
@@ -7446,14 +8737,10 @@ export {
7446
8737
  parseAuthorizationBasis,
7447
8738
  assertRepairUsesOriginalAuthorization,
7448
8739
  parseOperationJournal,
7449
- assertOperationJournalTransition,
7450
- operationJournalDigest,
7451
8740
  operationReservationJournalDigest,
7452
8741
  assertOperationReservationTopology,
7453
8742
  withOperationReservationDigests,
7454
8743
  parseOperationReservation,
7455
- createOperationReservation,
7456
- writeOperationReservation,
7457
8744
  readOperationReservation,
7458
8745
  removeOperationReservation,
7459
8746
  parseCheckpoint,
@@ -7500,13 +8787,41 @@ export {
7500
8787
  normalizeClaimScope,
7501
8788
  parseHandoff,
7502
8789
  assertHandoffTransition,
7503
- parseProjectConfig,
7504
- parseTeamPolicy,
7505
- projectConfigIdentityDigest,
7506
- projectConfigDigest,
7507
- assertConfigPolicyConsistency,
7508
- assertProjectConfigTransition,
7509
- assertTeamPolicyTransition,
8790
+ acquireLocalLock,
8791
+ acquireEntityLocks,
8792
+ acquireOperationEntityLocks,
8793
+ readLocalLock,
8794
+ replaceFileAtomically,
8795
+ getOperationDefinition,
8796
+ assertOperationJournalMatchesDefinition,
8797
+ createPreparedOperationJournal,
8798
+ prepareOperationStores,
8799
+ readOperationJournal,
8800
+ updateOperationJournal,
8801
+ listUnfinishedOperationJournals,
8802
+ PROJECT_SCHEMA_LOCK,
8803
+ acquireProjectWriteBarrier,
8804
+ PRIVACY_MIN_VERSION,
8805
+ PRIVACY_POLICY_FILE,
8806
+ PRIVACY_EXCLUSIONS_FILE,
8807
+ parsePrivacyPolicyCandidate,
8808
+ parsePrivacyPolicy,
8809
+ parsePrivacyExclusions,
8810
+ createInitialPrivacyPolicy,
8811
+ assertPrivacyPolicyTransition,
8812
+ assertPrivacyExclusionsTransition,
8813
+ readPrivacyPolicySnapshot,
8814
+ readPrivacyPolicyStatus,
8815
+ readPrivacyAuthorityFile,
8816
+ containsEnhancedSensitiveText,
8817
+ isPrivacyExcluded,
8818
+ assertPrivacyValueAllowed,
8819
+ assertSharedPrivacyValue,
8820
+ acquireSharedPrivacyWriteBarrier,
8821
+ withSharedPrivacyWrite,
8822
+ assertSharedTaskWriteAtRoot,
8823
+ assertPrivacyRecoveryActionsAllowed,
8824
+ scanPrivacyActivation,
7510
8825
  createConfirmedDecision,
7511
8826
  publishConfirmedDecision,
7512
8827
  readConfirmedDecision,
@@ -7521,4 +8836,4 @@ export {
7521
8836
  V3ContextStore,
7522
8837
  storedTaskAggregateDigest
7523
8838
  };
7524
- //# sourceMappingURL=chunk-NG72XZ3R.js.map
8839
+ //# sourceMappingURL=chunk-E2K22WYH.js.map