@fro.bot/systematic 3.5.4 → 3.5.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -19,8 +19,8 @@ import {
19
19
 
20
20
  // src/index.ts
21
21
  import { createHash as createHash5 } from "crypto";
22
- import fs11 from "fs";
23
- import path10 from "path";
22
+ import fs12 from "fs";
23
+ import path11 from "path";
24
24
  import { fileURLToPath as fileURLToPath3, pathToFileURL as pathToFileURL3 } from "url";
25
25
 
26
26
  // src/lib/bootstrap.ts
@@ -1333,10 +1333,20 @@ function digest(domain, facts) {
1333
1333
  function isCommandTimeout(error) {
1334
1334
  return error !== null && typeof error === "object" && "code" in error && error.code === "ETIMEDOUT";
1335
1335
  }
1336
+ function sanitizedEnvironment() {
1337
+ const env = { ...process.env };
1338
+ for (const key of Object.keys(env)) {
1339
+ if (key.startsWith("GIT_")) {
1340
+ delete env[key];
1341
+ }
1342
+ }
1343
+ return env;
1344
+ }
1336
1345
  function defaultCommandRunner(args2, cwd, maxOutputBytes, timeoutMs) {
1337
1346
  try {
1338
1347
  const result = spawnSync("git", [...args2], {
1339
1348
  cwd,
1349
+ env: sanitizedEnvironment(),
1340
1350
  encoding: "utf8",
1341
1351
  maxBuffer: maxOutputBytes,
1342
1352
  timeout: timeoutMs
@@ -1355,6 +1365,7 @@ function defaultRemoteCommandRunner(executable, args2, cwd, maxOutputBytes, time
1355
1365
  try {
1356
1366
  const result = spawnSync(executable, [...args2], {
1357
1367
  cwd,
1368
+ env: sanitizedEnvironment(),
1358
1369
  encoding: "utf8",
1359
1370
  maxBuffer: maxOutputBytes,
1360
1371
  timeout: timeoutMs
@@ -1429,6 +1440,140 @@ function runCommand(runner, args2, cwd, limits) {
1429
1440
  }
1430
1441
  return { status: "ok", output: { stdout: result.stdout } };
1431
1442
  }
1443
+ function canonicalPath(filePath, realPath = fs8.realpathSync) {
1444
+ try {
1445
+ return realPath(filePath);
1446
+ } catch {
1447
+ return;
1448
+ }
1449
+ }
1450
+ function requiredGitPath(runner, args2, cwd, limits, realPath = fs8.realpathSync) {
1451
+ const result = runCommand(runner, args2, cwd, limits);
1452
+ if (result.status === "error")
1453
+ return result;
1454
+ const rawValue = result.output.stdout.trim();
1455
+ if (!path8.isAbsolute(rawValue)) {
1456
+ return { status: "error", reasonCode: "target-unavailable" };
1457
+ }
1458
+ const value = canonicalPath(rawValue, realPath);
1459
+ return value === undefined ? { status: "error", reasonCode: "target-unavailable" } : { status: "ok", value };
1460
+ }
1461
+ function captureParentIdentity(targetDirectory, runner, limits, realPath = fs8.realpathSync) {
1462
+ const targetRoot = requiredGitPath(runner, ["rev-parse", "--show-toplevel"], targetDirectory, limits, realPath);
1463
+ if (targetRoot.status === "error")
1464
+ return targetRoot;
1465
+ const gitDir = requiredGitPath(runner, ["rev-parse", "--absolute-git-dir"], targetDirectory, limits, realPath);
1466
+ if (gitDir.status === "error")
1467
+ return gitDir;
1468
+ const commonDir = requiredGitPath(runner, ["rev-parse", "--path-format=absolute", "--git-common-dir"], targetDirectory, limits, realPath);
1469
+ if (commonDir.status === "error")
1470
+ return commonDir;
1471
+ const worktreeList = runCommand(runner, ["worktree", "list", "--porcelain", "-z"], targetDirectory, limits);
1472
+ if (worktreeList.status === "error")
1473
+ return worktreeList;
1474
+ const registeredWorktreeRoots = worktreeList.output.stdout.split("\x00").filter((record) => record.startsWith("worktree ")).map((record) => canonicalPath(record.slice("worktree ".length), realPath));
1475
+ if (registeredWorktreeRoots.some((root) => root === undefined)) {
1476
+ return { status: "error", reasonCode: "target-unavailable" };
1477
+ }
1478
+ const roots = registeredWorktreeRoots.filter((root) => root !== undefined);
1479
+ return roots.length > 0 ? {
1480
+ status: "ok",
1481
+ identity: {
1482
+ targetRoot: targetRoot.value,
1483
+ gitDir: gitDir.value,
1484
+ commonDir: commonDir.value,
1485
+ registeredWorktreeRoots: roots
1486
+ }
1487
+ } : { status: "error", reasonCode: "target-unavailable" };
1488
+ }
1489
+ function readGitfileTarget(gitfilePath, realPath = fs8.realpathSync) {
1490
+ let contents;
1491
+ try {
1492
+ contents = fs8.readFileSync(gitfilePath, "utf8");
1493
+ } catch {
1494
+ return;
1495
+ }
1496
+ const match = /^gitdir:\s*(.+?)\s*$/im.exec(contents);
1497
+ if (!match)
1498
+ return;
1499
+ const target = path8.isAbsolute(match[1]) ? match[1] : path8.resolve(path8.dirname(gitfilePath), match[1]);
1500
+ return canonicalPath(target, realPath);
1501
+ }
1502
+ function readGitdirBacklink(backlinkPath, realPath) {
1503
+ let contents;
1504
+ try {
1505
+ contents = fs8.readFileSync(backlinkPath, "utf8").trim();
1506
+ } catch {
1507
+ return;
1508
+ }
1509
+ if (contents.length === 0)
1510
+ return;
1511
+ const target = path8.isAbsolute(contents) ? contents : path8.resolve(path8.dirname(backlinkPath), contents);
1512
+ return canonicalPath(target, realPath);
1513
+ }
1514
+ function isPathWithin(root, candidate) {
1515
+ const relative = path8.relative(root, candidate);
1516
+ return relative !== "" && relative !== ".." && !relative.startsWith(`..${path8.sep}`) && !path8.isAbsolute(relative);
1517
+ }
1518
+ function validateDotGitLinkage(targetRoot, gitDir, commonDir, realPath) {
1519
+ const dotGit = path8.join(targetRoot, ".git");
1520
+ let dotGitStat;
1521
+ try {
1522
+ dotGitStat = fs8.lstatSync(dotGit);
1523
+ } catch {
1524
+ return false;
1525
+ }
1526
+ if (dotGitStat.isDirectory()) {
1527
+ return gitDir === commonDir && canonicalPath(dotGit, realPath) === commonDir;
1528
+ }
1529
+ if (!dotGitStat.isFile() || dotGitStat.isSymbolicLink() || !isPathWithin(path8.join(commonDir, "worktrees"), gitDir) || readGitfileTarget(dotGit, realPath) !== gitDir) {
1530
+ return false;
1531
+ }
1532
+ const backlinkPath = path8.join(gitDir, "gitdir");
1533
+ let backlinkStat;
1534
+ try {
1535
+ backlinkStat = fs8.lstatSync(backlinkPath);
1536
+ } catch {
1537
+ return false;
1538
+ }
1539
+ return backlinkStat.isFile() && !backlinkStat.isSymbolicLink() && readGitdirBacklink(backlinkPath, realPath) === canonicalPath(dotGit, realPath);
1540
+ }
1541
+ function validateRegisteredWorktree(candidateDirectory, parentIdentity, options = {}) {
1542
+ const runner = options.commandRunner ?? defaultCommandRunner;
1543
+ const limits = mergeLimits(options.limits);
1544
+ const realPath = options.realPath ?? fs8.realpathSync;
1545
+ const candidateRoot = canonicalPath(candidateDirectory, realPath);
1546
+ if (candidateRoot === undefined) {
1547
+ return { status: "error", reasonCode: "target-unavailable" };
1548
+ }
1549
+ const inside = runCommand(runner, ["rev-parse", "--is-inside-work-tree"], candidateRoot, limits);
1550
+ if (inside.status === "error")
1551
+ return inside;
1552
+ if (inside.output.stdout.trim() !== "true") {
1553
+ return { status: "error", reasonCode: "target-unavailable" };
1554
+ }
1555
+ const targetRoot = requiredGitPath(runner, ["rev-parse", "--show-toplevel"], candidateRoot, limits, realPath);
1556
+ if (targetRoot.status === "error")
1557
+ return targetRoot;
1558
+ const gitDir = requiredGitPath(runner, ["rev-parse", "--absolute-git-dir"], candidateRoot, limits, realPath);
1559
+ if (gitDir.status === "error")
1560
+ return gitDir;
1561
+ const commonDir = requiredGitPath(runner, ["rev-parse", "--path-format=absolute", "--git-common-dir"], candidateRoot, limits, realPath);
1562
+ if (commonDir.status === "error")
1563
+ return commonDir;
1564
+ if (commonDir.value !== parentIdentity.commonDir || !parentIdentity.registeredWorktreeRoots.includes(targetRoot.value)) {
1565
+ return { status: "error", reasonCode: "target-unavailable" };
1566
+ }
1567
+ if (!validateDotGitLinkage(targetRoot.value, gitDir.value, commonDir.value, realPath)) {
1568
+ return { status: "error", reasonCode: "target-unavailable" };
1569
+ }
1570
+ return {
1571
+ status: "ok",
1572
+ targetRoot: targetRoot.value,
1573
+ gitDir: gitDir.value,
1574
+ commonDir: commonDir.value
1575
+ };
1576
+ }
1432
1577
  function readRevision(runner, context, limits) {
1433
1578
  const branch = runCommand(runner, ["symbolic-ref", "--short", "HEAD"], context.commandDirectory, limits);
1434
1579
  if (branch.status === "error" && branch.reasonCode !== "command-failed") {
@@ -1785,9 +1930,11 @@ function createOpencodeOperationObserver(options) {
1785
1930
  } catch {
1786
1931
  targetDirectory = path8.resolve(options.targetDirectory);
1787
1932
  }
1788
- const targetDigest = digest("target", [targetDirectory]);
1789
1933
  const runner = options.commandRunner ?? defaultCommandRunner;
1790
1934
  const remoteRunner = options.remoteCommandRunner ?? defaultRemoteCommandRunner;
1935
+ const realPath = options.realPath ?? fs8.realpathSync;
1936
+ const parentIdentityResult = captureParentIdentity(targetDirectory, runner, limits, realPath);
1937
+ const targetDigest = digest("target", [targetDirectory]);
1791
1938
  const fileReader = options.fileReader ?? fs8.readFileSync;
1792
1939
  const symlinkReader = options.symlinkReader ?? fs8.readlinkSync;
1793
1940
  const statReader = options.statReader ?? ((filePath) => {
@@ -1802,6 +1949,11 @@ function createOpencodeOperationObserver(options) {
1802
1949
  });
1803
1950
  return {
1804
1951
  targetDigest,
1952
+ validateRegisteredWorktree(candidateDirectory) {
1953
+ if (parentIdentityResult.status === "error")
1954
+ return parentIdentityResult;
1955
+ return validateRegisteredWorktree(candidateDirectory, parentIdentityResult.identity, { commandRunner: runner, limits, realPath });
1956
+ },
1805
1957
  async snapshot() {
1806
1958
  const rootResult = runCommand(runner, ["rev-parse", "--show-toplevel"], targetDirectory, limits);
1807
1959
  if (rootResult.status === "error")
@@ -1849,6 +2001,8 @@ function createOpencodeOperationObserver(options) {
1849
2001
 
1850
2002
  // src/lib/opencode-workflow-guard.ts
1851
2003
  import { randomBytes as randomBytes4 } from "crypto";
2004
+ import fs10 from "fs";
2005
+ import path9 from "path";
1852
2006
 
1853
2007
  // src/lib/question-attestation.ts
1854
2008
  import { createHash as createHash3, randomBytes } from "crypto";
@@ -2369,8 +2523,10 @@ import { randomBytes as randomBytes2 } from "crypto";
2369
2523
 
2370
2524
  // src/lib/receipt-readback.ts
2371
2525
  import { createHash as createHash4 } from "crypto";
2372
- var RECEIPT_READBACK_SCHEMA_VERSION = 1;
2373
- var RECEIPT_READBACK_PROTOCOL_VERSION = 1;
2526
+ var RECEIPT_READBACK_SCHEMA_VERSION = 2;
2527
+ var RECEIPT_READBACK_PROTOCOL_VERSION = 2;
2528
+ var LEGACY_RECEIPT_READBACK_SCHEMA_VERSION = 1;
2529
+ var LEGACY_RECEIPT_READBACK_PROTOCOL_VERSION = 1;
2374
2530
  var MAX_CAPABILITIES = 16;
2375
2531
  var MAX_CAPABILITY_LENGTH = 128;
2376
2532
  var MAX_MARKERS = 128;
@@ -2412,6 +2568,12 @@ var CANONICAL_REQUIRED_KEYS = [
2412
2568
  "timestamp"
2413
2569
  ];
2414
2570
  var CANONICAL_OPTIONAL_KEYS = [
2571
+ "repositoryDigest",
2572
+ "worktreeDigest",
2573
+ "operationTargetIdentity",
2574
+ "resourceDigest"
2575
+ ];
2576
+ var LEGACY_CANONICAL_OPTIONAL_KEYS = [
2415
2577
  "repositoryDigest",
2416
2578
  "worktreeDigest",
2417
2579
  "resourceDigest"
@@ -2465,12 +2627,14 @@ var UNIT_PROGRESSION_KEYS = [
2465
2627
  "family",
2466
2628
  "requiredOperations",
2467
2629
  "resourceScopes",
2630
+ "pinnedOperationTargetIdentity",
2468
2631
  "state",
2469
2632
  "transitionDigest",
2470
2633
  "timestamp",
2471
2634
  "sessionSalt",
2472
2635
  "integrity"
2473
2636
  ];
2637
+ var LEGACY_UNIT_PROGRESSION_KEYS = UNIT_PROGRESSION_KEYS.filter((key) => key !== "pinnedOperationTargetIdentity");
2474
2638
  var RESOURCE_SCOPE_KEYS = ["operation", "resourceIdentity"];
2475
2639
  var LEDGER_METADATA_KEYS = [
2476
2640
  "schemaVersion",
@@ -2489,6 +2653,12 @@ function hasExactKeys(value, required, optional = []) {
2489
2653
  const keys = Object.keys(value);
2490
2654
  return required.every((key) => Object.hasOwn(value, key)) && keys.every((key) => allowed.has(key));
2491
2655
  }
2656
+ function isCurrentVersionPair(schemaVersion, protocolVersion) {
2657
+ return schemaVersion === RECEIPT_READBACK_SCHEMA_VERSION && protocolVersion === RECEIPT_READBACK_PROTOCOL_VERSION;
2658
+ }
2659
+ function isLegacyVersionPair(schemaVersion, protocolVersion) {
2660
+ return schemaVersion === LEGACY_RECEIPT_READBACK_SCHEMA_VERSION && protocolVersion === LEGACY_RECEIPT_READBACK_PROTOCOL_VERSION;
2661
+ }
2492
2662
  function isDigest(value) {
2493
2663
  return typeof value === "string" && DIGEST_PATTERN.test(value);
2494
2664
  }
@@ -2589,6 +2759,7 @@ function cloneCanonical(canonical) {
2589
2759
  workspaceDigest: canonical.workspaceDigest,
2590
2760
  repositoryDigest: canonical.repositoryDigest,
2591
2761
  worktreeDigest: canonical.worktreeDigest,
2762
+ ...isLocalOperation(canonical.operation) ? { operationTargetIdentity: canonical.operationTargetIdentity } : {},
2592
2763
  resourceDigest: canonical.resourceDigest,
2593
2764
  operation: canonical.operation,
2594
2765
  result: "success",
@@ -2610,16 +2781,19 @@ function cloneEnvelope(envelope) {
2610
2781
  function parseEnvelope(value) {
2611
2782
  if (!isRecord3(value) || !hasExactKeys(value, ENVELOPE_KEYS))
2612
2783
  return;
2613
- if (value.schemaVersion !== 1 || value.protocolVersion !== 1 || value.compatibility !== "compatible" || !isDigest(value.registrationDigest) || !isCapabilityList(value.capabilityFlags) || !isRecord3(value.canonical)) {
2784
+ const currentVersion = isCurrentVersionPair(value.schemaVersion, value.protocolVersion);
2785
+ const legacyVersion = isLegacyVersionPair(value.schemaVersion, value.protocolVersion);
2786
+ if (!currentVersion && !legacyVersion || value.compatibility !== "compatible" || !isDigest(value.registrationDigest) || !isCapabilityList(value.capabilityFlags) || !isRecord3(value.canonical)) {
2614
2787
  return;
2615
2788
  }
2616
2789
  const canonical = value.canonical;
2617
- if (!hasExactKeys(canonical, CANONICAL_REQUIRED_KEYS, CANONICAL_OPTIONAL_KEYS) || !isReceiptId(canonical.receiptId) || !isDigest(canonical.registrationDigest) || !isDigest(canonical.callDigest) || !isDigest(canonical.epochDigest) || !isDigest(canonical.unitDigest) || !isDigest(canonical.workspaceDigest) || canonical.repositoryDigest !== undefined && !isDigest(canonical.repositoryDigest) || canonical.worktreeDigest !== undefined && !isDigest(canonical.worktreeDigest) || canonical.resourceDigest !== undefined && !isDigest(canonical.resourceDigest) || !isOperation(canonical.operation) || canonical.result !== "success" || canonical.source !== "runtime-verified" || canonical.consumption !== "available" && canonical.consumption !== "consumed" || !isTimestamp(canonical.timestamp) || canonical.registrationDigest !== value.registrationDigest) {
2790
+ const operation = isOperation(canonical.operation) ? canonical.operation : undefined;
2791
+ if (!hasExactKeys(canonical, CANONICAL_REQUIRED_KEYS, currentVersion ? CANONICAL_OPTIONAL_KEYS : LEGACY_CANONICAL_OPTIONAL_KEYS) || !isReceiptId(canonical.receiptId) || !isDigest(canonical.registrationDigest) || !isDigest(canonical.callDigest) || !isDigest(canonical.epochDigest) || !isDigest(canonical.unitDigest) || !isDigest(canonical.workspaceDigest) || canonical.repositoryDigest !== undefined && !isDigest(canonical.repositoryDigest) || canonical.worktreeDigest !== undefined && !isDigest(canonical.worktreeDigest) || canonical.operationTargetIdentity !== undefined && !isDigest(canonical.operationTargetIdentity) || operation === undefined || currentVersion && isLocalOperation(operation) && !isDigest(canonical.operationTargetIdentity) || currentVersion && !isLocalOperation(operation) && Object.hasOwn(canonical, "operationTargetIdentity") || legacyVersion && Object.hasOwn(canonical, "operationTargetIdentity") || canonical.resourceDigest !== undefined && !isDigest(canonical.resourceDigest) || canonical.result !== "success" || canonical.source !== "runtime-verified" || canonical.consumption !== "available" && canonical.consumption !== "consumed" || !isTimestamp(canonical.timestamp) || canonical.registrationDigest !== value.registrationDigest) {
2618
2792
  return;
2619
2793
  }
2620
2794
  return {
2621
- schemaVersion: 1,
2622
- protocolVersion: 1,
2795
+ schemaVersion: currentVersion ? RECEIPT_READBACK_SCHEMA_VERSION : LEGACY_RECEIPT_READBACK_SCHEMA_VERSION,
2796
+ protocolVersion: currentVersion ? RECEIPT_READBACK_PROTOCOL_VERSION : LEGACY_RECEIPT_READBACK_PROTOCOL_VERSION,
2623
2797
  registrationDigest: value.registrationDigest,
2624
2798
  capabilityFlags: [...value.capabilityFlags],
2625
2799
  compatibility: "compatible",
@@ -2632,8 +2806,9 @@ function parseEnvelope(value) {
2632
2806
  workspaceDigest: canonical.workspaceDigest,
2633
2807
  repositoryDigest: canonical.repositoryDigest,
2634
2808
  worktreeDigest: canonical.worktreeDigest,
2809
+ ...isLocalOperation(operation) ? { operationTargetIdentity: canonical.operationTargetIdentity } : {},
2635
2810
  resourceDigest: canonical.resourceDigest,
2636
- operation: canonical.operation,
2811
+ operation,
2637
2812
  result: "success",
2638
2813
  source: "runtime-verified",
2639
2814
  consumption: canonical.consumption,
@@ -2642,40 +2817,42 @@ function parseEnvelope(value) {
2642
2817
  };
2643
2818
  }
2644
2819
  function parseLedgerMetadata(value) {
2645
- if (!isRecord3(value) || !hasExactKeys(value, LEDGER_METADATA_KEYS) || value.schemaVersion !== 1 || value.protocolVersion !== 1 || !isDigest(value.registrationDigest) || !isCapabilityList(value.capabilityFlags)) {
2820
+ if (!isRecord3(value) || !hasExactKeys(value, LEDGER_METADATA_KEYS) || value.schemaVersion !== RECEIPT_READBACK_SCHEMA_VERSION || value.protocolVersion !== RECEIPT_READBACK_PROTOCOL_VERSION || !isDigest(value.registrationDigest) || !isCapabilityList(value.capabilityFlags)) {
2646
2821
  return;
2647
2822
  }
2648
2823
  return {
2649
- schemaVersion: 1,
2650
- protocolVersion: 1,
2824
+ schemaVersion: RECEIPT_READBACK_SCHEMA_VERSION,
2825
+ protocolVersion: RECEIPT_READBACK_PROTOCOL_VERSION,
2651
2826
  registrationDigest: value.registrationDigest,
2652
2827
  capabilityFlags: [...value.capabilityFlags]
2653
2828
  };
2654
2829
  }
2655
2830
  function serializeEnvelope(envelope) {
2656
2831
  const canonical = envelope.canonical;
2832
+ const canonicalFields = {
2833
+ receiptId: canonical.receiptId,
2834
+ registrationDigest: canonical.registrationDigest,
2835
+ callDigest: canonical.callDigest,
2836
+ epochDigest: canonical.epochDigest,
2837
+ unitDigest: canonical.unitDigest,
2838
+ workspaceDigest: canonical.workspaceDigest,
2839
+ repositoryDigest: canonical.repositoryDigest ?? null,
2840
+ worktreeDigest: canonical.worktreeDigest ?? null,
2841
+ ...envelope.schemaVersion === RECEIPT_READBACK_SCHEMA_VERSION ? { operationTargetIdentity: canonical.operationTargetIdentity ?? null } : {},
2842
+ resourceDigest: canonical.resourceDigest ?? null,
2843
+ operation: canonical.operation,
2844
+ result: canonical.result,
2845
+ source: canonical.source,
2846
+ consumption: canonical.consumption,
2847
+ timestamp: canonical.timestamp
2848
+ };
2657
2849
  return JSON.stringify({
2658
2850
  schemaVersion: envelope.schemaVersion,
2659
2851
  protocolVersion: envelope.protocolVersion,
2660
2852
  registrationDigest: envelope.registrationDigest,
2661
2853
  capabilityFlags: [...envelope.capabilityFlags],
2662
2854
  compatibility: envelope.compatibility,
2663
- canonical: {
2664
- receiptId: canonical.receiptId,
2665
- registrationDigest: canonical.registrationDigest,
2666
- callDigest: canonical.callDigest,
2667
- epochDigest: canonical.epochDigest,
2668
- unitDigest: canonical.unitDigest,
2669
- workspaceDigest: canonical.workspaceDigest,
2670
- repositoryDigest: canonical.repositoryDigest ?? null,
2671
- worktreeDigest: canonical.worktreeDigest ?? null,
2672
- resourceDigest: canonical.resourceDigest ?? null,
2673
- operation: canonical.operation,
2674
- result: canonical.result,
2675
- source: canonical.source,
2676
- consumption: canonical.consumption,
2677
- timestamp: canonical.timestamp
2678
- }
2855
+ canonical: canonicalFields
2679
2856
  });
2680
2857
  }
2681
2858
  function hashIntegrity(scope, value) {
@@ -2711,7 +2888,10 @@ function progressionIntegrity(marker) {
2711
2888
  unitDigest: marker.unitDigest,
2712
2889
  family: marker.family,
2713
2890
  requiredOperations: [...marker.requiredOperations],
2714
- resourceScopes: marker.resourceScopes.map((scope) => ({ ...scope }))
2891
+ resourceScopes: marker.resourceScopes.map((scope) => ({ ...scope })),
2892
+ ...marker.schemaVersion === RECEIPT_READBACK_SCHEMA_VERSION ? {
2893
+ pinnedOperationTargetIdentity: marker.pinnedOperationTargetIdentity ?? null
2894
+ } : {}
2715
2895
  };
2716
2896
  return hashIntegrity("progression", JSON.stringify({
2717
2897
  kind: marker.kind,
@@ -2737,15 +2917,21 @@ function parseMintMarker(value) {
2737
2917
  if (value.kind !== "mint") {
2738
2918
  return markerResult(value.kind === "control" ? "unknown-kind" : "unknown-kind");
2739
2919
  }
2740
- if (value.schemaVersion !== RECEIPT_READBACK_SCHEMA_VERSION)
2741
- return markerResult("unknown-schema");
2742
- if (value.protocolVersion !== RECEIPT_READBACK_PROTOCOL_VERSION)
2920
+ const currentVersion = isCurrentVersionPair(value.schemaVersion, value.protocolVersion);
2921
+ const legacyVersion = isLegacyVersionPair(value.schemaVersion, value.protocolVersion);
2922
+ if (!currentVersion && !legacyVersion) {
2923
+ if (value.schemaVersion !== RECEIPT_READBACK_SCHEMA_VERSION && value.schemaVersion !== LEGACY_RECEIPT_READBACK_SCHEMA_VERSION)
2924
+ return markerResult("unknown-schema");
2743
2925
  return markerResult("unknown-protocol");
2926
+ }
2744
2927
  if (!hasExactKeys(value, MINT_KEYS))
2745
2928
  return markerResult("forbidden-field");
2746
2929
  const envelope = parseEnvelope(value.envelope);
2747
2930
  if (!envelope)
2748
2931
  return markerResult("malformed");
2932
+ if (currentVersion && envelope.schemaVersion !== RECEIPT_READBACK_SCHEMA_VERSION || legacyVersion && envelope.schemaVersion !== 1) {
2933
+ return markerResult("malformed");
2934
+ }
2749
2935
  if (!isSessionSaltHex(value.sessionSalt))
2750
2936
  return markerResult("malformed");
2751
2937
  if (!isDigest(value.integrity))
@@ -2754,8 +2940,8 @@ function parseMintMarker(value) {
2754
2940
  return markerResult("malformed");
2755
2941
  const marker = {
2756
2942
  kind: "mint",
2757
- schemaVersion: RECEIPT_READBACK_SCHEMA_VERSION,
2758
- protocolVersion: RECEIPT_READBACK_PROTOCOL_VERSION,
2943
+ schemaVersion: value.schemaVersion,
2944
+ protocolVersion: value.protocolVersion,
2759
2945
  envelope,
2760
2946
  sessionSalt: value.sessionSalt,
2761
2947
  integrity: value.integrity
@@ -2767,13 +2953,15 @@ function parseMintMarker(value) {
2767
2953
  function parseConsumeMarker(value) {
2768
2954
  if (!hasExactKeys(value, CONSUME_KEYS))
2769
2955
  return markerResult("forbidden-field");
2770
- if (value.schemaVersion !== RECEIPT_READBACK_SCHEMA_VERSION || value.protocolVersion !== RECEIPT_READBACK_PROTOCOL_VERSION || value.kind !== "control" || value.control !== "consume" || !isDigest(value.registrationDigest) || !isCapabilityList(value.capabilityFlags) || !isReceiptId(value.receiptId) || !isDigest(value.transitionDigest) || !isTimestamp(value.timestamp) || !isDigest(value.integrity)) {
2956
+ const currentVersion = isCurrentVersionPair(value.schemaVersion, value.protocolVersion);
2957
+ const legacyVersion = isLegacyVersionPair(value.schemaVersion, value.protocolVersion);
2958
+ if (!currentVersion && !legacyVersion || value.kind !== "control" || value.control !== "consume" || !isDigest(value.registrationDigest) || !isCapabilityList(value.capabilityFlags) || !isReceiptId(value.receiptId) || !isDigest(value.transitionDigest) || !isTimestamp(value.timestamp) || !isDigest(value.integrity)) {
2771
2959
  return markerResult("malformed");
2772
2960
  }
2773
2961
  const marker = {
2774
2962
  kind: "control",
2775
- schemaVersion: RECEIPT_READBACK_SCHEMA_VERSION,
2776
- protocolVersion: RECEIPT_READBACK_PROTOCOL_VERSION,
2963
+ schemaVersion: value.schemaVersion,
2964
+ protocolVersion: value.protocolVersion,
2777
2965
  registrationDigest: value.registrationDigest,
2778
2966
  capabilityFlags: [...value.capabilityFlags],
2779
2967
  control: "consume",
@@ -2794,8 +2982,10 @@ function parseProgressionMarker(value) {
2794
2982
  return markerResult("unknown-kind");
2795
2983
  }
2796
2984
  function parseProgressionBase(value) {
2797
- if (value.schemaVersion === RECEIPT_READBACK_SCHEMA_VERSION && value.protocolVersion === RECEIPT_READBACK_PROTOCOL_VERSION && value.kind === "control" && value.control === "progression" && isDigest(value.registrationDigest) && isCapabilityList(value.capabilityFlags) && (value.state === "started" || value.state === "completed") && isDigest(value.transitionDigest) && isTimestamp(value.timestamp) && isSessionSaltHex(value.sessionSalt) && isDigest(value.integrity)) {
2985
+ if ((isCurrentVersionPair(value.schemaVersion, value.protocolVersion) || isLegacyVersionPair(value.schemaVersion, value.protocolVersion)) && value.kind === "control" && value.control === "progression" && isDigest(value.registrationDigest) && isCapabilityList(value.capabilityFlags) && (value.state === "started" || value.state === "completed") && isDigest(value.transitionDigest) && isTimestamp(value.timestamp) && isSessionSaltHex(value.sessionSalt) && isDigest(value.integrity)) {
2798
2986
  return {
2987
+ schemaVersion: value.schemaVersion,
2988
+ protocolVersion: value.protocolVersion,
2799
2989
  registrationDigest: value.registrationDigest,
2800
2990
  capabilityFlags: [...value.capabilityFlags],
2801
2991
  state: value.state,
@@ -2817,8 +3007,8 @@ function parseEpochProgressionMarker(value) {
2817
3007
  }
2818
3008
  const marker = {
2819
3009
  kind: "control",
2820
- schemaVersion: RECEIPT_READBACK_SCHEMA_VERSION,
2821
- protocolVersion: RECEIPT_READBACK_PROTOCOL_VERSION,
3010
+ schemaVersion: base.schemaVersion,
3011
+ protocolVersion: base.protocolVersion,
2822
3012
  registrationDigest: base.registrationDigest,
2823
3013
  capabilityFlags: [...base.capabilityFlags],
2824
3014
  control: "progression",
@@ -2835,19 +3025,21 @@ function parseEpochProgressionMarker(value) {
2835
3025
  return progressionMarkerValidation(marker);
2836
3026
  }
2837
3027
  function parseUnitProgressionMarker(value) {
2838
- if (!hasExactKeys(value, UNIT_PROGRESSION_KEYS)) {
3028
+ const currentVersion = isCurrentVersionPair(value.schemaVersion, value.protocolVersion);
3029
+ if (!hasExactKeys(value, LEGACY_UNIT_PROGRESSION_KEYS, currentVersion ? ["pinnedOperationTargetIdentity"] : [])) {
2839
3030
  return Object.hasOwn(value, "epochId") && Object.hasOwn(value, "unitId") ? markerResult("forbidden-field") : markerResult("missing-internal-id");
2840
3031
  }
2841
3032
  const requiredOperations = canonicalRequiredOperations(value.requiredOperations);
2842
3033
  const resourceScopes = canonicalResourceScopes(value.resourceScopes);
3034
+ const pinnedOperationTargetIdentity = value.pinnedOperationTargetIdentity === undefined ? undefined : isDigest(value.pinnedOperationTargetIdentity) ? value.pinnedOperationTargetIdentity : undefined;
2843
3035
  const base = parseProgressionBase(value);
2844
- if (!base || !isInternalId(value.epochId) || !isDigest(value.epochDigest) || !isInternalId(value.unitId) || !isDigest(value.unitDigest) || !isFamily(value.family) || !requiredOperations || !resourceScopes) {
3036
+ if (!base || !isInternalId(value.epochId) || !isDigest(value.epochDigest) || !isInternalId(value.unitId) || !isDigest(value.unitDigest) || !isFamily(value.family) || !requiredOperations || !resourceScopes || value.pinnedOperationTargetIdentity !== undefined && pinnedOperationTargetIdentity === undefined) {
2845
3037
  return value.epochId === undefined || value.unitId === undefined ? markerResult("missing-internal-id") : markerResult("malformed");
2846
3038
  }
2847
3039
  const marker = {
2848
3040
  kind: "control",
2849
- schemaVersion: RECEIPT_READBACK_SCHEMA_VERSION,
2850
- protocolVersion: RECEIPT_READBACK_PROTOCOL_VERSION,
3041
+ schemaVersion: base.schemaVersion,
3042
+ protocolVersion: base.protocolVersion,
2851
3043
  registrationDigest: base.registrationDigest,
2852
3044
  capabilityFlags: [...base.capabilityFlags],
2853
3045
  control: "progression",
@@ -2859,6 +3051,7 @@ function parseUnitProgressionMarker(value) {
2859
3051
  family: value.family,
2860
3052
  requiredOperations: [...requiredOperations],
2861
3053
  resourceScopes: resourceScopes.map((scope) => ({ ...scope })),
3054
+ ...pinnedOperationTargetIdentity ? { pinnedOperationTargetIdentity } : {},
2862
3055
  state: base.state,
2863
3056
  transitionDigest: base.transitionDigest,
2864
3057
  timestamp: base.timestamp,
@@ -2877,10 +3070,11 @@ function validateReceiptMarker(input) {
2877
3070
  return parseMintMarker(input);
2878
3071
  if (input.kind !== "control")
2879
3072
  return markerResult("unknown-kind");
2880
- if (input.schemaVersion !== RECEIPT_READBACK_SCHEMA_VERSION)
2881
- return markerResult("unknown-schema");
2882
- if (input.protocolVersion !== RECEIPT_READBACK_PROTOCOL_VERSION)
3073
+ if (!isCurrentVersionPair(input.schemaVersion, input.protocolVersion) && !isLegacyVersionPair(input.schemaVersion, input.protocolVersion)) {
3074
+ if (input.schemaVersion !== RECEIPT_READBACK_SCHEMA_VERSION && input.schemaVersion !== LEGACY_RECEIPT_READBACK_SCHEMA_VERSION)
3075
+ return markerResult("unknown-schema");
2883
3076
  return markerResult("unknown-protocol");
3077
+ }
2884
3078
  if (input.control === "consume")
2885
3079
  return parseConsumeMarker(input);
2886
3080
  if (input.control === "progression")
@@ -3010,7 +3204,7 @@ function projectEpochMarker(source, input, context) {
3010
3204
  };
3011
3205
  }
3012
3206
  function projectUnitMarker(source, input, context) {
3013
- if (!isInternalId(input.epochId) || !isInternalId(input.unitId) || !isFamily(input.family) || normalizeRequiredOperations(input.requiredOperations) === undefined || normalizeResourceScopes(input.resourceScopes) === undefined) {
3207
+ if (!isInternalId(input.epochId) || !isInternalId(input.unitId) || !isFamily(input.family) || normalizeRequiredOperations(input.requiredOperations) === undefined || normalizeResourceScopes(input.resourceScopes) === undefined || input.pinnedOperationTargetIdentity !== undefined && !isDigest(input.pinnedOperationTargetIdentity)) {
3014
3208
  return;
3015
3209
  }
3016
3210
  const requiredOperations = normalizeRequiredOperations(input.requiredOperations);
@@ -3032,6 +3226,7 @@ function projectUnitMarker(source, input, context) {
3032
3226
  family: input.family,
3033
3227
  requiredOperations: [...requiredOperations],
3034
3228
  resourceScopes: resourceScopes.map((scope) => ({ ...scope })),
3229
+ ...input.pinnedOperationTargetIdentity ? { pinnedOperationTargetIdentity: input.pinnedOperationTargetIdentity } : {},
3035
3230
  state: input.state,
3036
3231
  transitionDigest: input.transitionDigest,
3037
3232
  timestamp: context.timestamp,
@@ -3107,6 +3302,7 @@ function cloneMarker(marker) {
3107
3302
  family: marker.family,
3108
3303
  requiredOperations: [...marker.requiredOperations],
3109
3304
  resourceScopes: marker.resourceScopes.map((scope) => ({ ...scope })),
3305
+ ...marker.pinnedOperationTargetIdentity ? { pinnedOperationTargetIdentity: marker.pinnedOperationTargetIdentity } : {},
3110
3306
  state: marker.state,
3111
3307
  transitionDigest: marker.transitionDigest,
3112
3308
  timestamp: marker.timestamp,
@@ -3173,6 +3369,9 @@ function serializeMarker(marker) {
3173
3369
  family: marker.family,
3174
3370
  requiredOperations: [...marker.requiredOperations],
3175
3371
  resourceScopes: marker.resourceScopes.map((scope) => ({ ...scope })),
3372
+ ...marker.schemaVersion === RECEIPT_READBACK_SCHEMA_VERSION ? {
3373
+ pinnedOperationTargetIdentity: marker.pinnedOperationTargetIdentity ?? null
3374
+ } : {},
3176
3375
  state: marker.state,
3177
3376
  transitionDigest: marker.transitionDigest,
3178
3377
  timestamp: marker.timestamp,
@@ -3196,6 +3395,12 @@ function sameEnvelope(first, second) {
3196
3395
  function foldMintMarker(marker, context) {
3197
3396
  if (marker.sessionSalt !== context.expectedSalt)
3198
3397
  return "salt-mismatch";
3398
+ if (marker.envelope.schemaVersion === 1 && context.expectation.operationTargetIdentity) {
3399
+ return "identity-digest-mismatch";
3400
+ }
3401
+ if (marker.envelope.schemaVersion === RECEIPT_READBACK_SCHEMA_VERSION && context.expectation.operationTargetIdentity && marker.envelope.canonical.operationTargetIdentity !== context.expectation.operationTargetIdentity) {
3402
+ return "identity-digest-mismatch";
3403
+ }
3199
3404
  const registrationMatches = marker.envelope.registrationDigest === context.expectation.registrationDigest && marker.envelope.canonical.registrationDigest === context.expectation.registrationDigest;
3200
3405
  if (!registrationMatches)
3201
3406
  return "cross-registration";
@@ -3261,6 +3466,7 @@ function progressionSnapshot(marker) {
3261
3466
  family: marker.family,
3262
3467
  requiredOperations: [...marker.requiredOperations],
3263
3468
  resourceScopes: marker.resourceScopes.map((scope) => ({ ...scope })),
3469
+ ...marker.pinnedOperationTargetIdentity ? { pinnedOperationTargetIdentity: marker.pinnedOperationTargetIdentity } : {},
3264
3470
  transitionDigest: marker.transitionDigest
3265
3471
  };
3266
3472
  }
@@ -3338,13 +3544,14 @@ function applyUnitStart(marker, epoch, context) {
3338
3544
  if (!sameUnitDeclaration(current, marker) && unitHasMintedEvidence(current, context)) {
3339
3545
  return "out-of-order";
3340
3546
  }
3341
- if (sameUnitDeclaration(current, marker) && current.transitionDigest !== marker.transitionDigest) {
3547
+ if (sameUnitDeclaration(current, marker) && current.transitionDigest !== marker.transitionDigest && current.pinnedOperationTargetIdentity === marker.pinnedOperationTargetIdentity) {
3342
3548
  return "conflicting-marker";
3343
3549
  }
3344
3550
  if (!unitDeclarationExtends(current, marker))
3345
3551
  return "conflicting-marker";
3346
- if (sameUnitDeclaration(current, marker))
3552
+ if (sameUnitDeclaration(current, marker) && current.pinnedOperationTargetIdentity === marker.pinnedOperationTargetIdentity) {
3347
3553
  return;
3554
+ }
3348
3555
  context.progression = { epoch, unit: snapshot };
3349
3556
  return;
3350
3557
  }
@@ -3388,7 +3595,10 @@ function unitDeclarationExtends(current, marker) {
3388
3595
  scope.operation,
3389
3596
  scope.resourceIdentity
3390
3597
  ]));
3391
- return [...current.resourceScopes].every((scope) => nextScopes.get(scope.operation) === scope.resourceIdentity);
3598
+ if (![...current.resourceScopes].every((scope) => nextScopes.get(scope.operation) === scope.resourceIdentity)) {
3599
+ return false;
3600
+ }
3601
+ return current.pinnedOperationTargetIdentity === undefined || current.pinnedOperationTargetIdentity === marker.pinnedOperationTargetIdentity;
3392
3602
  }
3393
3603
  function foldProgressionMarker(marker, context) {
3394
3604
  return marker.target === "epoch" ? applyEpochProgression(marker, context) : applyUnitProgression(marker, context);
@@ -3455,18 +3665,21 @@ function foldReceiptReadback(inputs, expectation) {
3455
3665
  }
3456
3666
  };
3457
3667
  }
3458
- function receiptReadbackExpectationFromMetadata(metadata2, sessionSalt) {
3668
+ function receiptReadbackExpectationFromMetadata(metadata2, sessionSalt, operationTargetIdentity) {
3459
3669
  return {
3460
3670
  registrationDigest: metadata2.registrationDigest,
3461
3671
  capabilityFlags: [...metadata2.capabilityFlags],
3462
3672
  sessionSalt: new Uint8Array(sessionSalt),
3463
- source: "runtime-verified"
3673
+ source: "runtime-verified",
3674
+ operationTargetIdentity
3464
3675
  };
3465
3676
  }
3466
3677
 
3467
3678
  // src/lib/receipt-ledger.ts
3468
- var RECEIPT_SCHEMA_VERSION = 1;
3469
- var RECEIPT_PROTOCOL_VERSION = 1;
3679
+ var RECEIPT_SCHEMA_VERSION = 2;
3680
+ var RECEIPT_PROTOCOL_VERSION = 2;
3681
+ var LEGACY_RECEIPT_SCHEMA_VERSION = 1;
3682
+ var LEGACY_RECEIPT_PROTOCOL_VERSION = 1;
3470
3683
  var OPERATION_SET = new Set([
3471
3684
  "implementation",
3472
3685
  "verification",
@@ -3514,6 +3727,9 @@ function isStringArray(value) {
3514
3727
  function isOperation2(value) {
3515
3728
  return typeof value === "string" && OPERATION_SET.has(value);
3516
3729
  }
3730
+ function isLocalOperation(operation) {
3731
+ return operation === "implementation" || operation === "verification" || operation === "commit";
3732
+ }
3517
3733
  function isReceiptReasonCode(value) {
3518
3734
  return typeof value === "string" && new Set([
3519
3735
  "already-consumed",
@@ -3573,7 +3789,7 @@ function cloneEnvelope2(envelope) {
3573
3789
  };
3574
3790
  }
3575
3791
  function envelopesEqual(first, second) {
3576
- return first.schemaVersion === second.schemaVersion && first.protocolVersion === second.protocolVersion && first.registrationDigest === second.registrationDigest && capabilityListsEqual(first.capabilityFlags, second.capabilityFlags) && first.compatibility === second.compatibility && first.canonical.receiptId === second.canonical.receiptId && first.canonical.registrationDigest === second.canonical.registrationDigest && first.canonical.callDigest === second.canonical.callDigest && first.canonical.epochDigest === second.canonical.epochDigest && first.canonical.unitDigest === second.canonical.unitDigest && first.canonical.workspaceDigest === second.canonical.workspaceDigest && first.canonical.repositoryDigest === second.canonical.repositoryDigest && first.canonical.worktreeDigest === second.canonical.worktreeDigest && first.canonical.resourceDigest === second.canonical.resourceDigest && first.canonical.operation === second.canonical.operation && first.canonical.result === second.canonical.result && first.canonical.source === second.canonical.source && first.canonical.consumption === second.canonical.consumption && first.canonical.timestamp === second.canonical.timestamp;
3792
+ return first.schemaVersion === second.schemaVersion && first.protocolVersion === second.protocolVersion && first.registrationDigest === second.registrationDigest && capabilityListsEqual(first.capabilityFlags, second.capabilityFlags) && first.compatibility === second.compatibility && first.canonical.receiptId === second.canonical.receiptId && first.canonical.registrationDigest === second.canonical.registrationDigest && first.canonical.callDigest === second.canonical.callDigest && first.canonical.epochDigest === second.canonical.epochDigest && first.canonical.unitDigest === second.canonical.unitDigest && first.canonical.workspaceDigest === second.canonical.workspaceDigest && first.canonical.repositoryDigest === second.canonical.repositoryDigest && first.canonical.worktreeDigest === second.canonical.worktreeDigest && first.canonical.operationTargetIdentity === second.canonical.operationTargetIdentity && first.canonical.resourceDigest === second.canonical.resourceDigest && first.canonical.operation === second.canonical.operation && first.canonical.result === second.canonical.result && first.canonical.source === second.canonical.source && first.canonical.consumption === second.canonical.consumption && first.canonical.timestamp === second.canonical.timestamp;
3577
3793
  }
3578
3794
  function cloneProgressionState(progression) {
3579
3795
  return {
@@ -3621,6 +3837,7 @@ function storedReceiptFromEnvelope(envelope) {
3621
3837
  workspaceDigest: envelope.canonical.workspaceDigest,
3622
3838
  repositoryDigest: envelope.canonical.repositoryDigest,
3623
3839
  worktreeDigest: envelope.canonical.worktreeDigest,
3840
+ operationTargetIdentity: envelope.canonical.operationTargetIdentity,
3624
3841
  resourceDigest: envelope.canonical.resourceDigest
3625
3842
  }
3626
3843
  };
@@ -3759,8 +3976,9 @@ function parseContext(value) {
3759
3976
  return;
3760
3977
  const repositoryIdentity = value.repositoryIdentity === undefined ? undefined : normalizedIdentity(value.repositoryIdentity);
3761
3978
  const worktreeIdentity = value.worktreeIdentity === undefined ? undefined : normalizedIdentity(value.worktreeIdentity);
3979
+ const operationTargetIdentity = value.operationTargetIdentity === undefined ? undefined : isDigest2(value.operationTargetIdentity) ? value.operationTargetIdentity : undefined;
3762
3980
  const resourceIdentity = value.resourceIdentity === undefined ? undefined : normalizedIdentity(value.resourceIdentity);
3763
- if (value.repositoryIdentity !== undefined && !repositoryIdentity || value.worktreeIdentity !== undefined && !worktreeIdentity || value.resourceIdentity !== undefined && !resourceIdentity) {
3981
+ if (value.repositoryIdentity !== undefined && !repositoryIdentity || value.worktreeIdentity !== undefined && !worktreeIdentity || value.operationTargetIdentity !== undefined && !isDigest2(operationTargetIdentity) || value.resourceIdentity !== undefined && !resourceIdentity) {
3764
3982
  return;
3765
3983
  }
3766
3984
  return {
@@ -3769,6 +3987,7 @@ function parseContext(value) {
3769
3987
  workspaceIdentity,
3770
3988
  repositoryIdentity,
3771
3989
  worktreeIdentity,
3990
+ operationTargetIdentity,
3772
3991
  resourceIdentity
3773
3992
  };
3774
3993
  }
@@ -3784,6 +4003,7 @@ function parseAfter(value) {
3784
4003
  workspaceIdentity: context.workspaceIdentity,
3785
4004
  repositoryIdentity: context.repositoryIdentity,
3786
4005
  worktreeIdentity: context.worktreeIdentity,
4006
+ operationTargetIdentity: context.operationTargetIdentity,
3787
4007
  resourceIdentity: context.resourceIdentity
3788
4008
  };
3789
4009
  }
@@ -3857,6 +4077,8 @@ function compareDigestedContexts(expected, actual) {
3857
4077
  return "workspace-mismatch";
3858
4078
  if (expected.worktreeDigest !== actual.worktreeDigest)
3859
4079
  return "workspace-mismatch";
4080
+ if (expected.operationTargetIdentity !== actual.operationTargetIdentity)
4081
+ return "workspace-mismatch";
3860
4082
  if (expected.resourceDigest !== actual.resourceDigest)
3861
4083
  return "workspace-mismatch";
3862
4084
  return;
@@ -3877,7 +4099,9 @@ function validateEnvelopeInput(input) {
3877
4099
  if (!("schemaVersion" in input) || !("protocolVersion" in input)) {
3878
4100
  return { compatibility: "unavailable", reasonCode: "incomplete-envelope" };
3879
4101
  }
3880
- if (input.schemaVersion !== RECEIPT_SCHEMA_VERSION || input.protocolVersion !== RECEIPT_PROTOCOL_VERSION) {
4102
+ const currentVersion = input.schemaVersion === RECEIPT_SCHEMA_VERSION && input.protocolVersion === RECEIPT_PROTOCOL_VERSION;
4103
+ const legacyVersion = input.schemaVersion === LEGACY_RECEIPT_SCHEMA_VERSION && input.protocolVersion === LEGACY_RECEIPT_PROTOCOL_VERSION;
4104
+ if (!currentVersion && !legacyVersion) {
3881
4105
  return { compatibility: "unavailable", reasonCode: "unknown-envelope" };
3882
4106
  }
3883
4107
  if (!("capabilityFlags" in input) || input.capabilityFlags === undefined) {
@@ -3897,16 +4121,19 @@ function isReceiptId2(value) {
3897
4121
  function parseEnvelope2(value) {
3898
4122
  if (!isRecord4(value))
3899
4123
  return;
3900
- if (value.schemaVersion !== RECEIPT_SCHEMA_VERSION || value.protocolVersion !== RECEIPT_PROTOCOL_VERSION || value.compatibility !== "compatible" || !isDigest2(value.registrationDigest) || !isBoundedCapabilityList(value.capabilityFlags) || !isRecord4(value.canonical)) {
4124
+ const currentVersion = value.schemaVersion === RECEIPT_SCHEMA_VERSION && value.protocolVersion === RECEIPT_PROTOCOL_VERSION;
4125
+ const legacyVersion = value.schemaVersion === LEGACY_RECEIPT_SCHEMA_VERSION && value.protocolVersion === LEGACY_RECEIPT_PROTOCOL_VERSION;
4126
+ if (!currentVersion && !legacyVersion || value.compatibility !== "compatible" || !isDigest2(value.registrationDigest) || !isBoundedCapabilityList(value.capabilityFlags) || !isRecord4(value.canonical)) {
3901
4127
  return;
3902
4128
  }
3903
4129
  const canonical = value.canonical;
3904
- if (!isReceiptId2(canonical.receiptId) || !isDigest2(canonical.registrationDigest) || !isDigest2(canonical.callDigest) || !isDigest2(canonical.epochDigest) || !isDigest2(canonical.unitDigest) || !isDigest2(canonical.workspaceDigest) || canonical.repositoryDigest !== undefined && !isDigest2(canonical.repositoryDigest) || canonical.worktreeDigest !== undefined && !isDigest2(canonical.worktreeDigest) || canonical.resourceDigest !== undefined && !isDigest2(canonical.resourceDigest) || !isOperation2(canonical.operation) || canonical.result !== "success" || canonical.source !== "runtime-verified" || canonical.consumption !== "available" && canonical.consumption !== "consumed" || typeof canonical.timestamp !== "number" || !Number.isFinite(canonical.timestamp)) {
4130
+ const operation = isOperation2(canonical.operation) ? canonical.operation : undefined;
4131
+ if (!isReceiptId2(canonical.receiptId) || !isDigest2(canonical.registrationDigest) || !isDigest2(canonical.callDigest) || !isDigest2(canonical.epochDigest) || !isDigest2(canonical.unitDigest) || !isDigest2(canonical.workspaceDigest) || canonical.repositoryDigest !== undefined && !isDigest2(canonical.repositoryDigest) || canonical.worktreeDigest !== undefined && !isDigest2(canonical.worktreeDigest) || canonical.operationTargetIdentity !== undefined && !isDigest2(canonical.operationTargetIdentity) || operation === undefined || currentVersion && isLocalOperation(operation) && !isDigest2(canonical.operationTargetIdentity) || currentVersion && !isLocalOperation(operation) && Object.hasOwn(canonical, "operationTargetIdentity") || canonical.resourceDigest !== undefined && !isDigest2(canonical.resourceDigest) || canonical.result !== "success" || canonical.source !== "runtime-verified" || canonical.consumption !== "available" && canonical.consumption !== "consumed" || typeof canonical.timestamp !== "number" || !Number.isFinite(canonical.timestamp)) {
3905
4132
  return;
3906
4133
  }
3907
4134
  return {
3908
- schemaVersion: RECEIPT_SCHEMA_VERSION,
3909
- protocolVersion: RECEIPT_PROTOCOL_VERSION,
4135
+ schemaVersion: currentVersion ? RECEIPT_SCHEMA_VERSION : LEGACY_RECEIPT_SCHEMA_VERSION,
4136
+ protocolVersion: currentVersion ? RECEIPT_PROTOCOL_VERSION : LEGACY_RECEIPT_PROTOCOL_VERSION,
3910
4137
  registrationDigest: value.registrationDigest,
3911
4138
  capabilityFlags: [...value.capabilityFlags],
3912
4139
  compatibility: "compatible",
@@ -3919,8 +4146,9 @@ function parseEnvelope2(value) {
3919
4146
  workspaceDigest: canonical.workspaceDigest,
3920
4147
  repositoryDigest: canonical.repositoryDigest,
3921
4148
  worktreeDigest: canonical.worktreeDigest,
4149
+ ...isLocalOperation(operation) ? { operationTargetIdentity: canonical.operationTargetIdentity } : {},
3922
4150
  resourceDigest: canonical.resourceDigest,
3923
- operation: canonical.operation,
4151
+ operation,
3924
4152
  result: "success",
3925
4153
  source: "runtime-verified",
3926
4154
  consumption: canonical.consumption,
@@ -3974,13 +4202,14 @@ function createReceiptLedger(options = {}) {
3974
4202
  }
3975
4203
  return digestReceiptIdentity(domain, normalized, sessionSaltBytes);
3976
4204
  }
3977
- function digestContext(context) {
4205
+ function digestContext(context, operation) {
3978
4206
  return {
3979
4207
  epochDigest: digestIdentity("epoch", context.epochId),
3980
4208
  unitDigest: digestIdentity("unit", context.unitId),
3981
4209
  workspaceDigest: digestIdentity("workspace", context.workspaceIdentity),
3982
4210
  repositoryDigest: context.repositoryIdentity ? digestIdentity("repository", context.repositoryIdentity) : undefined,
3983
4211
  worktreeDigest: context.worktreeIdentity ? digestIdentity("worktree", context.worktreeIdentity) : undefined,
4212
+ ...isLocalOperation(operation) ? { operationTargetIdentity: context.operationTargetIdentity } : {},
3984
4213
  resourceDigest: context.resourceIdentity ? digestIdentity("resource", context.resourceIdentity) : undefined
3985
4214
  };
3986
4215
  }
@@ -3994,7 +4223,7 @@ function createReceiptLedger(options = {}) {
3994
4223
  if (!parsed)
3995
4224
  return prepareInvalidObservationResult(input);
3996
4225
  const callDigest = digestIdentity("call", parsed.callId);
3997
- const digested = digestContext(parsed.context);
4226
+ const digested = digestContext(parsed.context, parsed.operation);
3998
4227
  const existing = lifecycleByCall.get(callDigest);
3999
4228
  if (existing) {
4000
4229
  return resolveExistingPrepare(existing, parsed.operation, digested);
@@ -4013,10 +4242,10 @@ function createReceiptLedger(options = {}) {
4013
4242
  if (!parsed)
4014
4243
  return invalidObservationResult(input);
4015
4244
  const callDigest = digestIdentity("call", parsed.callId);
4016
- const actualContext = digestContext(parsed.context);
4017
4245
  const matchingEntry = lifecycleByCall.get(callDigest);
4018
4246
  if (!matchingEntry)
4019
4247
  return invalidObservationResult(input);
4248
+ const actualContext = digestContext(parsed.context, matchingEntry.operation);
4020
4249
  const contextMismatch = compareDigestedContexts(matchingEntry.context, actualContext);
4021
4250
  if (contextMismatch) {
4022
4251
  sealEntry(matchingEntry);
@@ -4027,7 +4256,7 @@ function createReceiptLedger(options = {}) {
4027
4256
  sealEntry(matchingEntry);
4028
4257
  return { status: "rejected", reasonCode: entryRejection };
4029
4258
  }
4030
- const afterDigests = digestAfter(parsed.after);
4259
+ const afterDigests = digestAfter(parsed.after, matchingEntry.operation);
4031
4260
  const noOpReason = getNoOpReason(matchingEntry, afterDigests);
4032
4261
  if (noOpReason) {
4033
4262
  sealEntry(matchingEntry);
@@ -4070,13 +4299,14 @@ function createReceiptLedger(options = {}) {
4070
4299
  return "successful-no-op";
4071
4300
  return;
4072
4301
  }
4073
- function digestAfter(after) {
4302
+ function digestAfter(after, operation) {
4074
4303
  return {
4075
4304
  epochDigest: "",
4076
4305
  unitDigest: "",
4077
4306
  workspaceDigest: digestIdentity("workspace", after.workspaceIdentity),
4078
4307
  repositoryDigest: after.repositoryIdentity ? digestIdentity("repository", after.repositoryIdentity) : undefined,
4079
4308
  worktreeDigest: after.worktreeIdentity ? digestIdentity("worktree", after.worktreeIdentity) : undefined,
4309
+ ...isLocalOperation(operation) ? { operationTargetIdentity: after.operationTargetIdentity } : {},
4080
4310
  resourceDigest: after.resourceIdentity ? digestIdentity("resource", after.resourceIdentity) : undefined
4081
4311
  };
4082
4312
  }
@@ -4102,6 +4332,10 @@ function createReceiptLedger(options = {}) {
4102
4332
  return !entry.context.resourceDigest || !after.resourceDigest || after.resourceDigest === entry.context.resourceDigest ? "no-op-resource" : undefined;
4103
4333
  }
4104
4334
  function mintReceipt(entry, callDigest, after) {
4335
+ if (isLocalOperation(entry.operation) && !after.operationTargetIdentity) {
4336
+ entry.status = "abandoned";
4337
+ return { status: "rejected", reasonCode: "invalid-observation" };
4338
+ }
4105
4339
  const receiptId = randomBytes2(16).toString("hex");
4106
4340
  const canonical = {
4107
4341
  receiptId,
@@ -4112,6 +4346,7 @@ function createReceiptLedger(options = {}) {
4112
4346
  workspaceDigest: entry.context.workspaceDigest,
4113
4347
  repositoryDigest: after.repositoryDigest,
4114
4348
  worktreeDigest: after.worktreeDigest,
4349
+ ...isLocalOperation(entry.operation) ? { operationTargetIdentity: after.operationTargetIdentity } : {},
4115
4350
  resourceDigest: after.resourceDigest,
4116
4351
  operation: entry.operation,
4117
4352
  result: "success",
@@ -4136,6 +4371,7 @@ function createReceiptLedger(options = {}) {
4136
4371
  workspaceDigest: entry.context.workspaceDigest,
4137
4372
  repositoryDigest: after.repositoryDigest,
4138
4373
  worktreeDigest: after.worktreeDigest,
4374
+ operationTargetIdentity: after.operationTargetIdentity,
4139
4375
  resourceDigest: after.resourceDigest
4140
4376
  }
4141
4377
  });
@@ -4149,10 +4385,10 @@ function createReceiptLedger(options = {}) {
4149
4385
  if (!callId || !context)
4150
4386
  return { status: "rejected", reasonCode: "invalid-observation" };
4151
4387
  const callDigest = digestIdentity("call", callId);
4152
- const digested = digestContext(context);
4153
4388
  const matchingEntry = lifecycleByCall.get(callDigest);
4154
4389
  if (!matchingEntry)
4155
4390
  return { status: "rejected", reasonCode: "unknown-receipt" };
4391
+ const digested = digestContext(context, matchingEntry.operation);
4156
4392
  const contextMismatch = compareDigestedContexts(matchingEntry.context, digested);
4157
4393
  if (contextMismatch)
4158
4394
  return { status: "rejected", reasonCode: contextMismatch };
@@ -4171,7 +4407,7 @@ function createReceiptLedger(options = {}) {
4171
4407
  const stored = receipts.get(receiptId);
4172
4408
  if (!stored)
4173
4409
  return { status: "rejected", reasonCode: "unknown-receipt" };
4174
- const actual = digestContext(context);
4410
+ const actual = digestContext(context, stored.envelope.canonical.operation);
4175
4411
  const contextMismatch = compareDigestedContexts(stored.context, actual);
4176
4412
  if (contextMismatch)
4177
4413
  return { status: "rejected", reasonCode: contextMismatch };
@@ -4181,13 +4417,19 @@ function createReceiptLedger(options = {}) {
4181
4417
  stored.envelope.canonical.consumption = "consumed";
4182
4418
  return { status: "consumed", receipt: cloneEnvelope2(stored.envelope) };
4183
4419
  }
4184
- function recoverReceipt(input) {
4420
+ function recoverReceipt(input, operationTargetIdentity) {
4185
4421
  const markerResult2 = getMintMarker(input);
4186
4422
  if (markerResult2.status !== "valid")
4187
4423
  return markerResult2;
4188
4424
  const marker = markerResult2.marker;
4189
4425
  if (marker.sessionSalt !== sessionSalt)
4190
4426
  return { status: "rejected", category: "salt-mismatch" };
4427
+ if (marker.envelope.schemaVersion === 1 && operationTargetIdentity !== undefined) {
4428
+ return { status: "rejected", category: "identity-digest-mismatch" };
4429
+ }
4430
+ if (marker.envelope.schemaVersion === RECEIPT_SCHEMA_VERSION && operationTargetIdentity !== undefined && marker.envelope.canonical.operationTargetIdentity !== operationTargetIdentity) {
4431
+ return { status: "rejected", category: "identity-digest-mismatch" };
4432
+ }
4191
4433
  const envelopeValidation = validateEnvelope(marker.envelope);
4192
4434
  const envelopeFailure = recoveryEnvelopeCategory(envelopeValidation);
4193
4435
  if (envelopeFailure) {
@@ -4212,8 +4454,8 @@ function createReceiptLedger(options = {}) {
4212
4454
  receipts.set(receiptId, storedReceiptFromEnvelope(envelope));
4213
4455
  return { status: "recovered", receipt: cloneEnvelope2(envelope) };
4214
4456
  }
4215
- function recoverReadback(input) {
4216
- const folded = foldForLedgerRecovery(input, receiptReadbackExpectationFromMetadata(metadata2, sessionSaltBytes));
4457
+ function recoverReadback(input, operationTargetIdentity) {
4458
+ const folded = foldForLedgerRecovery(input, receiptReadbackExpectationFromMetadata(metadata2, sessionSaltBytes, operationTargetIdentity));
4217
4459
  if (folded.status !== "reconstructed") {
4218
4460
  return { status: "rejected", category: folded.category };
4219
4461
  }
@@ -7685,6 +7927,7 @@ function parseOperationContext(value) {
7685
7927
  "workspaceIdentity",
7686
7928
  "repositoryIdentity",
7687
7929
  "worktreeIdentity",
7930
+ "operationTargetIdentity",
7688
7931
  "resourceIdentity",
7689
7932
  "resourceRevisionIdentity"
7690
7933
  ]))) {
@@ -7708,14 +7951,16 @@ function hasRequiredOperationIdentities(value) {
7708
7951
  function parseOperationStateFields(value) {
7709
7952
  const repositoryIdentity = parseOptionalDigestIdentity(value.repositoryIdentity);
7710
7953
  const worktreeIdentity = parseOptionalDigestIdentity(value.worktreeIdentity);
7954
+ const operationTargetIdentity = parseOptionalDigestIdentity(value.operationTargetIdentity);
7711
7955
  const resourceIdentity = parseOptionalDigestIdentity(value.resourceIdentity);
7712
7956
  const resourceRevisionIdentity = parseOptionalDigestIdentity(value.resourceRevisionIdentity);
7713
- if (!validOptionalIdentity(value.repositoryIdentity, repositoryIdentity) || !validOptionalIdentity(value.worktreeIdentity, worktreeIdentity) || !validOptionalIdentity(value.resourceIdentity, resourceIdentity) || !validOptionalIdentity(value.resourceRevisionIdentity, resourceRevisionIdentity)) {
7957
+ if (!validOptionalIdentity(value.repositoryIdentity, repositoryIdentity) || !validOptionalIdentity(value.worktreeIdentity, worktreeIdentity) || !validOptionalIdentity(value.operationTargetIdentity, operationTargetIdentity) || !validOptionalIdentity(value.resourceIdentity, resourceIdentity) || !validOptionalIdentity(value.resourceRevisionIdentity, resourceRevisionIdentity)) {
7714
7958
  return;
7715
7959
  }
7716
7960
  return {
7717
7961
  repositoryIdentity,
7718
7962
  worktreeIdentity,
7963
+ operationTargetIdentity,
7719
7964
  resourceIdentity,
7720
7965
  resourceRevisionIdentity
7721
7966
  };
@@ -7759,6 +8004,7 @@ var OPERATION_AFTER_FIELDS = new Set([
7759
8004
  "workspaceIdentity",
7760
8005
  "repositoryIdentity",
7761
8006
  "worktreeIdentity",
8007
+ "operationTargetIdentity",
7762
8008
  "resourceIdentity",
7763
8009
  "resourceRevisionIdentity",
7764
8010
  "pullRequest",
@@ -8293,6 +8539,7 @@ var RECOVERY_UNIT_KEYS = new Set([
8293
8539
  "family",
8294
8540
  "requiredOperations",
8295
8541
  "resourceScopes",
8542
+ "pinnedOperationTargetIdentity",
8296
8543
  "transitionDigest"
8297
8544
  ]);
8298
8545
  var RECOVERY_STATE_KEYS = new Set([
@@ -8454,7 +8701,8 @@ function parseRecoveryUnit(value) {
8454
8701
  return;
8455
8702
  const requiredOperations = parseRecoveryOperations(value.requiredOperations);
8456
8703
  const resourceScopes = parseRecoveryResourceScopes(value.resourceScopes);
8457
- if (value.target !== "unit" || value.state !== "started" && value.state !== "completed" || !isRecoveryId(value.epochId) || !isRecoveryDigest(value.epochDigest) || !isRecoveryId(value.unitId) || !isRecoveryDigest(value.unitDigest) || !isFamily2(value.family) || !requiredOperations || !resourceScopes || !isRecoveryDigest(value.transitionDigest)) {
8704
+ const pinnedOperationTargetIdentity = value.pinnedOperationTargetIdentity === undefined ? undefined : isRecoveryDigest(value.pinnedOperationTargetIdentity) ? value.pinnedOperationTargetIdentity : undefined;
8705
+ if (value.target !== "unit" || value.state !== "started" && value.state !== "completed" || !isRecoveryId(value.epochId) || !isRecoveryDigest(value.epochDigest) || !isRecoveryId(value.unitId) || !isRecoveryDigest(value.unitDigest) || !isFamily2(value.family) || !requiredOperations || !resourceScopes || value.pinnedOperationTargetIdentity !== undefined && pinnedOperationTargetIdentity === undefined || !isRecoveryDigest(value.transitionDigest)) {
8458
8706
  return;
8459
8707
  }
8460
8708
  return {
@@ -8467,6 +8715,7 @@ function parseRecoveryUnit(value) {
8467
8715
  family: value.family,
8468
8716
  requiredOperations,
8469
8717
  resourceScopes,
8718
+ ...pinnedOperationTargetIdentity ? { pinnedOperationTargetIdentity } : {},
8470
8719
  transitionDigest: value.transitionDigest
8471
8720
  };
8472
8721
  }
@@ -8518,7 +8767,8 @@ function cloneUnit(unit) {
8518
8767
  resourceScopes: Object.freeze([...unit.resourceScopes].map(([operation, resourceIdentity]) => ({
8519
8768
  operation,
8520
8769
  resourceIdentity
8521
- })))
8770
+ }))),
8771
+ ...unit.pinnedOperationTargetIdentity ? { pinnedOperationTargetIdentity: unit.pinnedOperationTargetIdentity } : {}
8522
8772
  });
8523
8773
  }
8524
8774
  function familyForSkill(skill) {
@@ -8606,6 +8856,7 @@ function parseReadback(input) {
8606
8856
  "workspaceIdentity",
8607
8857
  "repositoryIdentity",
8608
8858
  "worktreeIdentity",
8859
+ "operationTargetIdentity",
8609
8860
  "resourceIdentity",
8610
8861
  "resourceRevisionIdentity",
8611
8862
  "pullRequest",
@@ -8618,6 +8869,7 @@ function parseReadback(input) {
8618
8869
  workspaceIdentity: input.workspaceIdentity,
8619
8870
  repositoryIdentity: input.repositoryIdentity,
8620
8871
  worktreeIdentity: input.worktreeIdentity,
8872
+ operationTargetIdentity: input.operationTargetIdentity,
8621
8873
  resourceIdentity: input.resourceIdentity,
8622
8874
  resourceRevisionIdentity: input.resourceRevisionIdentity,
8623
8875
  pullRequest: input.pullRequest,
@@ -8631,6 +8883,7 @@ function parseReadback(input) {
8631
8883
  workspaceIdentity: after.workspaceIdentity,
8632
8884
  repositoryIdentity: after.repositoryIdentity,
8633
8885
  worktreeIdentity: after.worktreeIdentity,
8886
+ operationTargetIdentity: after.operationTargetIdentity,
8634
8887
  resourceIdentity: after.resourceIdentity,
8635
8888
  resourceRevisionIdentity: after.resourceRevisionIdentity,
8636
8889
  pullRequest: after.pullRequest,
@@ -8644,9 +8897,10 @@ function parseLegacyReadbackAfter(input) {
8644
8897
  }
8645
8898
  const repositoryIdentity = parseOptionalReadbackIdentity(input.repositoryIdentity);
8646
8899
  const worktreeIdentity = parseOptionalReadbackIdentity(input.worktreeIdentity);
8900
+ const operationTargetIdentity = parseOptionalReadbackIdentity(input.operationTargetIdentity);
8647
8901
  const resourceIdentity = parseOptionalReadbackIdentity(input.resourceIdentity);
8648
8902
  const resourceRevisionIdentity = parseOptionalReadbackIdentity(input.resourceRevisionIdentity);
8649
- if (!isOptionalReadbackIdentity(input.repositoryIdentity, repositoryIdentity) || !isOptionalReadbackIdentity(input.worktreeIdentity, worktreeIdentity) || !isOptionalReadbackIdentity(input.resourceIdentity, resourceIdentity) || !isOptionalReadbackIdentity(input.resourceRevisionIdentity, resourceRevisionIdentity)) {
8903
+ if (!isOptionalReadbackIdentity(input.repositoryIdentity, repositoryIdentity) || !isOptionalReadbackIdentity(input.worktreeIdentity, worktreeIdentity) || !isOptionalReadbackIdentity(input.operationTargetIdentity, operationTargetIdentity) || !isOptionalReadbackIdentity(input.resourceIdentity, resourceIdentity) || !isOptionalReadbackIdentity(input.resourceRevisionIdentity, resourceRevisionIdentity)) {
8650
8904
  return;
8651
8905
  }
8652
8906
  const pullRequest = parseLegacyPullRequest(input.pullRequest);
@@ -8660,6 +8914,7 @@ function parseLegacyReadbackAfter(input) {
8660
8914
  workspaceIdentity: input.workspaceIdentity,
8661
8915
  ...repositoryIdentity ? { repositoryIdentity } : {},
8662
8916
  ...worktreeIdentity ? { worktreeIdentity } : {},
8917
+ ...operationTargetIdentity ? { operationTargetIdentity } : {},
8663
8918
  ...resourceIdentity ? { resourceIdentity } : {},
8664
8919
  ...resourceRevisionIdentity ? { resourceRevisionIdentity } : {},
8665
8920
  ...pullRequest ? { pullRequest } : {},
@@ -8699,10 +8954,12 @@ function ledgerResourceIdentity(operation, resourceIdentity, resourceRevisionIde
8699
8954
  function toLedgerContext(operation, context) {
8700
8955
  const {
8701
8956
  resourceRevisionIdentity: _resourceRevisionIdentity,
8957
+ operationTargetIdentity: _operationTargetIdentity,
8702
8958
  ...ledgerContext
8703
8959
  } = context;
8704
8960
  return {
8705
8961
  ...ledgerContext,
8962
+ ...isLocalOperation(operation) ? { operationTargetIdentity: context.operationTargetIdentity } : {},
8706
8963
  resourceIdentity: ledgerResourceIdentity(operation, context.resourceIdentity, context.resourceRevisionIdentity)
8707
8964
  };
8708
8965
  }
@@ -8712,10 +8969,12 @@ function toLedgerAfter(operation, after) {
8712
8969
  pullRequest: _pullRequest,
8713
8970
  checkState: _checkState,
8714
8971
  reviewDecision: _reviewDecision,
8972
+ operationTargetIdentity: _operationTargetIdentity,
8715
8973
  ...ledgerAfter
8716
8974
  } = after;
8717
8975
  return {
8718
8976
  ...ledgerAfter,
8977
+ ...isLocalOperation(operation) ? { operationTargetIdentity: after.operationTargetIdentity } : {},
8719
8978
  resourceIdentity: ledgerResourceIdentity(operation, after.resourceIdentity, after.resourceRevisionIdentity)
8720
8979
  };
8721
8980
  }
@@ -8769,12 +9028,13 @@ function parseU4Readback(input) {
8769
9028
  "workspaceIdentity",
8770
9029
  "repositoryIdentity",
8771
9030
  "worktreeIdentity",
9031
+ "operationTargetIdentity",
8772
9032
  "resourceIdentity",
8773
9033
  "resourceRevisionIdentity",
8774
9034
  "pullRequest",
8775
9035
  "checkState",
8776
9036
  "reviewDecision"
8777
- ])) || input.operation !== undefined && !isOperation4(input.operation)) {
9037
+ ])) || input.operation !== undefined && !isOperation4(input.operation) || input.operation !== undefined && isOperation4(input.operation) && !isLocalOperation(input.operation) && Object.hasOwn(input, "operationTargetIdentity")) {
8778
9038
  return;
8779
9039
  }
8780
9040
  const { operation: _operation, ...afterInput } = input;
@@ -8786,6 +9046,7 @@ function parseU4Readback(input) {
8786
9046
  workspaceIdentity: after.workspaceIdentity,
8787
9047
  repositoryIdentity: after.repositoryIdentity,
8788
9048
  worktreeIdentity: after.worktreeIdentity,
9049
+ operationTargetIdentity: after.operationTargetIdentity,
8789
9050
  resourceIdentity: after.resourceIdentity,
8790
9051
  resourceRevisionIdentity: after.resourceRevisionIdentity,
8791
9052
  pullRequest: after.pullRequest,
@@ -8936,11 +9197,21 @@ function createWorkflowGuard(options) {
8936
9197
  return "epoch-mismatch";
8937
9198
  if (input.context.unitId !== unit.unitId)
8938
9199
  return "unit-mismatch";
8939
- return currentIdentityReason(input.context) ?? resourceBeforeReason(input, unit);
9200
+ return currentIdentityReason(input.context) ?? operationTargetReason(input, unit) ?? resourceBeforeReason(input, unit);
9201
+ }
9202
+ function operationTargetReason(input, unit) {
9203
+ if (!isLocalOperation(input.operation))
9204
+ return;
9205
+ if (input.after && input.after.operationTargetIdentity !== input.context.operationTargetIdentity) {
9206
+ return "operation-target-mismatch";
9207
+ }
9208
+ return unit.pinnedOperationTargetIdentity !== undefined && input.context.operationTargetIdentity !== unit.pinnedOperationTargetIdentity ? "operation-target-mismatch" : undefined;
8940
9209
  }
8941
9210
  function currentIdentityReason(context) {
8942
9211
  if (context.workspaceIdentity !== currentWorkspaceIdentity)
8943
9212
  return "workspace-mismatch";
9213
+ if (context.operationTargetIdentity !== undefined)
9214
+ return;
8944
9215
  if (context.repositoryIdentity !== currentRepositoryIdentity)
8945
9216
  return "receipt-mismatch";
8946
9217
  return context.worktreeIdentity !== currentWorktreeIdentity ? "receipt-mismatch" : undefined;
@@ -8971,7 +9242,7 @@ function createWorkflowGuard(options) {
8971
9242
  if (input.after.workspaceIdentity !== input.context.workspaceIdentity) {
8972
9243
  return "workspace-mismatch";
8973
9244
  }
8974
- return resourceAfterReason(input, unit);
9245
+ return operationTargetReason(input, unit) ?? resourceAfterReason(input, unit);
8975
9246
  }
8976
9247
  function resourceAfterReason(input, unit) {
8977
9248
  if (!operationUsesResource(input.operation))
@@ -9243,6 +9514,9 @@ function createWorkflowGuard(options) {
9243
9514
  if (envelope.canonical.unitDigest !== options.ledger.digestIdentity("unit", unit.unitId)) {
9244
9515
  return "receipt-mismatch";
9245
9516
  }
9517
+ if (isLocalOperation(envelope.canonical.operation) && unit.pinnedOperationTargetIdentity !== undefined && envelope.canonical.operationTargetIdentity !== unit.pinnedOperationTargetIdentity) {
9518
+ return "operation-target-mismatch";
9519
+ }
9246
9520
  const historicalImplementation = envelope.canonical.operation === "implementation" && unit.evidence.get("implementation")?.canonical.receiptId === envelope.canonical.receiptId;
9247
9521
  if (!historicalImplementation) {
9248
9522
  if (envelope.canonical.workspaceDigest !== options.ledger.digestIdentity("workspace", currentWorkspaceIdentity)) {
@@ -9353,7 +9627,7 @@ function createWorkflowGuard(options) {
9353
9627
  return { satisfied, missing };
9354
9628
  }
9355
9629
  function issueKind(reasonCode) {
9356
- return reasonCode === "incompatible-receipt" || reasonCode === "guard-unavailable" ? "unavailable" : "rejected";
9630
+ return reasonCode === "incompatible-receipt" || reasonCode === "guard-unavailable" || reasonCode === "operation-target-mismatch" ? "unavailable" : "rejected";
9357
9631
  }
9358
9632
  function makeStatus(state, reasonCode, repair, satisfied = [], missing = []) {
9359
9633
  const epochSnapshot = epoch ? cloneEpoch(epoch) : null;
@@ -9487,6 +9761,7 @@ function createWorkflowGuard(options) {
9487
9761
  workspaceIdentity: currentWorkspaceIdentity,
9488
9762
  repositoryIdentity: currentRepositoryIdentity,
9489
9763
  worktreeIdentity: currentWorktreeIdentity,
9764
+ operationTargetIdentity: envelope.canonical.operationTargetIdentity,
9490
9765
  resourceIdentity: ledgerResourceIdentity(operation, currentResource(unit, operation), currentResourceRevisionIdentities.get(operation))
9491
9766
  };
9492
9767
  }
@@ -9556,6 +9831,9 @@ function createWorkflowGuard(options) {
9556
9831
  return { status: "accepted", operation };
9557
9832
  }
9558
9833
  unit.evidence.set(operation, read.envelope);
9834
+ if (isLocalOperation(operation) && unit.pinnedOperationTargetIdentity === undefined && read.envelope.canonical.operationTargetIdentity !== undefined) {
9835
+ unit.pinnedOperationTargetIdentity = read.envelope.canonical.operationTargetIdentity;
9836
+ }
9559
9837
  clearIssue(unit, operation);
9560
9838
  globalIssue = undefined;
9561
9839
  return { status: "accepted", operation };
@@ -9566,6 +9844,7 @@ function createWorkflowGuard(options) {
9566
9844
  workspaceIdentity: after.workspaceIdentity,
9567
9845
  repositoryIdentity: after.repositoryIdentity,
9568
9846
  worktreeIdentity: after.worktreeIdentity,
9847
+ ...isLocalOperation(operation) ? { operationTargetIdentity: after.operationTargetIdentity } : {},
9569
9848
  resourceIdentity: after.resourceIdentity,
9570
9849
  resourceRevisionIdentity: after.resourceRevisionIdentity,
9571
9850
  pullRequest: after.pullRequest,
@@ -9887,6 +10166,7 @@ function createWorkflowGuard(options) {
9887
10166
  workspaceIdentity: parsed.after.workspaceIdentity,
9888
10167
  repositoryIdentity: parsed.after.repositoryIdentity,
9889
10168
  worktreeIdentity: parsed.after.worktreeIdentity,
10169
+ operationTargetIdentity: parsed.after.operationTargetIdentity,
9890
10170
  resourceIdentity: parsed.after.resourceIdentity,
9891
10171
  resourceRevisionIdentity: parsed.after.resourceRevisionIdentity
9892
10172
  }));
@@ -9910,6 +10190,9 @@ function createWorkflowGuard(options) {
9910
10190
  if (!global?.repositoryIdentity || !global.worktreeIdentity) {
9911
10191
  return "invalid-receipt";
9912
10192
  }
10193
+ if (unit.pinnedOperationTargetIdentity !== undefined && global.operationTargetIdentity !== unit.pinnedOperationTargetIdentity) {
10194
+ return "operation-target-mismatch";
10195
+ }
9913
10196
  const required = RESOURCE_FINAL_READBACK_OPERATIONS.filter((operation) => unit.evidence.has(operation));
9914
10197
  for (const operation of required) {
9915
10198
  const readback = readbacks.find((item) => item.operation === operation);
@@ -10029,6 +10312,16 @@ function createWorkflowGuard(options) {
10029
10312
  }
10030
10313
  return;
10031
10314
  }
10315
+ function recoveryTargetPinReason(unit, matching) {
10316
+ if (!unit)
10317
+ return;
10318
+ const localTargets = matching.filter((envelope) => isLocalOperation(envelope.canonical.operation)).map((envelope) => envelope.canonical.operationTargetIdentity);
10319
+ if (unit.pinnedOperationTargetIdentity !== undefined) {
10320
+ return localTargets.some((target) => target !== unit.pinnedOperationTargetIdentity) ? "operation-target-mismatch" : undefined;
10321
+ }
10322
+ const distinctTargets = new Set(localTargets.filter((target) => target !== undefined));
10323
+ return distinctTargets.size > 1 ? "operation-target-mismatch" : undefined;
10324
+ }
10032
10325
  function resourceScopeMatches(operation, persisted, trusted) {
10033
10326
  return persisted === trusted || persisted === options.ledger.digestIdentity("resource", trusted) || persisted === options.ledger.digestIdentity("resource", ledgerResourceIdentity(operation, trusted, currentResourceRevisionIdentities.get(operation)) ?? trusted);
10034
10327
  }
@@ -10058,6 +10351,7 @@ function createWorkflowGuard(options) {
10058
10351
  workspaceIdentity: currentWorkspaceIdentity,
10059
10352
  repositoryIdentity: currentRepositoryIdentity,
10060
10353
  worktreeIdentity: currentWorktreeIdentity,
10354
+ operationTargetIdentity: envelope.canonical.operationTargetIdentity,
10061
10355
  resourceIdentity: resourceScopes.get(envelope.canonical.operation)
10062
10356
  };
10063
10357
  }
@@ -10151,6 +10445,9 @@ function createWorkflowGuard(options) {
10151
10445
  const boundaryFailure = receiptSet.matching.map(recoveryBoundaryReason).find((reason) => reason !== undefined);
10152
10446
  if (boundaryFailure)
10153
10447
  return { status: "rejected", reasonCode: boundaryFailure };
10448
+ const targetPinFailure = recoveryTargetPinReason(state.progression.unit, receiptSet.matching);
10449
+ if (targetPinFailure)
10450
+ return { status: "rejected", reasonCode: targetPinFailure };
10154
10451
  const completionFailure = recoveryCompletionReason(state.progression, receiptSet.matching);
10155
10452
  if (completionFailure)
10156
10453
  return { status: "rejected", reasonCode: completionFailure };
@@ -10160,12 +10457,16 @@ function createWorkflowGuard(options) {
10160
10457
  const snapshot = progression.unit;
10161
10458
  if (!snapshot)
10162
10459
  return;
10460
+ const inferredOperationTargetIdentity = snapshot.pinnedOperationTargetIdentity ?? matching.find((envelope) => isLocalOperation(envelope.canonical.operation))?.canonical.operationTargetIdentity;
10163
10461
  const unit = {
10164
10462
  unitId: snapshot.unitId,
10165
10463
  status: snapshot.state === "completed" ? "completed" : "active",
10166
10464
  requiredOperations: Object.freeze([...snapshot.requiredOperations]),
10167
10465
  declaredResourceOperations: Object.freeze(snapshot.resourceScopes.map((scope) => scope.operation)),
10168
10466
  resourceScopes: new Map(resourceScopes),
10467
+ ...inferredOperationTargetIdentity ? {
10468
+ pinnedOperationTargetIdentity: inferredOperationTargetIdentity
10469
+ } : {},
10169
10470
  evidence: new Map,
10170
10471
  issues: new Map,
10171
10472
  staleReceiptIds: new Set,
@@ -10432,7 +10733,7 @@ function createWorkflowGuard(options) {
10432
10733
  // src/lib/opencode-workflow-guard.ts
10433
10734
  var MARKER_OPEN = "<SYSTEMATIC_WORKFLOW_GUARD>";
10434
10735
  var MARKER_CLOSE = "</SYSTEMATIC_WORKFLOW_GUARD>";
10435
- var MARKER_PROTOCOL_VERSION = 1;
10736
+ var MARKER_PROTOCOL_VERSION = 2;
10436
10737
  var MAX_MARKER_LENGTH = 4096;
10437
10738
  var MAX_MARKER_SOURCES = 8;
10438
10739
  var MAX_CALL_ID_LENGTH = 256;
@@ -10494,6 +10795,7 @@ var REASON_CODES = new Set([
10494
10795
  "no-active-unit",
10495
10796
  "no-op-operation",
10496
10797
  "operation-not-required",
10798
+ "operation-target-mismatch",
10497
10799
  "receipt-mismatch",
10498
10800
  "rejected-operation",
10499
10801
  "resource-mismatch",
@@ -10637,6 +10939,216 @@ function digestCall(ledger, callID) {
10637
10939
  function isLocalOperationTool(tool) {
10638
10940
  return tool === "write" || tool === "edit" || tool === "apply_patch" || tool === "bash";
10639
10941
  }
10942
+ function unavailableOperationTarget() {
10943
+ return { status: "unavailable", reasonCode: "target-unavailable" };
10944
+ }
10945
+ function canonicalExistingPath(filePath, realPath) {
10946
+ try {
10947
+ return realPath(filePath);
10948
+ } catch {
10949
+ return;
10950
+ }
10951
+ }
10952
+ function registeredWorktreeIdentity(validation) {
10953
+ return validation.status === "ok" ? `${validation.gitDir}
10954
+ ${validation.commonDir}` : undefined;
10955
+ }
10956
+ function canonicalFileTarget(filePath, realPath) {
10957
+ const existing = canonicalExistingPath(filePath, realPath);
10958
+ if (existing)
10959
+ return existing;
10960
+ const parent = canonicalExistingPath(path9.dirname(filePath), realPath);
10961
+ const basename = path9.basename(filePath);
10962
+ return parent && basename && basename !== "." && basename !== ".." ? path9.join(parent, basename) : undefined;
10963
+ }
10964
+ function pathWithinOrEqual(root, candidate) {
10965
+ const relative = path9.relative(root, candidate);
10966
+ return relative === "" || relative !== ".." && !relative.startsWith(`..${path9.sep}`) && !path9.isAbsolute(relative);
10967
+ }
10968
+ function targetIsGitAdminStorage(targetPath, validation) {
10969
+ return pathWithinOrEqual(path9.join(validation.targetRoot, ".git"), targetPath) || pathWithinOrEqual(validation.gitDir, targetPath) || pathWithinOrEqual(validation.commonDir, targetPath);
10970
+ }
10971
+ function targetResultFromValidation(candidatePath, validation) {
10972
+ if (validation.status === "error")
10973
+ return unavailableOperationTarget();
10974
+ if (!pathWithinOrEqual(validation.targetRoot, candidatePath)) {
10975
+ return unavailableOperationTarget();
10976
+ }
10977
+ if (targetIsGitAdminStorage(candidatePath, validation)) {
10978
+ return unavailableOperationTarget();
10979
+ }
10980
+ return { status: "available", targetRoot: validation.targetRoot };
10981
+ }
10982
+ function trustedParentTarget(parentTargetRoot, candidatePath) {
10983
+ if (!pathWithinOrEqual(parentTargetRoot, candidatePath))
10984
+ return;
10985
+ if (pathWithinOrEqual(path9.join(parentTargetRoot, ".git"), candidatePath)) {
10986
+ return unavailableOperationTarget();
10987
+ }
10988
+ return { status: "available", targetRoot: parentTargetRoot };
10989
+ }
10990
+ function validationForCandidate(candidatePath, options, targetPath = candidatePath) {
10991
+ try {
10992
+ return targetResultFromValidation(targetPath, options.validateRegisteredWorktree(candidatePath));
10993
+ } catch {
10994
+ return unavailableOperationTarget();
10995
+ }
10996
+ }
10997
+ function deriveFileTarget(rawPath, baseDirectory, parentTargetRoot, options, realPath, allowParentFastPath = true) {
10998
+ const resolvedPath = path9.resolve(baseDirectory, rawPath);
10999
+ const canonicalPath2 = canonicalFileTarget(resolvedPath, realPath);
11000
+ if (!canonicalPath2)
11001
+ return unavailableOperationTarget();
11002
+ const parentResult = allowParentFastPath && !path9.isAbsolute(rawPath) ? trustedParentTarget(parentTargetRoot, canonicalPath2) : undefined;
11003
+ if (parentResult)
11004
+ return parentResult;
11005
+ return validationForCandidate(path9.dirname(canonicalPath2), options, canonicalPath2);
11006
+ }
11007
+ function sharedTargetRoot(targets, extraRoots = []) {
11008
+ const roots = [...extraRoots];
11009
+ for (const target of targets) {
11010
+ if (target.status === "unavailable")
11011
+ return;
11012
+ roots.push(target.targetRoot);
11013
+ }
11014
+ const targetRoot = roots[0];
11015
+ return targetRoot && roots.every((root) => root === targetRoot) ? targetRoot : undefined;
11016
+ }
11017
+ function deriveDirectoryTarget(rawPath, baseDirectory, parentTargetRoot, options, realPath) {
11018
+ const resolvedPath = canonicalExistingPath(path9.resolve(baseDirectory, rawPath), realPath);
11019
+ if (!resolvedPath)
11020
+ return { status: "unavailable" };
11021
+ const parentResult = path9.isAbsolute(rawPath) ? undefined : trustedParentTarget(parentTargetRoot, resolvedPath);
11022
+ if (parentResult?.status === "unavailable") {
11023
+ return { status: "unavailable" };
11024
+ }
11025
+ if (parentResult) {
11026
+ return {
11027
+ status: "available",
11028
+ target: { targetRoot: parentResult.targetRoot, resolvedPath }
11029
+ };
11030
+ }
11031
+ const validated = validationForCandidate(resolvedPath, options);
11032
+ return validated.status === "available" ? {
11033
+ status: "available",
11034
+ target: { targetRoot: validated.targetRoot, resolvedPath }
11035
+ } : { status: "unavailable" };
11036
+ }
11037
+ var PATCH_FILE_PREFIXES = [
11038
+ "*** Add File:",
11039
+ "*** Delete File:",
11040
+ "*** Update File:",
11041
+ "*** Move to:"
11042
+ ];
11043
+ function patchTextFileTargets(patchText) {
11044
+ const paths = [];
11045
+ for (const line of patchText.split(/\r?\n/)) {
11046
+ const prefix = PATCH_FILE_PREFIXES.find((candidate) => line.startsWith(candidate));
11047
+ if (!prefix)
11048
+ continue;
11049
+ const filePath = line.slice(prefix.length).trim();
11050
+ if (!filePath)
11051
+ return;
11052
+ paths.push(filePath);
11053
+ }
11054
+ return paths;
11055
+ }
11056
+ function hunkFileTargets(value) {
11057
+ if (!Array.isArray(value))
11058
+ return;
11059
+ const paths = [];
11060
+ for (const hunk of value) {
11061
+ if (!isRecord7(hunk) || typeof hunk.path !== "string" || !hunk.path) {
11062
+ return;
11063
+ }
11064
+ paths.push(hunk.path);
11065
+ if (hunk.move_path === undefined)
11066
+ continue;
11067
+ if (typeof hunk.move_path !== "string" || !hunk.move_path)
11068
+ return;
11069
+ paths.push(hunk.move_path);
11070
+ }
11071
+ return paths;
11072
+ }
11073
+ function patchFileTargets(args2) {
11074
+ const patchValue = args2.patchText ?? args2.patch;
11075
+ if (typeof patchValue === "string")
11076
+ return patchTextFileTargets(patchValue);
11077
+ if (patchValue !== undefined)
11078
+ return;
11079
+ return args2.hunks === undefined ? [] : hunkFileTargets(args2.hunks);
11080
+ }
11081
+ function fileTargetArguments(args2) {
11082
+ const paths = [];
11083
+ for (const key of ["filePath", "path"]) {
11084
+ if (!(key in args2))
11085
+ continue;
11086
+ if (typeof args2[key] !== "string" || args2[key].length === 0) {
11087
+ return;
11088
+ }
11089
+ paths.push(args2[key]);
11090
+ }
11091
+ return paths.length > 0 ? paths : undefined;
11092
+ }
11093
+ function deriveFileOperationTarget(args2, sessionLocation, parentTargetRoot, options, realPath) {
11094
+ const paths = fileTargetArguments(args2);
11095
+ if (!paths)
11096
+ return unavailableOperationTarget();
11097
+ const targets = paths.map((rawPath) => deriveFileTarget(rawPath, sessionLocation, parentTargetRoot, options, realPath));
11098
+ const targetRoot = sharedTargetRoot(targets);
11099
+ return targetRoot ? { status: "available", targetRoot } : unavailableOperationTarget();
11100
+ }
11101
+ function deriveApplyPatchTarget(args2, sessionLocation, parentTargetRoot, options, realPath) {
11102
+ if (args2.workdir !== undefined && (typeof args2.workdir !== "string" || args2.workdir.length === 0)) {
11103
+ return unavailableOperationTarget();
11104
+ }
11105
+ const patchPaths = patchFileTargets(args2);
11106
+ if (!patchPaths)
11107
+ return unavailableOperationTarget();
11108
+ const workdir = args2.workdir === undefined ? {
11109
+ status: "available",
11110
+ target: {
11111
+ targetRoot: parentTargetRoot,
11112
+ resolvedPath: sessionLocation
11113
+ }
11114
+ } : deriveDirectoryTarget(args2.workdir, sessionLocation, parentTargetRoot, options, realPath);
11115
+ if (workdir.status === "unavailable")
11116
+ return unavailableOperationTarget();
11117
+ const targets = patchPaths.map((rawPath) => deriveFileTarget(rawPath, workdir.target.resolvedPath, parentTargetRoot, options, realPath, args2.workdir === undefined));
11118
+ const targetRoot = sharedTargetRoot(targets, [workdir.target.targetRoot]);
11119
+ return targetRoot ? { status: "available", targetRoot } : unavailableOperationTarget();
11120
+ }
11121
+ function deriveBashTarget(args2, sessionLocation, parentTargetRoot, options, realPath) {
11122
+ if (args2.workdir === undefined) {
11123
+ return { status: "available", targetRoot: parentTargetRoot };
11124
+ }
11125
+ if (typeof args2.workdir !== "string" || args2.workdir.length === 0) {
11126
+ return unavailableOperationTarget();
11127
+ }
11128
+ const candidatePath = canonicalExistingPath(path9.resolve(sessionLocation, args2.workdir), realPath);
11129
+ if (!candidatePath)
11130
+ return unavailableOperationTarget();
11131
+ const parentResult = path9.isAbsolute(args2.workdir) ? undefined : trustedParentTarget(parentTargetRoot, candidatePath);
11132
+ return parentResult ?? validationForCandidate(candidatePath, options);
11133
+ }
11134
+ function deriveOpencodeOperationTarget(tool, args2, options) {
11135
+ const realPath = options.realPath ?? fs10.realpathSync;
11136
+ const parentTargetRoot = canonicalExistingPath(options.parentTargetRoot, realPath);
11137
+ if (!parentTargetRoot)
11138
+ return unavailableOperationTarget();
11139
+ if (!isRecord7(args2))
11140
+ return unavailableOperationTarget();
11141
+ const sessionLocation = canonicalExistingPath(options.sessionLocation ?? parentTargetRoot, realPath);
11142
+ if (!sessionLocation)
11143
+ return unavailableOperationTarget();
11144
+ if (tool === "write" || tool === "edit") {
11145
+ return deriveFileOperationTarget(args2, sessionLocation, parentTargetRoot, options, realPath);
11146
+ }
11147
+ if (tool === "apply_patch") {
11148
+ return deriveApplyPatchTarget(args2, sessionLocation, parentTargetRoot, options, realPath);
11149
+ }
11150
+ return tool === "bash" ? deriveBashTarget(args2, sessionLocation, parentTargetRoot, options, realPath) : unavailableOperationTarget();
11151
+ }
10640
11152
  function serializeStableArray(value, depth, budget) {
10641
11153
  const entries = [];
10642
11154
  let remaining = budget;
@@ -10719,7 +11231,7 @@ function terminalForOutput(tool, output) {
10719
11231
  };
10720
11232
  }
10721
11233
  function localOperation(operation) {
10722
- return operation === "implementation" || operation === "verification" || operation === "commit";
11234
+ return operation !== null && isLocalOperation(operation);
10723
11235
  }
10724
11236
  function remoteOperation(operation) {
10725
11237
  return operation === "push" || operation === "pr-creation" || operation === "check-readback" || operation === "review-readback";
@@ -10905,7 +11417,7 @@ function buildMarker(sources, malformed, current) {
10905
11417
  };
10906
11418
  return `${MARKER_OPEN}${JSON.stringify(document2)}${MARKER_CLOSE}`;
10907
11419
  }
10908
- function createSessionRuntime(options) {
11420
+ function createSessionRuntime(options, operationObservers, recoveredOperationContexts) {
10909
11421
  let ledger;
10910
11422
  let guard;
10911
11423
  let initialized = false;
@@ -10928,6 +11440,74 @@ function createSessionRuntime(options) {
10928
11440
  const pendingQuestionChallenges = new Map;
10929
11441
  const blockedQuestionCalls = new Map;
10930
11442
  const consumedQuestionTargets = new Set;
11443
+ function rememberOperationObserver(registration) {
11444
+ const existing = operationObservers.get(registration.observer.targetDigest);
11445
+ if (existing && existing.targetRoot !== registration.targetRoot)
11446
+ return;
11447
+ operationObservers.set(registration.observer.targetDigest, registration);
11448
+ }
11449
+ function parentObserverRegistration() {
11450
+ if (!options.observer)
11451
+ return;
11452
+ const targetRoot = canonicalExistingPath(options.targetDirectory ?? process.cwd(), fs10.realpathSync);
11453
+ if (!targetRoot)
11454
+ return;
11455
+ let validation;
11456
+ try {
11457
+ validation = options.observer.validateRegisteredWorktree(targetRoot);
11458
+ } catch {
11459
+ return;
11460
+ }
11461
+ const registeredIdentity = registeredWorktreeIdentity(validation);
11462
+ if (validation.status !== "ok" || registeredIdentity === undefined || validation.targetRoot !== targetRoot) {
11463
+ return;
11464
+ }
11465
+ const registration = {
11466
+ targetRoot,
11467
+ registeredWorktreeIdentity: registeredIdentity,
11468
+ observer: options.observer
11469
+ };
11470
+ rememberOperationObserver(registration);
11471
+ return registration;
11472
+ }
11473
+ function operationObserverRegistration(targetIdentity) {
11474
+ const existing = operationObservers.get(targetIdentity);
11475
+ if (existing)
11476
+ return existing;
11477
+ if (options.observer?.targetDigest !== targetIdentity)
11478
+ return;
11479
+ return parentObserverRegistration();
11480
+ }
11481
+ function effectiveOperationObserver() {
11482
+ const parentObserver = options.observer;
11483
+ if (!parentObserver)
11484
+ return;
11485
+ const pinnedTargetIdentity = guard.status().unit?.pinnedOperationTargetIdentity;
11486
+ if (pinnedTargetIdentity === undefined || pinnedTargetIdentity === options.workspaceIdentity) {
11487
+ return {
11488
+ observer: parentObserver,
11489
+ targetIdentity: options.workspaceIdentity,
11490
+ pinned: false
11491
+ };
11492
+ }
11493
+ const registration = operationObserverRegistration(pinnedTargetIdentity);
11494
+ if (!registration)
11495
+ return;
11496
+ let validation;
11497
+ try {
11498
+ validation = parentObserver.validateRegisteredWorktree(registration.targetRoot);
11499
+ } catch {
11500
+ return;
11501
+ }
11502
+ if (validation.status !== "ok" || validation.targetRoot !== registration.targetRoot || registeredWorktreeIdentity(validation) !== registration.registeredWorktreeIdentity || registration.observer.targetDigest !== pinnedTargetIdentity) {
11503
+ return;
11504
+ }
11505
+ return {
11506
+ observer: registration.observer,
11507
+ targetIdentity: pinnedTargetIdentity,
11508
+ pinned: true
11509
+ };
11510
+ }
10931
11511
  function questionResource(target) {
10932
11512
  const status = guard.status();
10933
11513
  return `workflow/${target}/${status.unit?.unitId ?? status.epoch?.epochId ?? "unknown"}`;
@@ -11330,17 +11910,14 @@ function createSessionRuntime(options) {
11330
11910
  markUnavailable();
11331
11911
  return;
11332
11912
  }
11333
- const current = await options.observer?.snapshot();
11334
- if (!current || current.status === "unavailable") {
11335
- markUnavailable();
11336
- return;
11337
- }
11913
+ try {
11914
+ await options.observer?.snapshot();
11915
+ } catch {}
11338
11916
  const currentStatus = guard.status();
11339
11917
  if (!currentStatus.epoch || !currentStatus.unit)
11340
11918
  return;
11341
11919
  const expectedWorkspace = childLedger.digestIdentity("workspace", parentBefore.workspaceIdentity);
11342
- const expectedRepository = childLedger.digestIdentity("repository", current.snapshot.repositoryRevisionDigest);
11343
- const expectedWorktree = childLedger.digestIdentity("worktree", current.snapshot.worktreeRevisionDigest);
11920
+ let batchTargetIdentity = currentStatus.unit.pinnedOperationTargetIdentity;
11344
11921
  const candidates = [];
11345
11922
  for (const childReceipt of recovered.receipts) {
11346
11923
  const operation = childReceipt.canonical.operation;
@@ -11350,15 +11927,66 @@ function createSessionRuntime(options) {
11350
11927
  markUnavailable();
11351
11928
  return;
11352
11929
  }
11930
+ const targetIdentity = childReceipt.canonical.operationTargetIdentity;
11931
+ if (!targetIdentity) {
11932
+ markUnavailable();
11933
+ return;
11934
+ }
11935
+ if (batchTargetIdentity !== undefined && targetIdentity !== batchTargetIdentity) {
11936
+ markUnavailable();
11937
+ return;
11938
+ }
11939
+ batchTargetIdentity = targetIdentity;
11940
+ const registration = operationObserverRegistration(targetIdentity);
11941
+ if (!registration || !options.observer) {
11942
+ markUnavailable();
11943
+ return;
11944
+ }
11945
+ let validation;
11946
+ try {
11947
+ validation = options.observer.validateRegisteredWorktree(registration.targetRoot);
11948
+ } catch {
11949
+ markUnavailable();
11950
+ return;
11951
+ }
11952
+ if (validation.status === "error" || validation.targetRoot !== registration.targetRoot || registeredWorktreeIdentity(validation) !== registration.registeredWorktreeIdentity || registration.observer.targetDigest !== targetIdentity) {
11953
+ markUnavailable();
11954
+ return;
11955
+ }
11956
+ let targetResult;
11957
+ try {
11958
+ targetResult = await registration.observer.snapshot();
11959
+ } catch {
11960
+ markUnavailable();
11961
+ return;
11962
+ }
11963
+ if (targetResult.status === "unavailable" || targetResult.snapshot.targetDigest !== targetIdentity) {
11964
+ markUnavailable();
11965
+ return;
11966
+ }
11967
+ const recoveredContext = recoveredOperationContexts.get(childReceipt.canonical.receiptId);
11968
+ if (recoveredContext && recoveredContext.targetIdentity !== targetIdentity || targetIdentity !== options.observer.targetDigest && !recoveredContext) {
11969
+ markUnavailable();
11970
+ return;
11971
+ }
11972
+ const expectedRepository = childLedger.digestIdentity("repository", targetResult.snapshot.repositoryRevisionDigest);
11973
+ const expectedWorktree = childLedger.digestIdentity("worktree", targetResult.snapshot.worktreeRevisionDigest);
11353
11974
  if (childReceipt.canonical.repositoryDigest !== expectedRepository || childReceipt.canonical.worktreeDigest !== expectedWorktree) {
11354
11975
  continue;
11355
11976
  }
11356
- candidates.push(childReceipt);
11977
+ candidates.push({
11978
+ receipt: childReceipt,
11979
+ snapshot: targetResult.snapshot,
11980
+ before: recoveredContext?.before
11981
+ });
11357
11982
  }
11358
11983
  let minted = false;
11359
- for (const childReceipt of candidates) {
11984
+ for (const candidate of candidates) {
11985
+ const childReceipt = candidate.receipt;
11986
+ const targetSnapshot = candidate.snapshot;
11360
11987
  const operation = childReceipt.canonical.operation;
11361
11988
  const parentContext = guard.currentOperationContext();
11989
+ const beforeSnapshot = candidate.before;
11362
11990
  const callID = `task-${host.callID}-${childReceipt.canonical.receiptId}`;
11363
11991
  const observation = {
11364
11992
  callId: callID,
@@ -11368,13 +11996,15 @@ function createSessionRuntime(options) {
11368
11996
  epochId: currentStatus.epoch.epochId,
11369
11997
  unitId: currentStatus.unit.unitId,
11370
11998
  workspaceIdentity: parentContext.workspaceIdentity,
11371
- ...parentContext.repositoryIdentity ? { repositoryIdentity: parentContext.repositoryIdentity } : {},
11372
- ...parentContext.worktreeIdentity ? { worktreeIdentity: parentContext.worktreeIdentity } : {}
11999
+ operationTargetIdentity: childReceipt.canonical.operationTargetIdentity,
12000
+ repositoryIdentity: beforeSnapshot?.repositoryRevisionDigest ?? parentContext.repositoryIdentity ?? targetSnapshot.repositoryRevisionDigest,
12001
+ worktreeIdentity: beforeSnapshot?.worktreeRevisionDigest ?? parentContext.worktreeIdentity ?? targetSnapshot.worktreeRevisionDigest
11373
12002
  },
11374
12003
  after: {
11375
12004
  workspaceIdentity: parentContext.workspaceIdentity,
11376
- repositoryIdentity: current.snapshot.repositoryRevisionDigest,
11377
- worktreeIdentity: current.snapshot.worktreeRevisionDigest
12005
+ operationTargetIdentity: childReceipt.canonical.operationTargetIdentity,
12006
+ repositoryIdentity: targetSnapshot.repositoryRevisionDigest,
12007
+ worktreeIdentity: targetSnapshot.worktreeRevisionDigest
11378
12008
  },
11379
12009
  terminal: {
11380
12010
  status: "success",
@@ -11477,35 +12107,37 @@ function createSessionRuntime(options) {
11477
12107
  abandonPending();
11478
12108
  if (!options.observer)
11479
12109
  return;
11480
- let result;
11481
- try {
11482
- result = await options.observer.snapshot();
11483
- } catch {
12110
+ const effective = effectiveOperationObserver();
12111
+ if (!effective) {
11484
12112
  markUnavailable();
11485
12113
  return;
11486
12114
  }
11487
- if (result.status === "unavailable") {
12115
+ let result;
12116
+ try {
12117
+ result = await effective.observer.snapshot();
12118
+ } catch {
11488
12119
  markUnavailable();
11489
12120
  return;
11490
12121
  }
11491
- if (result.snapshot.targetDigest !== options.workspaceIdentity) {
12122
+ if (result.status === "unavailable" || result.snapshot.targetDigest !== effective.targetIdentity) {
11492
12123
  markUnavailable();
11493
12124
  return;
11494
12125
  }
11495
12126
  const observed = guard.observeReadback({
11496
12127
  workspaceIdentity: options.workspaceIdentity,
11497
12128
  repositoryIdentity: result.snapshot.repositoryRevisionDigest,
11498
- worktreeIdentity: result.snapshot.worktreeRevisionDigest
12129
+ worktreeIdentity: result.snapshot.worktreeRevisionDigest,
12130
+ ...effective.pinned ? { operationTargetIdentity: effective.targetIdentity } : {}
11499
12131
  });
11500
- if (observed.status === "rejected" && observed.reasonCode === "workspace-mismatch" || !await refreshRemoteReadbacks(result.snapshot)) {
12132
+ if (observed.status === "rejected" && observed.reasonCode === "workspace-mismatch" || !await refreshRemoteReadbacks(effective.observer, result.snapshot)) {
11501
12133
  markUnavailable();
11502
12134
  }
11503
12135
  }
11504
- async function refreshRemoteReadbacks(local) {
12136
+ async function refreshRemoteReadbacks(observer, local) {
11505
12137
  const remoteOperations = guard.status().satisfiedOperations.filter(remoteOperation);
11506
12138
  if (remoteOperations.length === 0)
11507
12139
  return true;
11508
- const remoteSnapshot = options.observer?.remoteSnapshot;
12140
+ const remoteSnapshot = observer.remoteSnapshot;
11509
12141
  if (!remoteSnapshot)
11510
12142
  return false;
11511
12143
  for (const operation of remoteOperations) {
@@ -11711,7 +12343,7 @@ function createSessionRuntime(options) {
11711
12343
  }
11712
12344
  blockedCompletes.set(callDigest, target);
11713
12345
  }
11714
- async function remoteIntentForOperation(host, args2) {
12346
+ async function remoteIntentForOperation(host, args2, observer) {
11715
12347
  if (host.tool !== "bash" || !isRecord7(args2))
11716
12348
  return;
11717
12349
  const command = bashCommand(args2);
@@ -11720,7 +12352,7 @@ function createSessionRuntime(options) {
11720
12352
  const classification = await classifyCommandIntent(options.classifier, command);
11721
12353
  if (!classification || !remoteOperation(classification.category))
11722
12354
  return;
11723
- const remoteSnapshot = options.observer?.remoteSnapshot;
12355
+ const remoteSnapshot = observer.remoteSnapshot;
11724
12356
  if (!remoteSnapshot)
11725
12357
  return { operation: classification.category };
11726
12358
  try {
@@ -11744,9 +12376,40 @@ function createSessionRuntime(options) {
11744
12376
  if (bindCall(callDigest, "operation", `${host.tool}:${fingerprint}`) !== "new") {
11745
12377
  return;
11746
12378
  }
12379
+ const parentTargetRoot = options.targetDirectory ?? process.cwd();
12380
+ const sessionLocation = options.sessionLocation ?? parentTargetRoot;
12381
+ const target = deriveOpencodeOperationTarget(host.tool, args2, {
12382
+ parentTargetRoot,
12383
+ sessionLocation,
12384
+ validateRegisteredWorktree: options.observer.validateRegisteredWorktree
12385
+ });
12386
+ if (target.status === "unavailable") {
12387
+ markUnavailable();
12388
+ return;
12389
+ }
12390
+ let registeredTarget;
12391
+ try {
12392
+ registeredTarget = options.observer.validateRegisteredWorktree(target.targetRoot);
12393
+ } catch {
12394
+ markUnavailable();
12395
+ return;
12396
+ }
12397
+ const registeredIdentity = registeredWorktreeIdentity(registeredTarget);
12398
+ if (registeredTarget.status !== "ok" || registeredIdentity === undefined || registeredTarget.targetRoot !== target.targetRoot) {
12399
+ markUnavailable();
12400
+ return;
12401
+ }
12402
+ const canonicalParentTargetRoot = canonicalExistingPath(parentTargetRoot, fs10.realpathSync);
12403
+ if (!canonicalParentTargetRoot) {
12404
+ markUnavailable();
12405
+ return;
12406
+ }
12407
+ const operationObserver = target.targetRoot === canonicalParentTargetRoot ? options.observer : [...operationObservers.values()].find((registration) => registration.targetRoot === target.targetRoot)?.observer ?? createOpencodeOperationObserver({
12408
+ targetDirectory: target.targetRoot
12409
+ });
11747
12410
  let result;
11748
12411
  try {
11749
- result = await options.observer.snapshot();
12412
+ result = await operationObserver.snapshot();
11750
12413
  } catch {
11751
12414
  markUnavailable();
11752
12415
  return;
@@ -11755,15 +12418,24 @@ function createSessionRuntime(options) {
11755
12418
  markUnavailable();
11756
12419
  return;
11757
12420
  }
11758
- if (result.snapshot.targetDigest !== options.workspaceIdentity) {
12421
+ if (result.snapshot.targetDigest !== operationObserver.targetDigest) {
11759
12422
  markUnavailable();
11760
12423
  return;
11761
12424
  }
11762
- const remote = await remoteIntentForOperation(host, args2);
12425
+ rememberOperationObserver({
12426
+ targetRoot: target.targetRoot,
12427
+ registeredWorktreeIdentity: registeredIdentity,
12428
+ observer: operationObserver
12429
+ });
12430
+ const remote = await remoteIntentForOperation(host, args2, operationObserver);
11763
12431
  pendingOperations.set(callDigest, {
11764
12432
  callID: host.callID,
11765
12433
  tool: host.tool,
11766
12434
  argsFingerprint: fingerprint,
12435
+ targetRoot: target.targetRoot,
12436
+ targetIdentity: operationObserver.targetDigest,
12437
+ registeredWorktreeIdentity: registeredIdentity,
12438
+ observer: operationObserver,
11767
12439
  before: result.snapshot,
11768
12440
  ...remote ? { remoteOperation: remote.operation, remoteBefore: remote.before } : {}
11769
12441
  });
@@ -12079,44 +12751,87 @@ function createSessionRuntime(options) {
12079
12751
  async function completionReadbacks() {
12080
12752
  if (!options.observer)
12081
12753
  return { status: "none" };
12082
- let result;
12754
+ let parentResult;
12083
12755
  try {
12084
- result = await options.observer.snapshot();
12756
+ parentResult = await options.observer.snapshot();
12085
12757
  } catch {
12086
12758
  return { status: "unavailable" };
12087
12759
  }
12088
- if (result.status === "unavailable" || result.snapshot.targetDigest !== options.workspaceIdentity) {
12760
+ if (parentResult.status === "unavailable" || parentResult.snapshot.targetDigest !== options.workspaceIdentity) {
12761
+ return { status: "unavailable" };
12762
+ }
12763
+ const effective = effectiveOperationObserver();
12764
+ if (!effective)
12089
12765
  return { status: "unavailable" };
12766
+ let effectiveSnapshot = parentResult.snapshot;
12767
+ if (effective.pinned) {
12768
+ let pinnedResult;
12769
+ try {
12770
+ pinnedResult = await effective.observer.snapshot();
12771
+ } catch {
12772
+ return { status: "unavailable" };
12773
+ }
12774
+ if (pinnedResult.status === "unavailable" || pinnedResult.snapshot.targetDigest !== effective.targetIdentity) {
12775
+ return { status: "unavailable" };
12776
+ }
12777
+ effectiveSnapshot = pinnedResult.snapshot;
12090
12778
  }
12091
- const remoteReadbacks = await completionRemoteReadbacks();
12779
+ const initialEffectiveSnapshot = effectiveSnapshot;
12780
+ const remoteReadbacks = await completionRemoteReadbacks(effective.observer);
12092
12781
  if (!remoteReadbacks)
12093
12782
  return { status: "unavailable" };
12094
- let finalResult;
12783
+ let finalParentResult;
12095
12784
  try {
12096
- finalResult = await options.observer.snapshot();
12785
+ finalParentResult = await options.observer.snapshot();
12097
12786
  } catch {
12098
12787
  return { status: "unavailable" };
12099
12788
  }
12100
- if (finalResult.status === "unavailable" || finalResult.snapshot.targetDigest !== result.snapshot.targetDigest || finalResult.snapshot.repositoryRevisionDigest !== result.snapshot.repositoryRevisionDigest || finalResult.snapshot.worktreeRevisionDigest !== result.snapshot.worktreeRevisionDigest) {
12789
+ if (finalParentResult.status === "unavailable" || finalParentResult.snapshot.targetDigest !== parentResult.snapshot.targetDigest || finalParentResult.snapshot.repositoryRevisionDigest !== parentResult.snapshot.repositoryRevisionDigest || finalParentResult.snapshot.worktreeRevisionDigest !== parentResult.snapshot.worktreeRevisionDigest) {
12101
12790
  return { status: "unavailable" };
12102
12791
  }
12792
+ let finalEffective = effective;
12793
+ if (effective.pinned) {
12794
+ const revalidated = effectiveOperationObserver();
12795
+ if (!revalidated)
12796
+ return { status: "unavailable" };
12797
+ finalEffective = revalidated;
12798
+ if (finalEffective.observer !== effective.observer || finalEffective.targetIdentity !== effective.targetIdentity || !finalEffective.pinned) {
12799
+ return { status: "unavailable" };
12800
+ }
12801
+ let pinnedResult;
12802
+ try {
12803
+ pinnedResult = await finalEffective.observer.snapshot();
12804
+ } catch {
12805
+ return { status: "unavailable" };
12806
+ }
12807
+ if (pinnedResult.status === "unavailable" || pinnedResult.snapshot.targetDigest !== finalEffective.targetIdentity) {
12808
+ return { status: "unavailable" };
12809
+ }
12810
+ effectiveSnapshot = pinnedResult.snapshot;
12811
+ if (effectiveSnapshot.repositoryRevisionDigest !== initialEffectiveSnapshot.repositoryRevisionDigest || effectiveSnapshot.worktreeRevisionDigest !== initialEffectiveSnapshot.worktreeRevisionDigest) {
12812
+ return { status: "unavailable" };
12813
+ }
12814
+ } else {
12815
+ effectiveSnapshot = finalParentResult.snapshot;
12816
+ }
12103
12817
  return {
12104
12818
  status: "ready",
12105
12819
  readbacks: [
12106
12820
  {
12107
12821
  workspaceIdentity: options.workspaceIdentity,
12108
- repositoryIdentity: finalResult.snapshot.repositoryRevisionDigest,
12109
- worktreeIdentity: finalResult.snapshot.worktreeRevisionDigest
12822
+ repositoryIdentity: effectiveSnapshot.repositoryRevisionDigest,
12823
+ worktreeIdentity: effectiveSnapshot.worktreeRevisionDigest,
12824
+ operationTargetIdentity: finalEffective.targetIdentity
12110
12825
  },
12111
- ...remoteReadbacks.map(({ operation, snapshot }) => remoteReadbackInput(operation, finalResult.snapshot, options.workspaceIdentity, snapshot))
12826
+ ...remoteReadbacks.map(({ operation, snapshot }) => remoteReadbackInput(operation, effectiveSnapshot, options.workspaceIdentity, snapshot))
12112
12827
  ]
12113
12828
  };
12114
12829
  }
12115
- async function completionRemoteReadbacks() {
12830
+ async function completionRemoteReadbacks(observer) {
12116
12831
  const remoteOperations = guard.status().satisfiedOperations.filter(remoteOperation);
12117
12832
  if (remoteOperations.length === 0)
12118
12833
  return [];
12119
- const remoteSnapshot = options.observer?.remoteSnapshot;
12834
+ const remoteSnapshot = observer.remoteSnapshot;
12120
12835
  if (!remoteSnapshot)
12121
12836
  return;
12122
12837
  const readbacks = [];
@@ -12185,19 +12900,28 @@ function createSessionRuntime(options) {
12185
12900
  }
12186
12901
  return pending;
12187
12902
  }
12188
- async function captureAfterOperation() {
12903
+ async function captureAfterOperation(pending) {
12189
12904
  if (!options.observer)
12190
12905
  return;
12906
+ let validation;
12907
+ try {
12908
+ validation = options.observer.validateRegisteredWorktree(pending.targetRoot);
12909
+ } catch {
12910
+ return;
12911
+ }
12912
+ if (validation.status === "error" || validation.targetRoot !== pending.targetRoot || registeredWorktreeIdentity(validation) !== pending.registeredWorktreeIdentity) {
12913
+ return;
12914
+ }
12191
12915
  let result;
12192
12916
  try {
12193
- result = await options.observer.snapshot();
12917
+ result = await pending.observer.snapshot();
12194
12918
  } catch {
12195
12919
  return;
12196
12920
  }
12197
- return result.status === "available" && result.snapshot.targetDigest === options.workspaceIdentity ? result.snapshot : undefined;
12921
+ return result.status === "available" && result.snapshot.targetDigest === pending.targetIdentity ? result.snapshot : undefined;
12198
12922
  }
12199
- async function captureRemoteAfter(operation) {
12200
- const remoteSnapshot = options.observer?.remoteSnapshot;
12923
+ async function captureRemoteAfter(operation, observer) {
12924
+ const remoteSnapshot = observer.remoteSnapshot;
12201
12925
  if (!remoteSnapshot)
12202
12926
  return;
12203
12927
  let result;
@@ -12231,6 +12955,7 @@ function createSessionRuntime(options) {
12231
12955
  workspaceIdentity: options.workspaceIdentity,
12232
12956
  repositoryIdentity: pending.before.repositoryRevisionDigest,
12233
12957
  worktreeIdentity: pending.before.worktreeRevisionDigest,
12958
+ ...localOperation(operation) ? { operationTargetIdentity: pending.targetIdentity } : {},
12234
12959
  ...pending.remoteBefore?.status === "available" ? {
12235
12960
  resourceIdentity: pending.remoteBefore.snapshot.resourceIdentity,
12236
12961
  resourceRevisionIdentity: pending.remoteBefore.snapshot.resourceRevisionIdentity
@@ -12240,6 +12965,7 @@ function createSessionRuntime(options) {
12240
12965
  workspaceIdentity: options.workspaceIdentity,
12241
12966
  repositoryIdentity: after2.repositoryRevisionDigest,
12242
12967
  worktreeIdentity: after2.worktreeRevisionDigest,
12968
+ ...localOperation(operation) ? { operationTargetIdentity: pending.targetIdentity } : {},
12243
12969
  commitClosure: after2.commitClosure,
12244
12970
  ...remoteAfter ? {
12245
12971
  resourceIdentity: remoteAfter.resourceIdentity,
@@ -12292,7 +13018,7 @@ function createSessionRuntime(options) {
12292
13018
  sealOperation(callDigest, pending, true);
12293
13019
  return { status: "unavailable" };
12294
13020
  }
12295
- const afterSnapshot = await captureAfterOperation();
13021
+ const afterSnapshot = await captureAfterOperation(pending);
12296
13022
  if (!afterSnapshot) {
12297
13023
  sealOperation(callDigest, pending, true);
12298
13024
  return { status: "unavailable" };
@@ -12309,7 +13035,7 @@ function createSessionRuntime(options) {
12309
13035
  }
12310
13036
  let finalObservation = classification.observation;
12311
13037
  if (remoteOperation(finalObservation.operation)) {
12312
- const remoteAfter = await captureRemoteAfter(finalObservation.operation);
13038
+ const remoteAfter = await captureRemoteAfter(finalObservation.operation, pending.observer);
12313
13039
  if (!remoteAfter) {
12314
13040
  sealOperation(callDigest, pending, true);
12315
13041
  return { status: "unavailable" };
@@ -12346,7 +13072,11 @@ function createSessionRuntime(options) {
12346
13072
  }
12347
13073
  if (observed.status !== "accepted")
12348
13074
  return { status: "ignored" };
12349
- return { status: "accepted", operation: observed.operation };
13075
+ return {
13076
+ status: "accepted",
13077
+ operation: observed.operation,
13078
+ after: afterSnapshot
13079
+ };
12350
13080
  }
12351
13081
  function receiptForOperation(callID, operation) {
12352
13082
  const callDigest = digestCall(ledger, callID);
@@ -12390,8 +13120,16 @@ function createSessionRuntime(options) {
12390
13120
  }
12391
13121
  if (result.status === "accepted") {
12392
13122
  const receipt = receiptForOperation(host.callID, result.operation);
12393
- if (receipt)
13123
+ if (receipt) {
13124
+ if (localOperation(result.operation)) {
13125
+ recoveredOperationContexts.set(receipt.canonical.receiptId, {
13126
+ targetIdentity: pending.targetIdentity,
13127
+ before: pending.before,
13128
+ after: result.after
13129
+ });
13130
+ }
12394
13131
  mergeReceiptMarker(output, receipt);
13132
+ }
12395
13133
  }
12396
13134
  }
12397
13135
  async function after(input, output) {
@@ -12557,6 +13295,8 @@ function makeWorkflowTool(description, args2, getRuntime, execute) {
12557
13295
  }
12558
13296
  function createOpencodeWorkflowGuard(options) {
12559
13297
  const sessions = new Map;
13298
+ const operationObservers = new Map;
13299
+ const recoveredOperationContexts = new Map;
12560
13300
  function sessionRuntimeFor(sessionID) {
12561
13301
  const existing = sessions.get(sessionID);
12562
13302
  if (existing)
@@ -12565,7 +13305,7 @@ function createOpencodeWorkflowGuard(options) {
12565
13305
  ...options,
12566
13306
  registrationIdentity: options.registrationIdentity,
12567
13307
  sessionSalt: options.sessionSalt
12568
- });
13308
+ }, operationObservers, recoveredOperationContexts);
12569
13309
  sessions.set(sessionID, runtime);
12570
13310
  return runtime;
12571
13311
  }
@@ -12694,8 +13434,8 @@ function createOpencodeWorkflowGuard(options) {
12694
13434
  }
12695
13435
 
12696
13436
  // src/lib/skill-resolver.ts
12697
- import fs10 from "fs";
12698
- import path9 from "path";
13437
+ import fs11 from "fs";
13438
+ import path10 from "path";
12699
13439
  import { pathToFileURL as pathToFileURL2 } from "url";
12700
13440
  function getAllSkills(options) {
12701
13441
  const { bundledSkillsDir, disabledSkills } = options;
@@ -12731,7 +13471,7 @@ function buildSkillToolParameterHint(options) {
12731
13471
  }
12732
13472
  function buildSkillContentOutput(matchedSkill) {
12733
13473
  const body2 = extractSkillBody(matchedSkill.wrappedTemplate);
12734
- const dir = path9.dirname(matchedSkill.skillFile);
13474
+ const dir = path10.dirname(matchedSkill.skillFile);
12735
13475
  const base = pathToFileURL2(dir).href;
12736
13476
  const files = discoverSkillFiles(dir);
12737
13477
  const lines = [
@@ -12762,17 +13502,17 @@ function discoverSkillFiles(dir, limit = 10) {
12762
13502
  function handleEntry(entry, currentDir) {
12763
13503
  if (entry.isDirectory()) {
12764
13504
  if (!shouldSkipDirectory(entry.name)) {
12765
- recurse(path9.resolve(currentDir, entry.name));
13505
+ recurse(path10.resolve(currentDir, entry.name));
12766
13506
  }
12767
13507
  } else if (shouldIncludeFile(entry.name)) {
12768
- files.push(path9.resolve(currentDir, entry.name));
13508
+ files.push(path10.resolve(currentDir, entry.name));
12769
13509
  }
12770
13510
  }
12771
13511
  function recurse(currentDir) {
12772
13512
  if (files.length >= limit)
12773
13513
  return;
12774
13514
  try {
12775
- const entries = fs10.readdirSync(currentDir, { withFileTypes: true });
13515
+ const entries = fs11.readdirSync(currentDir, { withFileTypes: true });
12776
13516
  for (const entry of entries) {
12777
13517
  if (files.length >= limit)
12778
13518
  break;
@@ -12829,19 +13569,19 @@ function createSkillTool(options) {
12829
13569
  }
12830
13570
 
12831
13571
  // src/index.ts
12832
- var __dirname3 = path10.dirname(fileURLToPath3(import.meta.url));
12833
- var packageRoot2 = path10.resolve(__dirname3, "..");
12834
- var bundledSkillsDir = path10.join(packageRoot2, "skills");
12835
- var bundledAgentsDir2 = path10.join(packageRoot2, "agents");
12836
- var bundledCommandsDir = path10.join(packageRoot2, "commands");
12837
- var packageJsonPath = path10.join(packageRoot2, "package.json");
12838
- var canonicalPackageSource = pathToFileURL3(fs11.realpathSync(packageRoot2)).href;
13572
+ var __dirname3 = path11.dirname(fileURLToPath3(import.meta.url));
13573
+ var packageRoot2 = path11.resolve(__dirname3, "..");
13574
+ var bundledSkillsDir = path11.join(packageRoot2, "skills");
13575
+ var bundledAgentsDir2 = path11.join(packageRoot2, "agents");
13576
+ var bundledCommandsDir = path11.join(packageRoot2, "commands");
13577
+ var packageJsonPath = path11.join(packageRoot2, "package.json");
13578
+ var canonicalPackageSource = pathToFileURL3(fs12.realpathSync(packageRoot2)).href;
12839
13579
  var registrationSourceIdentity = createHash5("sha256").update(`systematic/opencode-registration-source/v1/${canonicalPackageSource}`).digest("hex");
12840
13580
  var getPackageVersion = () => {
12841
13581
  try {
12842
- if (!fs11.existsSync(packageJsonPath))
13582
+ if (!fs12.existsSync(packageJsonPath))
12843
13583
  return "unknown";
12844
- const content = fs11.readFileSync(packageJsonPath, "utf8");
13584
+ const content = fs12.readFileSync(packageJsonPath, "utf8");
12845
13585
  const parsed = JSON.parse(content);
12846
13586
  return parsed.version ?? "unknown";
12847
13587
  } catch {
@@ -12889,6 +13629,8 @@ var initializePlugin = async ({
12889
13629
  repositoryIdentity: initialIdentities.repositoryRevisionDigest,
12890
13630
  worktreeIdentity: initialIdentities.worktreeRevisionDigest,
12891
13631
  registrationIdentity: registrationSourceIdentity,
13632
+ targetDirectory: typeof worktree === "string" ? worktree : directory,
13633
+ sessionLocation: directory,
12892
13634
  observer,
12893
13635
  classifier: createReceiptClassifier(),
12894
13636
  hostReadback: (() => {