archctx 0.4.6 → 0.4.8

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/bin/archctx.mjs CHANGED
@@ -961,8 +961,123 @@ function projectionApplyReceiptInvariantIssues(input) {
961
961
  if (digestJson(input.result.applyReceipt) !== digestJson(input.identity)) {
962
962
  issues.push("projection apply receipt result must carry the same apply identity");
963
963
  }
964
+ if (input.recovery)
965
+ issues.push(...projectionApplyRecoveryBindingInvariantIssues(input.recovery, input));
964
966
  return issues;
965
967
  }
968
+ function projectionApplyRecoveryBindingInvariantIssues(binding, receipt) {
969
+ const issues = [
970
+ ...sortedUniqueIssues("recovery.targets", binding.targets),
971
+ ...sortedUniqueIssues("recovery.changedPaths", binding.changedPaths)
972
+ ];
973
+ if (binding.schemaVersion !== PROJECTION_APPLY_RECOVERY_BINDING_SCHEMA_VERSION) {
974
+ issues.push("recovery schemaVersion is invalid");
975
+ }
976
+ if (binding.targets.length === 0)
977
+ issues.push("recovery targets must contain at least one projection target");
978
+ if (receipt) {
979
+ if (binding.receiptDigest !== receipt.result.receiptDigest) {
980
+ issues.push("recovery receiptDigest must match the committed result receiptDigest");
981
+ }
982
+ if (binding.originalExpectedSnapshot.repositoryId !== receipt.result.inputSnapshot.repositoryId || binding.originalExpectedSnapshot.workspaceId !== receipt.result.inputSnapshot.workspaceId || binding.originalExpectedSnapshot.headSha !== receipt.result.inputSnapshot.headSha || binding.originalExpectedSnapshot.worktreeDigest !== receipt.result.inputSnapshot.worktreeDigest) {
983
+ issues.push("recovery originalExpectedSnapshot must match the committed input snapshot");
984
+ }
985
+ if (binding.rendererVersion !== receipt.result.outputSnapshot.rendererVersion || binding.layoutVersion !== receipt.result.outputSnapshot.layoutVersion || digestJson(binding.generatedFrom) !== digestJson(receipt.result.outputSnapshot.generatedFrom)) {
986
+ issues.push("recovery renderer, layout, and CodeGraph provenance must match the committed output snapshot");
987
+ }
988
+ const expectedDigests = projectionApplyReceiptResultingDigests(receipt);
989
+ if (!expectedDigests || digestJson(binding.expectedResultingDigests) !== digestJson(expectedDigests)) {
990
+ issues.push("recovery expectedResultingDigests must match committed refresh signals");
991
+ }
992
+ }
993
+ return issues;
994
+ }
995
+ function projectionApplyRecoveryIntentInvariantIssues(input) {
996
+ const issues = [];
997
+ if (input.schemaVersion !== PROJECTION_APPLY_RECOVERY_INTENT_SCHEMA_VERSION)
998
+ issues.push("recovery intent schemaVersion is invalid");
999
+ if (!/^[a-zA-Z0-9_.:-]+$/.test(input.requestId))
1000
+ issues.push("recovery intent requestId must use the stable identifier character set");
1001
+ if (input.profile !== "repo-harness/v1")
1002
+ issues.push("recovery intent profile is invalid");
1003
+ for (const field of ["lookupKey", "applyId"]) {
1004
+ if (!/^sha256:[a-f0-9]{64}$/.test(input.receipt[field]))
1005
+ issues.push(`recovery intent receipt.${field} must be a SHA-256 digest`);
1006
+ }
1007
+ return issues;
1008
+ }
1009
+ function projectionApplyRecoveryProofDigest(input) {
1010
+ return digestJson(input);
1011
+ }
1012
+ function projectionApplyRecoveryProofInvariantIssues(input) {
1013
+ const issues = [
1014
+ ...sortedUniqueIssues("acceptedChange.reasonCodes", input.acceptedChange.reasonCodes),
1015
+ ...sortedUniqueIssues("acceptedChange.affectedNodeIds", input.acceptedChange.affectedNodeIds)
1016
+ ];
1017
+ if (input.schemaVersion !== PROJECTION_APPLY_RECOVERY_PROOF_SCHEMA_VERSION)
1018
+ issues.push("recovery proof schemaVersion is invalid");
1019
+ if (!/^[a-zA-Z0-9_.:-]+$/.test(input.requestId))
1020
+ issues.push("recovery proof requestId must use the stable identifier character set");
1021
+ if (input.current.snapshot.generatedFrom.codeGraphStatus !== "ready")
1022
+ issues.push("recovery proof requires a ready current CodeGraph snapshot");
1023
+ const { proofDigest: _proofDigest, deliveryStatus: _deliveryStatus, ...payload } = input;
1024
+ if (projectionApplyRecoveryProofDigest(payload) !== input.proofDigest) {
1025
+ issues.push("recovery proofDigest must bind the semantic proof payload");
1026
+ }
1027
+ return issues;
1028
+ }
1029
+ function projectionApplyRecoveryResultInvariantIssues(input) {
1030
+ const issues = [
1031
+ ...projectionApplyRecoveryProofInvariantIssues(input.proof),
1032
+ ...input.refreshSignals.flatMap((signal, index) => architectureRefreshSignalInvariantIssues(signal, `refreshSignals[${index}]`)),
1033
+ ...sortedUniqueIssues("refreshSignals.signalId", input.refreshSignals.map((signal) => signal.signalId))
1034
+ ];
1035
+ if (input.schemaVersion !== PROJECTION_APPLY_RECOVERY_RESULT_SCHEMA_VERSION) {
1036
+ issues.push("recovery result schemaVersion is invalid");
1037
+ }
1038
+ if (input.proof.deliveryStatus === "already-delivered" && input.refreshSignals.length > 0) {
1039
+ issues.push("already-delivered recovery result cannot repeat refreshSignals");
1040
+ }
1041
+ for (const [index, signal] of input.refreshSignals.entries()) {
1042
+ const prefix = `refreshSignals[${index}]`;
1043
+ if (signal.projectionReceiptDigest !== input.proof.receipt.receiptDigest) {
1044
+ issues.push(`${prefix}.projectionReceiptDigest must match recovery proof receiptDigest`);
1045
+ }
1046
+ if (digestJson(signal.acceptedChange) !== digestJson(input.proof.acceptedChange)) {
1047
+ issues.push(`${prefix}.acceptedChange must match recovery proof approval`);
1048
+ }
1049
+ if (digestJson(signal.resultingDigests) !== digestJson(input.proof.expectedResultingDigests)) {
1050
+ issues.push(`${prefix}.resultingDigests must match recovery proof`);
1051
+ }
1052
+ }
1053
+ return issues;
1054
+ }
1055
+ function projectionApplyRecoveryProofReceiptInvariantIssues(proof, receipt) {
1056
+ const issues = [
1057
+ ...projectionApplyRecoveryProofInvariantIssues(proof),
1058
+ ...projectionApplyReceiptInvariantIssues(receipt)
1059
+ ];
1060
+ const binding = receipt.recovery;
1061
+ if (!binding)
1062
+ return [...issues, "committed projection receipt does not support semantic recovery"];
1063
+ if (proof.receipt.lookupKey !== receipt.identity.lookupKey || proof.receipt.applyId !== receipt.identity.applyId || proof.receipt.receiptDigest !== receipt.result.receiptDigest) {
1064
+ issues.push("recovery proof receipt identity must match the committed receipt");
1065
+ }
1066
+ if (digestJson(proof.acceptedChange) !== digestJson(receipt.identity.acceptedChange) || digestJson(proof.expectedResultingDigests) !== digestJson(binding.expectedResultingDigests)) {
1067
+ issues.push("recovery proof approval binding must match the committed receipt");
1068
+ }
1069
+ if (digestJson(proof.current.resultingDigests) !== digestJson(binding.expectedResultingDigests) || proof.current.ownedOutputDigest !== binding.ownedOutputDigest || proof.current.snapshot.rendererVersion !== binding.rendererVersion || proof.current.snapshot.layoutVersion !== binding.layoutVersion || digestJson(proof.current.snapshot.generatedFrom) !== digestJson(binding.generatedFrom)) {
1070
+ issues.push("recovery proof current state must match the committed recovery binding");
1071
+ }
1072
+ return issues;
1073
+ }
1074
+ function projectionApplyReceiptResultingDigests(receipt) {
1075
+ const signals = receipt.result.refreshSignals;
1076
+ if (signals.length === 0)
1077
+ return;
1078
+ const [first] = signals;
1079
+ return signals.every((signal) => digestJson(signal.resultingDigests) === digestJson(first.resultingDigests)) ? first.resultingDigests : undefined;
1080
+ }
966
1081
  function architectureRefreshSignalInvariantIssues(input, prefix = "signal") {
967
1082
  const issues = [
968
1083
  ...sortedUniqueIssues(`${prefix}.reasonCodes`, input.reasonCodes),
@@ -1006,7 +1121,7 @@ function sortedUniqueIssues(label, values) {
1006
1121
  const expected = [...new Set(values)].sort();
1007
1122
  return expected.length === values.length && expected.every((value, index) => value === values[index]) ? [] : [`${label} must be sorted and unique`];
1008
1123
  }
1009
- var PROJECTION_REQUEST_SCHEMA_VERSION = "archcontext.projection-request/v1", PROJECTION_RESULT_SCHEMA_VERSION = "archcontext.projection-result/v2", PROJECTION_APPLY_IDENTITY_SCHEMA_VERSION = "archcontext.projection-apply-identity/v1", ARCHITECTURE_REFRESH_SIGNAL_SCHEMA_VERSION = "archcontext.architecture-refresh-signal/v1", ARCHCTX_CAPABILITIES_SCHEMA_VERSION = "archcontext.capabilities/v1", ARCHITECTURE_DOCS_RENDERER_VERSION = "archcontext.docs-renderer/v4", AGENT_CONTEXT_RENDERER_VERSION = "archcontext.agent-context-renderer/v1", PROJECTION_MODES, PROJECTION_TARGETS, ARCHITECTURE_MAJOR_CHANGE_REASON_CODES, ARCHCTX_FEATURES;
1124
+ var PROJECTION_REQUEST_SCHEMA_VERSION = "archcontext.projection-request/v1", PROJECTION_RESULT_SCHEMA_VERSION = "archcontext.projection-result/v2", PROJECTION_APPLY_IDENTITY_SCHEMA_VERSION = "archcontext.projection-apply-identity/v1", PROJECTION_APPLY_RECOVERY_BINDING_SCHEMA_VERSION = "archcontext.projection-apply-recovery-binding/v1", PROJECTION_APPLY_RECOVERY_INTENT_SCHEMA_VERSION = "archcontext.projection-apply-recovery-intent/v1", PROJECTION_APPLY_RECOVERY_PROOF_SCHEMA_VERSION = "archcontext.projection-apply-recovery-proof/v1", PROJECTION_APPLY_RECOVERY_RESULT_SCHEMA_VERSION = "archcontext.projection-apply-recovery-result/v1", ARCHITECTURE_REFRESH_SIGNAL_SCHEMA_VERSION = "archcontext.architecture-refresh-signal/v1", ARCHCTX_CAPABILITIES_SCHEMA_VERSION = "archcontext.capabilities/v1", ARCHITECTURE_DOCS_RENDERER_VERSION = "archcontext.docs-renderer/v4", AGENT_CONTEXT_RENDERER_VERSION = "archcontext.agent-context-renderer/v1", PROJECTION_MODES, PROJECTION_TARGETS, ARCHITECTURE_MAJOR_CHANGE_REASON_CODES, ARCHCTX_FEATURES;
1010
1125
  var init_projection = __esm(() => {
1011
1126
  init_schema();
1012
1127
  PROJECTION_MODES = ["check", "plan", "apply", "adopt"];
@@ -1030,6 +1145,7 @@ var init_projection = __esm(() => {
1030
1145
  "architecture-docs-renderer-v2",
1031
1146
  "architecture-refresh-signal-v1",
1032
1147
  "projection-apply-receipt-v1",
1148
+ "projection-apply-recovery-v1",
1033
1149
  "projection-protocol-v2"
1034
1150
  ];
1035
1151
  });
@@ -1088,7 +1204,7 @@ function productVersionManifest() {
1088
1204
  }
1089
1205
  };
1090
1206
  }
1091
- var ARCHCONTEXT_PRODUCT_NAME = "archctx", ARCHCONTEXT_PRODUCT_VERSION = "0.4.6", ARCHCONTEXT_PACKAGE_MANAGER = "bun@1.3.10", ARCHCONTEXT_NODE_RANGE = ">=24 <26", LOCAL_RUNTIME_RPC_SCHEMA_VERSION = "archcontext.runtime-rpc/v1", ARCHCONTEXT_SCHEMA_SET_VERSION = "2026-07-11.explorer-view-v2";
1207
+ var ARCHCONTEXT_PRODUCT_NAME = "archctx", ARCHCONTEXT_PRODUCT_VERSION = "0.4.8", ARCHCONTEXT_PACKAGE_MANAGER = "bun@1.4.0", ARCHCONTEXT_NODE_RANGE = ">=22.22 <26", LOCAL_RUNTIME_RPC_SCHEMA_VERSION = "archcontext.runtime-rpc/v1", ARCHCONTEXT_SCHEMA_SET_VERSION = "2026-07-11.explorer-view-v2";
1092
1208
 
1093
1209
  // packages/contracts/src/validator.ts
1094
1210
  function validateJsonSchema(schema, value) {
@@ -1837,7 +1953,7 @@ var init_src2 = __esm(() => {
1837
1953
 
1838
1954
  // node_modules/.bun/@colbymchenry+codegraph@1.5.0/node_modules/@colbymchenry/codegraph/npm-sdk.js
1839
1955
  var require_npm_sdk = __commonJS(function(exports, module) {
1840
- var __dirname = "/Users/ancienttwo/Projects/arch-context-wt-projection-proof-apply-reconcile/node_modules/.bun/@colbymchenry+codegraph@1.5.0/node_modules/@colbymchenry/codegraph";
1956
+ var __dirname = "/Users/ancienttwo/Projects/arch-context-wt-release-048/node_modules/.bun/@colbymchenry+codegraph@1.5.0/node_modules/@colbymchenry/codegraph";
1841
1957
  var path = __require("path");
1842
1958
  var os = __require("os");
1843
1959
  var fs = __require("fs");
@@ -5623,7 +5739,7 @@ var init_src7 = __esm(() => {
5623
5739
 
5624
5740
  // packages/surfaces/cli/src/main.ts
5625
5741
  import { execFileSync as execFileSync8, spawn as spawn2, spawnSync as spawnSync3 } from "child_process";
5626
- import { accessSync as accessSync3, chmodSync as chmodSync5, closeSync as closeSync8, constants, existsSync as existsSync17, mkdirSync as mkdirSync9, openSync as openSync8, readFileSync as readFileSync16, rmSync as rmSync10, statSync as statSync11, writeFileSync as writeFileSync8 } from "fs";
5742
+ import { accessSync as accessSync3, chmodSync as chmodSync6, closeSync as closeSync8, constants, existsSync as existsSync17, mkdirSync as mkdirSync9, openSync as openSync8, readFileSync as readFileSync16, rmSync as rmSync10, statSync as statSync11, writeFileSync as writeFileSync8 } from "fs";
5627
5743
  import { dirname as dirname11, join as join10, resolve as resolve19 } from "path";
5628
5744
  import { fileURLToPath as fileURLToPath2 } from "url";
5629
5745
 
@@ -8339,17 +8455,15 @@ function stickyArchitectureDocumentationProjectionProvenance(current, existingMa
8339
8455
  if (!prior)
8340
8456
  return current;
8341
8457
  assertArchitectureDocumentationProjectionProvenance(prior, current.rendererVersion);
8342
- return architectureDocumentationSemanticProvenanceDigest(prior) === architectureDocumentationSemanticProvenanceDigest(current) ? prior : current;
8458
+ return architectureDocumentationStickyProvenanceDigest(prior) === architectureDocumentationStickyProvenanceDigest(current) ? prior : current;
8343
8459
  } catch {
8344
8460
  return current;
8345
8461
  }
8346
8462
  }
8347
- function architectureDocumentationSemanticProvenanceDigest(provenance) {
8463
+ function architectureDocumentationStickyProvenanceDigest(provenance) {
8348
8464
  return digestJson({
8349
8465
  sourceTreeDigest: provenance.sourceTreeDigest,
8350
8466
  modelDigest: provenance.modelDigest,
8351
- codeGraphDigest: provenance.codeGraphDigest,
8352
- indexedWorktreeDigest: provenance.indexedWorktreeDigest,
8353
8467
  rendererVersion: provenance.rendererVersion,
8354
8468
  layoutVersion: provenance.layoutVersion,
8355
8469
  generatedFrom: provenance.generatedFrom
@@ -10397,13 +10511,21 @@ function diagnostics() {
10397
10511
  const egress = localEgressStatus();
10398
10512
  return {
10399
10513
  node: process.version,
10400
- supportedNode: /^v(24|25)\./.test(process.version),
10514
+ supportedNode: isSupportedNodeVersion(process.version),
10401
10515
  codeGraphVersion: REQUIRED_CODEGRAPH_VERSION,
10402
10516
  privacyRouteDigest: controlPlaneRouteDigest(),
10403
10517
  secureDefaults: secureDefaults(),
10404
10518
  egress
10405
10519
  };
10406
10520
  }
10521
+ function isSupportedNodeVersion(version) {
10522
+ const match = /^v?(\d+)\.(\d+)\.(\d+)$/.exec(version);
10523
+ if (!match)
10524
+ return false;
10525
+ const major = Number(match[1]);
10526
+ const minor = Number(match[2]);
10527
+ return major < 26 && (major > 22 || major === 22 && minor >= 22);
10528
+ }
10407
10529
  function secureDefaults() {
10408
10530
  return {
10409
10531
  tunnelEnabledByDefault: false,
@@ -10449,8 +10571,9 @@ function uninstallMarker(content, host) {
10449
10571
  function dependencyAudit(root) {
10450
10572
  const packageJson = JSON.parse(readFileSync6(resolve6(root, "package.json"), "utf8"));
10451
10573
  const issues = [];
10452
- if (!packageJson.engines?.node?.includes(">=24"))
10453
- issues.push("node engine must include >=24");
10574
+ if (packageJson.engines?.node !== ARCHCONTEXT_NODE_RANGE) {
10575
+ issues.push(`node engine must equal ${ARCHCONTEXT_NODE_RANGE}`);
10576
+ }
10454
10577
  return { ok: issues.length === 0, issues };
10455
10578
  }
10456
10579
  function secretScan(root) {
@@ -13950,6 +14073,12 @@ var LOCAL_SQLITE_MIGRATIONS = [
13950
14073
  )`,
13951
14074
  "CREATE INDEX IF NOT EXISTS idx_projection_apply_receipts_journal ON projection_apply_receipts(journal_id)"
13952
14075
  ]
14076
+ },
14077
+ {
14078
+ id: "0020_projection_apply_recovery_proof",
14079
+ statements: [
14080
+ "ALTER TABLE projection_apply_receipts ADD COLUMN recovery_proof_json TEXT"
14081
+ ]
13953
14082
  }
13954
14083
  ];
13955
14084
  var ARCHCONTEXT_STATE_DIR_ENV = "ARCHCONTEXT_STATE_DIR";
@@ -14119,7 +14248,6 @@ function recoverRuntimeStateTarget(input) {
14119
14248
  ensurePrivateDir(stagingDir);
14120
14249
  assertRuntimeStateRecoveryPrivatePermissions(stagingDir, 448);
14121
14250
  migrateSqliteDatabaseSync(stagingPath);
14122
- compactSqliteDatabase(stagingPath);
14123
14251
  const stagingIntegrity = assertCurrentLocalStore(stagingPath);
14124
14252
  receiptPath = join2(quarantineDirectory, RUNTIME_STATE_RECOVERY_RECEIPT_FILE);
14125
14253
  writeRuntimeStateRecoveryReceipt(receiptPath, {
@@ -14248,7 +14376,6 @@ function migrateLegacyLocalStoreIfNeeded(root = process.cwd(), env = process.env
14248
14376
  integrityCheck.legacy = vacuumLegacySqliteInto(paths.legacyLocalStorePath, stagingPath);
14249
14377
  makePrivateFile(stagingPath);
14250
14378
  migrateSqliteDatabaseSync(stagingPath);
14251
- compactSqliteDatabase(stagingPath);
14252
14379
  integrityCheck.staging = assertCurrentLocalStore(stagingPath);
14253
14380
  publishStagedLocalStore(stagingPath, paths.localStorePath);
14254
14381
  integrityCheck.target = assertCurrentLocalStore(paths.localStorePath);
@@ -14269,10 +14396,17 @@ function migrateLegacyLocalStoreIfNeeded(root = process.cwd(), env = process.env
14269
14396
  function upgradeExistingLocalStoreTarget(paths, integrityCheck) {
14270
14397
  const lock = acquireLegacyMigrationLock(paths);
14271
14398
  try {
14272
- assertUpgradeableLocalStoreTarget(paths.localStorePath);
14273
- integrityCheck.target = assertSqliteIntegrity(paths.localStorePath);
14274
- migrateSqliteDatabaseSync(paths.localStorePath);
14275
- compactSqliteDatabase(paths.localStorePath);
14399
+ const db = openSqliteDatabaseSync(paths.localStorePath);
14400
+ let compacted = false;
14401
+ try {
14402
+ integrityCheck.target = assertUpgradeableLocalStoreTarget(db, paths.localStorePath);
14403
+ migrateOpenedSqliteDatabase(db);
14404
+ compacted = true;
14405
+ } finally {
14406
+ db.close();
14407
+ }
14408
+ if (compacted)
14409
+ removeSqliteSidecars(paths.localStorePath);
14276
14410
  integrityCheck.target = assertCurrentLocalStore(paths.localStorePath);
14277
14411
  delete integrityCheck.error;
14278
14412
  const markerPath = writeLegacyMigrationMarker(paths, integrityCheck, []);
@@ -14900,30 +15034,41 @@ function readAppliedLocalSqliteMigrations(db) {
14900
15034
  return new Set;
14901
15035
  return new Set(db.prepare("SELECT id FROM schema_migrations").all().map((row) => String(row.id)));
14902
15036
  }
14903
- function openSqliteDatabaseSync(databasePath) {
14904
- if (databasePath !== ":memory:")
14905
- ensurePrivateDir(dirname2(databasePath));
14906
- try {
14907
- const nodeSqlite = runtimeRequire("node:sqlite");
14908
- const db2 = new nodeSqlite.DatabaseSync(databasePath);
14909
- return {
14910
- exec: (sql) => db2.exec(sql),
14911
- prepare: (sql) => db2.prepare(sql),
14912
- close: () => db2.close()
14913
- };
14914
- } catch (error) {
14915
- if (error.code !== "ERR_UNKNOWN_BUILTIN_MODULE" && error.code !== "MODULE_NOT_FOUND") {
14916
- throw error;
14917
- }
14918
- }
14919
- const bunSqlite = runtimeRequire("bun:sqlite");
15037
+ function selectSqliteRuntime() {
15038
+ if (process.versions.bun)
15039
+ return "bun";
15040
+ if (process.versions.node)
15041
+ return "node";
15042
+ throw new Error("Unsupported SQLite runtime: archctx requires the Bun or Node runtime, but neither process.versions.bun nor process.versions.node is set.");
15043
+ }
15044
+ function adaptBunSqliteDatabase(bunSqlite, databasePath) {
14920
15045
  const db = new bunSqlite.Database(databasePath);
15046
+ const releaseStatements = () => db.clearQueryCache();
14921
15047
  return {
14922
15048
  exec: (sql) => db.exec(sql),
14923
15049
  prepare: (sql) => db.query(sql),
15050
+ releaseStatements,
15051
+ close: () => {
15052
+ releaseStatements();
15053
+ db.close();
15054
+ }
15055
+ };
15056
+ }
15057
+ function adaptNodeSqliteDatabase(nodeSqlite, databasePath) {
15058
+ const db = new nodeSqlite.DatabaseSync(databasePath);
15059
+ return {
15060
+ exec: (sql) => db.exec(sql),
15061
+ prepare: (sql) => db.prepare(sql),
14924
15062
  close: () => db.close()
14925
15063
  };
14926
15064
  }
15065
+ function openSqliteDatabaseSync(databasePath) {
15066
+ if (databasePath !== ":memory:")
15067
+ ensurePrivateDir(dirname2(databasePath));
15068
+ if (selectSqliteRuntime() === "bun")
15069
+ return adaptBunSqliteDatabase(runtimeRequire("bun:sqlite"), databasePath);
15070
+ return adaptNodeSqliteDatabase(runtimeRequire("node:sqlite"), databasePath);
15071
+ }
14927
15072
  function legacyMigrationResult(migrated, skippedReason, paths, copiedFiles, details) {
14928
15073
  return {
14929
15074
  schemaVersion: "archcontext.legacy-local-store-migration/v1",
@@ -14970,7 +15115,6 @@ function runtimeStateRecoveryStartupProbe(paths, sourceFiles) {
14970
15115
  makePrivateFile(target);
14971
15116
  }
14972
15117
  migrateSqliteDatabaseSync(probePath);
14973
- compactSqliteDatabase(probePath);
14974
15118
  assertCurrentLocalStore(probePath);
14975
15119
  return { ok: true };
14976
15120
  } catch (error) {
@@ -15185,20 +15329,16 @@ function assertCurrentLocalStoreSchema(db, path) {
15185
15329
  throw new Error(`SQLite local store schema incomplete for ${path}: missing migrations ${missingMigrations.join(", ")}`);
15186
15330
  }
15187
15331
  }
15188
- function assertUpgradeableLocalStoreTarget(path) {
15189
- const db = openSqliteDatabaseSync(path);
15190
- try {
15191
- const integrity = sqliteIntegrityCheckOpenDatabase(db, path);
15192
- const tables = new Set(db.prepare("SELECT name FROM sqlite_master WHERE type = 'table'").all().map((row) => String(row.name)));
15193
- const hasArchContextMarker = ["schema_migrations", "task_states", "repository_sessions", "snapshots"].some((table) => tables.has(table));
15194
- if (!hasArchContextMarker) {
15195
- throw new Error(`SQLite target is not an ArchContext local store candidate: ${path}`);
15196
- }
15197
- if (integrity !== "ok")
15198
- throw new Error(`SQLite integrity_check failed for ${path}: ${integrity}`);
15199
- } finally {
15200
- db.close();
15332
+ function assertUpgradeableLocalStoreTarget(db, path) {
15333
+ const integrity = sqliteIntegrityCheckOpenDatabase(db, path);
15334
+ const tables = new Set(db.prepare("SELECT name FROM sqlite_master WHERE type = 'table'").all().map((row) => String(row.name)));
15335
+ const hasArchContextMarker = ["schema_migrations", "task_states", "repository_sessions", "snapshots"].some((table) => tables.has(table));
15336
+ if (!hasArchContextMarker) {
15337
+ throw new Error(`SQLite target is not an ArchContext local store candidate: ${path}`);
15201
15338
  }
15339
+ if (integrity !== "ok")
15340
+ throw new Error(`SQLite integrity_check failed for ${path}: ${integrity}`);
15341
+ return integrity;
15202
15342
  }
15203
15343
  function assertTrustedLegacyLocalStoreSource(paths) {
15204
15344
  const stat = lstatSync2(paths.legacyLocalStorePath);
@@ -15227,23 +15367,29 @@ function vacuumLegacySqliteInto(sourcePath, targetPath) {
15227
15367
  }
15228
15368
  function migrateSqliteDatabaseSync(databasePath) {
15229
15369
  const db = openSqliteDatabaseSync(databasePath);
15370
+ let compacted = false;
15230
15371
  try {
15231
- applyLocalSqliteMigrations(db);
15232
- backfillArchitectureEventDirectScope(db);
15233
- backfillArchitectureChangeFeed(db);
15372
+ migrateOpenedSqliteDatabase(db);
15373
+ compacted = true;
15234
15374
  } finally {
15235
15375
  db.close();
15236
15376
  }
15377
+ if (compacted)
15378
+ removeSqliteSidecars(databasePath);
15237
15379
  }
15238
- function compactSqliteDatabase(databasePath) {
15239
- const db = openSqliteDatabaseSync(databasePath);
15240
- try {
15241
- db.exec("PRAGMA busy_timeout = 5000");
15242
- db.exec("PRAGMA wal_checkpoint(TRUNCATE)");
15243
- db.exec("PRAGMA journal_mode = DELETE");
15244
- } finally {
15245
- db.close();
15246
- }
15380
+ function migrateOpenedSqliteDatabase(db) {
15381
+ applyLocalSqliteMigrations(db);
15382
+ backfillArchitectureEventDirectScope(db);
15383
+ backfillArchitectureChangeFeed(db);
15384
+ compactMigratedSqliteDatabase(db);
15385
+ }
15386
+ function compactMigratedSqliteDatabase(db) {
15387
+ db.exec("PRAGMA busy_timeout = 5000");
15388
+ db.releaseStatements?.();
15389
+ db.exec("PRAGMA wal_checkpoint(TRUNCATE)");
15390
+ db.exec("PRAGMA journal_mode = DELETE");
15391
+ }
15392
+ function removeSqliteSidecars(databasePath) {
15247
15393
  for (const suffix of ["-wal", "-shm"])
15248
15394
  rmSync(`${databasePath}${suffix}`, { force: true });
15249
15395
  }
@@ -15930,15 +16076,15 @@ function runCodeGraphCli2(binary, workspaceRoot, args) {
15930
16076
  // packages/local-runtime/runtime-daemon/src/index.ts
15931
16077
  import { randomBytes } from "node:crypto";
15932
16078
  import { execFileSync as execFileSync7 } from "node:child_process";
15933
- import { chmodSync as chmodSync3, closeSync as closeSync6, existsSync as existsSync15, mkdirSync as mkdirSync7, mkdtempSync as mkdtempSync4, openSync as openSync6, readdirSync as readdirSync9, readFileSync as readFileSync14, rmSync as rmSync8, statSync as statSync9, writeFileSync as writeFileSync6 } from "node:fs";
16079
+ import { chmodSync as chmodSync4, closeSync as closeSync6, existsSync as existsSync15, lstatSync as lstatSync7, mkdirSync as mkdirSync7, mkdtempSync as mkdtempSync4, openSync as openSync6, readdirSync as readdirSync9, readFileSync as readFileSync14, rmSync as rmSync8, statSync as statSync9, writeFileSync as writeFileSync6 } from "node:fs";
15934
16080
  import { createServer } from "node:http";
15935
16081
  import { tmpdir as tmpdir3 } from "node:os";
15936
- import { dirname as dirname10, join as join9, resolve as resolve18 } from "node:path";
16082
+ import { basename as basename6, dirname as dirname10, join as join9, resolve as resolve18, sep as sep7 } from "node:path";
15937
16083
 
15938
16084
  // packages/core/changeset-engine/src/index.ts
15939
16085
  init_src();
15940
- import { closeSync as closeSync4, existsSync as existsSync8, fsyncSync as fsyncSync2, lstatSync as lstatSync3, mkdirSync as mkdirSync3, openSync as openSync4, readFileSync as readFileSync9, renameSync as renameSync2, rmSync as rmSync3, writeFileSync as writeFileSync2 } from "node:fs";
15941
- import { dirname as dirname5, resolve as resolve11 } from "node:path";
16086
+ import { chmodSync as chmodSync2, closeSync as closeSync4, existsSync as existsSync8, fsyncSync as fsyncSync2, lstatSync as lstatSync3, mkdirSync as mkdirSync3, openSync as openSync4, readFileSync as readFileSync9, renameSync as renameSync2, rmSync as rmSync3, writeFileSync as writeFileSync2 } from "node:fs";
16087
+ import { dirname as dirname5, isAbsolute as isAbsolute5, relative as relative6, resolve as resolve11, sep as sep4 } from "node:path";
15942
16088
 
15943
16089
  // packages/core/policy-engine/src/index.ts
15944
16090
  import { existsSync as existsSync7, realpathSync as realpathSync6 } from "node:fs";
@@ -16209,14 +16355,57 @@ function assertSafeTarget(root, path, scope) {
16209
16355
  throw new Error(`Refusing to write symlink target: ${path}`);
16210
16356
  }
16211
16357
  }
16358
+ function writeFileWithoutFollowingSymlinks(request) {
16359
+ const absoluteRoot = resolve11(request.root);
16360
+ const absolute = assertPathHasNoSymlinkSegments(request.root, request.path);
16361
+ const segments = relative6(absoluteRoot, absolute).split(sep4);
16362
+ if (existsSync8(absolute))
16363
+ assertExpectedHash(absolute, request.expectedHash);
16364
+ else if (request.expectedHash !== "missing")
16365
+ throw new Error(`Expected missing file hash for new path: ${request.path}`);
16366
+ mkdirSync3(dirname5(absolute), { recursive: true });
16367
+ assertNoSymlinkSegments2(absoluteRoot, segments, request.path);
16368
+ atomicWriteFile(absolute, `${absolute}.archctx-tmp-${process.pid}-${nextNoFollowWriteSequence()}`, request.body, request.mode);
16369
+ }
16370
+ function assertPathHasNoSymlinkSegments(root, path) {
16371
+ const absoluteRoot = resolve11(root);
16372
+ const absolute = resolve11(absoluteRoot, path);
16373
+ const contained = relative6(absoluteRoot, absolute);
16374
+ if (contained === "" || contained === ".." || contained.startsWith(`..${sep4}`) || isAbsolute5(contained)) {
16375
+ throw new Error(`Path escapes repository: ${path}`);
16376
+ }
16377
+ assertNoSymlinkSegments2(absoluteRoot, contained.split(sep4), path);
16378
+ return absolute;
16379
+ }
16380
+ var noFollowWriteSequence = 0;
16381
+ function nextNoFollowWriteSequence() {
16382
+ noFollowWriteSequence += 1;
16383
+ return noFollowWriteSequence;
16384
+ }
16385
+ function assertNoSymlinkSegments2(absoluteRoot, segments, displayPath) {
16386
+ let current = absoluteRoot;
16387
+ for (const segment of segments) {
16388
+ current = resolve11(current, segment);
16389
+ let stat;
16390
+ try {
16391
+ stat = lstatSync3(current);
16392
+ } catch {
16393
+ return;
16394
+ }
16395
+ if (stat.isSymbolicLink())
16396
+ throw new Error(`Refusing to write through symlink: ${displayPath}`);
16397
+ }
16398
+ }
16212
16399
  function assertExpectedHash(path, expectedHash) {
16213
16400
  const actual = digestJson({ body: readFileSync9(path, "utf8") });
16214
16401
  if (expectedHash !== actual)
16215
16402
  throw new Error(`Expected hash mismatch: ${path}`);
16216
16403
  }
16217
- function atomicWriteFile(path, tempPath, body) {
16404
+ function atomicWriteFile(path, tempPath, body, mode) {
16218
16405
  mkdirSync3(dirname5(path), { recursive: true });
16219
- writeFileSync2(tempPath, body, "utf8");
16406
+ writeFileSync2(tempPath, body, mode === undefined ? "utf8" : { encoding: "utf8", mode, flag: "wx" });
16407
+ if (mode !== undefined)
16408
+ chmodSync2(tempPath, mode);
16220
16409
  fsyncFile2(tempPath);
16221
16410
  renameSync2(tempPath, path);
16222
16411
  fsyncDirectory2(dirname5(path));
@@ -16502,7 +16691,7 @@ init_src();
16502
16691
  // packages/core/practice-catalog/src/index.ts
16503
16692
  init_src();
16504
16693
  import { existsSync as existsSync9, lstatSync as lstatSync4, readdirSync as readdirSync6, readFileSync as readFileSync10, realpathSync as realpathSync7 } from "node:fs";
16505
- import { dirname as dirname6, relative as relative6, resolve as resolve12, sep as sep4 } from "node:path";
16694
+ import { dirname as dirname6, relative as relative7, resolve as resolve12, sep as sep5 } from "node:path";
16506
16695
  import { fileURLToPath } from "node:url";
16507
16696
  var PRACTICE_CATALOG_VERSION = "2026.06.0";
16508
16697
  var BUILTIN_PRACTICE_ASSETS_DIR = resolve12(dirname6(fileURLToPath(import.meta.url)), "../assets");
@@ -16754,7 +16943,7 @@ function loadRepoOverlayAssets(root, errors) {
16754
16943
  return [];
16755
16944
  const out = [];
16756
16945
  for (const path of listDataFiles(overlayRoot, overlayRoot, errors)) {
16757
- const relativePath = `.archcontext/practices/${relative6(overlayRoot, path).split(sep4).join("/")}`;
16946
+ const relativePath = `.archcontext/practices/${relative7(overlayRoot, path).split(sep5).join("/")}`;
16758
16947
  try {
16759
16948
  assertRepoRelativePath(relativePath);
16760
16949
  assertRealChild(root, path);
@@ -17161,11 +17350,11 @@ function safeRealpath(path) {
17161
17350
  }
17162
17351
  }
17163
17352
  function isRealChild(rootReal, pathReal) {
17164
- const rel = relative6(rootReal, pathReal);
17165
- return rel === "" || !rel.startsWith("..") && !rel.includes(`..${sep4}`) && !resolve12(rel).startsWith("..");
17353
+ const rel = relative7(rootReal, pathReal);
17354
+ return rel === "" || !rel.startsWith("..") && !rel.includes(`..${sep5}`) && !resolve12(rel).startsWith("..");
17166
17355
  }
17167
17356
  function displayPath(path) {
17168
- return path.split(sep4).join("/");
17357
+ return path.split(sep5).join("/");
17169
17358
  }
17170
17359
  function isRecord2(value) {
17171
17360
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
@@ -17727,7 +17916,7 @@ function checkResult(input, result) {
17727
17916
  // packages/core/practice-engine/src/enforcement.ts
17728
17917
  init_src();
17729
17918
  import { existsSync as existsSync10, lstatSync as lstatSync5, readdirSync as readdirSync7, readFileSync as readFileSync11 } from "node:fs";
17730
- import { basename as basename5, join as join5, relative as relative7, resolve as resolve13, sep as sep5 } from "node:path";
17919
+ import { basename as basename5, join as join5, relative as relative8, resolve as resolve13, sep as sep6 } from "node:path";
17731
17920
  var ENFORCEMENT_RANK = { advisory: 0, checkpoint: 1, complete: 2 };
17732
17921
  var POLICY_MODES = new Set(["advisory", "active", "fail-open", "fail-closed"]);
17733
17922
  var DEFAULT_POLICY = {
@@ -18159,8 +18348,8 @@ function assertRepoPolicyFile(root, path, relativePath) {
18159
18348
  throw new Error(`practice-policy-symlink-denied: ${relativePath}`);
18160
18349
  const rootResolved = resolve13(root);
18161
18350
  const pathResolved = resolve13(path);
18162
- const rel = relative7(rootResolved, pathResolved);
18163
- if (rel === "" || rel.startsWith("..") || rel.includes(`..${sep5}`))
18351
+ const rel = relative8(rootResolved, pathResolved);
18352
+ if (rel === "" || rel.startsWith("..") || rel.includes(`..${sep6}`))
18164
18353
  throw new Error(`practice-policy-path-escape: ${relativePath}`);
18165
18354
  if (basename5(pathResolved).startsWith("."))
18166
18355
  throw new Error(`practice-policy-hidden-file-denied: ${relativePath}`);
@@ -18891,6 +19080,32 @@ function createInterventionProposal(input) {
18891
19080
  }
18892
19081
 
18893
19082
  // packages/core/context-compiler/src/index.ts
19083
+ var SELF_REFERENTIAL_CONTEXT_EXTENSION_KEYS = ["byteLength", "budgetExceeded", "digest"];
19084
+ function canonicalContextMeasurementForm(context) {
19085
+ const extensions = { ...context.extensions };
19086
+ for (const key of SELF_REFERENTIAL_CONTEXT_EXTENSION_KEYS)
19087
+ delete extensions[key];
19088
+ return { ...context, extensions };
19089
+ }
19090
+ function finalizeContextBudgetMetadata(context, maxBytes) {
19091
+ const canonical = canonicalContextMeasurementForm(context);
19092
+ const byteLength = Buffer.byteLength(JSON.stringify(canonical), "utf8");
19093
+ const withMetadata = {
19094
+ ...canonical,
19095
+ extensions: {
19096
+ ...canonical.extensions,
19097
+ byteLength,
19098
+ budgetExceeded: byteLength > maxBytes
19099
+ }
19100
+ };
19101
+ return {
19102
+ ...withMetadata,
19103
+ extensions: {
19104
+ ...withMetadata.extensions,
19105
+ digest: digestJson(withMetadata)
19106
+ }
19107
+ };
19108
+ }
18894
19109
  async function compileTaskContext(input) {
18895
19110
  const model = await input.modelStore.validateModel(input.workspace);
18896
19111
  const ledgerReadback = await input.architectureLedger?.queryForTask({
@@ -19101,7 +19316,6 @@ async function compileLandscapeTaskContext(input) {
19101
19316
  });
19102
19317
  }
19103
19318
  function finalizeContext(context, digests) {
19104
- const byteLength = Buffer.byteLength(JSON.stringify(context), "utf8");
19105
19319
  const withMetadata = {
19106
19320
  ...context,
19107
19321
  extensions: {
@@ -19117,18 +19331,10 @@ function finalizeContext(context, digests) {
19117
19331
  codeFactsMode: digests.codeFactsMode,
19118
19332
  landscapeDigest: digests.landscapeDigest,
19119
19333
  activeRepositories: digests.activeRepositories,
19120
- crossRepoRelations: digests.crossRepoRelations,
19121
- byteLength,
19122
- budgetExceeded: byteLength > digests.maxBytes
19123
- }
19124
- };
19125
- return {
19126
- ...withMetadata,
19127
- extensions: {
19128
- ...withMetadata.extensions,
19129
- digest: digestJson(withMetadata)
19334
+ crossRepoRelations: digests.crossRepoRelations
19130
19335
  }
19131
19336
  };
19337
+ return finalizeContextBudgetMetadata(withMetadata, digests.maxBytes);
19132
19338
  }
19133
19339
  function trimPracticeGuidance(guidance, maxMatches) {
19134
19340
  const matches2 = guidance.matches.slice(0, maxMatches);
@@ -21770,6 +21976,7 @@ function clampInteger(value, min, max) {
21770
21976
 
21771
21977
  // packages/local-runtime/runtime-daemon/src/index.ts
21772
21978
  init_src();
21979
+ init_src();
21773
21980
 
21774
21981
  // packages/local-runtime/git-adapter/src/index.ts
21775
21982
  import { execFileSync as execFileSync5, spawnSync as spawnSync2 } from "node:child_process";
@@ -22058,11 +22265,11 @@ function isGitWorktreeError(error) {
22058
22265
  // packages/local-runtime/local-store-sqlite/src/index.ts
22059
22266
  import { execFileSync as execFileSync6 } from "node:child_process";
22060
22267
  import { createHash as createHash9, randomUUID as randomUUID2 } from "node:crypto";
22061
- import { chmodSync as chmodSync2, closeSync as closeSync5, copyFileSync as copyFileSync2, existsSync as existsSync13, fsyncSync as fsyncSync3, lstatSync as lstatSync6, mkdirSync as mkdirSync5, openSync as openSync5, readFileSync as readFileSync12, readSync as readSync4, realpathSync as realpathSync8, renameSync as renameSync3, rmSync as rmSync5, statfsSync as statfsSync2, statSync as statSync8, writeFileSync as writeFileSync3 } from "node:fs";
22268
+ import { chmodSync as chmodSync3, closeSync as closeSync5, copyFileSync as copyFileSync2, existsSync as existsSync13, fsyncSync as fsyncSync3, lstatSync as lstatSync6, mkdirSync as mkdirSync5, openSync as openSync5, readFileSync as readFileSync12, readSync as readSync4, realpathSync as realpathSync8, renameSync as renameSync3, rmSync as rmSync5, statfsSync as statfsSync2, statSync as statSync8, writeFileSync as writeFileSync3 } from "node:fs";
22062
22269
  import { readdir, readFile } from "node:fs/promises";
22063
22270
  import { createRequire as createRequire5 } from "node:module";
22064
22271
  import { homedir as homedir2 } from "node:os";
22065
- import { dirname as dirname8, isAbsolute as isAbsolute5, join as join7, relative as relative8, resolve as resolve16 } from "node:path";
22272
+ import { dirname as dirname8, isAbsolute as isAbsolute6, join as join7, relative as relative9, resolve as resolve16 } from "node:path";
22066
22273
  init_src();
22067
22274
  var runtimeRequire2 = createRequire5(import.meta.url);
22068
22275
  var SQLITE_SIDECAR_SUFFIXES2 = ["", "-wal", "-shm"];
@@ -22878,6 +23085,12 @@ var LOCAL_SQLITE_MIGRATIONS2 = [
22878
23085
  )`,
22879
23086
  "CREATE INDEX IF NOT EXISTS idx_projection_apply_receipts_journal ON projection_apply_receipts(journal_id)"
22880
23087
  ]
23088
+ },
23089
+ {
23090
+ id: "0020_projection_apply_recovery_proof",
23091
+ statements: [
23092
+ "ALTER TABLE projection_apply_receipts ADD COLUMN recovery_proof_json TEXT"
23093
+ ]
22881
23094
  }
22882
23095
  ];
22883
23096
  var CHANGESET_STARTUP_CLEANUP_LIMIT = 100;
@@ -22975,7 +23188,6 @@ function migrateLegacyLocalStoreIfNeeded2(root = process.cwd(), env = process.en
22975
23188
  integrityCheck.legacy = vacuumLegacySqliteInto2(paths.legacyLocalStorePath, stagingPath);
22976
23189
  makePrivateFile2(stagingPath);
22977
23190
  migrateSqliteDatabaseSync2(stagingPath);
22978
- compactSqliteDatabase2(stagingPath);
22979
23191
  integrityCheck.staging = assertCurrentLocalStore2(stagingPath);
22980
23192
  publishStagedLocalStore2(stagingPath, paths.localStorePath);
22981
23193
  integrityCheck.target = assertCurrentLocalStore2(paths.localStorePath);
@@ -22996,10 +23208,17 @@ function migrateLegacyLocalStoreIfNeeded2(root = process.cwd(), env = process.en
22996
23208
  function upgradeExistingLocalStoreTarget2(paths, integrityCheck) {
22997
23209
  const lock = acquireLegacyMigrationLock2(paths);
22998
23210
  try {
22999
- assertUpgradeableLocalStoreTarget2(paths.localStorePath);
23000
- integrityCheck.target = assertSqliteIntegrity2(paths.localStorePath);
23001
- migrateSqliteDatabaseSync2(paths.localStorePath);
23002
- compactSqliteDatabase2(paths.localStorePath);
23211
+ const db = openSqliteDatabaseSync2(paths.localStorePath);
23212
+ let compacted = false;
23213
+ try {
23214
+ integrityCheck.target = assertUpgradeableLocalStoreTarget2(db, paths.localStorePath);
23215
+ migrateOpenedSqliteDatabase2(db);
23216
+ compacted = true;
23217
+ } finally {
23218
+ db.close();
23219
+ }
23220
+ if (compacted)
23221
+ removeSqliteSidecars2(paths.localStorePath);
23003
23222
  integrityCheck.target = assertCurrentLocalStore2(paths.localStorePath);
23004
23223
  delete integrityCheck.error;
23005
23224
  const markerPath = writeLegacyMigrationMarker2(paths, integrityCheck, []);
@@ -23082,6 +23301,14 @@ class SqliteLocalStore {
23082
23301
  updatedAt: String(row.updated_at)
23083
23302
  }));
23084
23303
  }
23304
+ async deleteRepositorySession(repositoryId) {
23305
+ const db = await this.database();
23306
+ const existing = db.prepare("SELECT repository_id FROM repository_sessions WHERE repository_id = ?").get(repositoryId);
23307
+ if (!existing)
23308
+ return false;
23309
+ db.prepare("DELETE FROM repository_sessions WHERE repository_id = ?").run(repositoryId);
23310
+ return true;
23311
+ }
23085
23312
  async enqueueRuntimeAgentJob(input) {
23086
23313
  if (input.job.status !== "queued")
23087
23314
  throw new Error("runtime-agent-job-enqueue-requires-queued-status");
@@ -23192,7 +23419,7 @@ class SqliteLocalStore {
23192
23419
  maxAttempts,
23193
23420
  debounceUntil: input.debounceUntil
23194
23421
  });
23195
- const inserted = runtimeAgentJobById(db, input.job.jobId);
23422
+ const inserted = runtimeAgentJobInScope(db, runtimeAgentJobScope(input.job), input.job.jobId);
23196
23423
  if (!inserted)
23197
23424
  throw new Error(`runtime-agent-job-insert-failed: ${input.job.jobId}`);
23198
23425
  const backpressure = maxQueuedJobs === undefined ? undefined : {
@@ -23276,21 +23503,15 @@ class SqliteLocalStore {
23276
23503
  const nextAttempt = record2.attemptCount + 1;
23277
23504
  if (nextAttempt > record2.maxAttempts) {
23278
23505
  const failed = runtimeAgentJobWithPatch(record2.job, { status: "failed", updatedAt: input.now });
23279
- db.prepare(`UPDATE runtime_job_queue
23280
- SET status = ?, job_json = ?, updated_at = ?, attempt_count = ?, lease_owner = NULL,
23281
- leased_at = NULL, lease_expires_at = NULL, last_error = ?, dead_lettered_at = ?
23282
- WHERE job_id = ?`).run("failed", stableJson2(failed), input.now, nextAttempt, "max-attempts-exhausted", input.now, record2.job.jobId);
23506
+ updateRuntimeAgentJobInScope(db, input, record2.job.jobId, `status = ?, job_json = ?, updated_at = ?, attempt_count = ?, lease_owner = NULL,
23507
+ leased_at = NULL, lease_expires_at = NULL, last_error = ?, dead_lettered_at = ?`, ["failed", stableJson2(failed), input.now, nextAttempt, "max-attempts-exhausted", input.now]);
23283
23508
  db.exec("COMMIT");
23284
23509
  return;
23285
23510
  }
23286
23511
  const running = runtimeAgentJobWithPatch(record2.job, { status: "running", updatedAt: input.now });
23287
- db.prepare(`UPDATE runtime_job_queue
23288
- SET status = ?, job_json = ?, updated_at = ?, attempt_count = ?, lease_owner = ?,
23289
- leased_at = ?, lease_expires_at = ?, last_error = NULL
23290
- WHERE job_id = ?`).run("running", stableJson2(running), input.now, nextAttempt, input.workerId, input.now, leaseExpiresAt, record2.job.jobId);
23291
- const claimed = runtimeAgentJobById(db, record2.job.jobId);
23292
- if (!claimed)
23293
- throw new Error(`runtime-agent-job-not-found: ${record2.job.jobId}`);
23512
+ updateRuntimeAgentJobInScope(db, input, record2.job.jobId, `status = ?, job_json = ?, updated_at = ?, attempt_count = ?, lease_owner = ?,
23513
+ leased_at = ?, lease_expires_at = ?, last_error = NULL`, ["running", stableJson2(running), input.now, nextAttempt, input.workerId, input.now, leaseExpiresAt]);
23514
+ const claimed = requireRuntimeAgentJobInScope(db, input, record2.job.jobId);
23294
23515
  db.exec("COMMIT");
23295
23516
  return claimed;
23296
23517
  } catch (error) {
@@ -23300,9 +23521,7 @@ class SqliteLocalStore {
23300
23521
  }
23301
23522
  async completeRuntimeAgentJob(input) {
23302
23523
  const db = await this.database();
23303
- const record2 = runtimeAgentJobById(db, input.jobId);
23304
- if (!record2)
23305
- throw new Error(`runtime-agent-job-not-found: ${input.jobId}`);
23524
+ const record2 = requireRuntimeAgentJobInScope(db, input, input.jobId);
23306
23525
  if (record2.job.status !== "running")
23307
23526
  throw new Error(`runtime-agent-job-complete-requires-running: ${input.jobId}`);
23308
23527
  if (input.workerId && record2.leaseOwner && record2.leaseOwner !== input.workerId) {
@@ -23315,52 +23534,31 @@ class SqliteLocalStore {
23315
23534
  outputDigest: input.outputDigest,
23316
23535
  runMetadata: input.runMetadata
23317
23536
  });
23318
- db.prepare(`UPDATE runtime_job_queue
23319
- SET status = ?, job_json = ?, updated_at = ?, output_digest = ?, lease_owner = NULL,
23320
- leased_at = NULL, lease_expires_at = NULL, last_error = ?, dead_lettered_at = COALESCE(?, dead_lettered_at)
23321
- WHERE job_id = ?`).run(input.status, stableJson2(job), input.now, input.outputDigest ?? record2.job.outputDigest ?? null, input.error ?? null, deadLetteredAt ?? null, input.jobId);
23322
- const updated = runtimeAgentJobById(db, input.jobId);
23323
- if (!updated)
23324
- throw new Error(`runtime-agent-job-not-found: ${input.jobId}`);
23325
- return updated;
23537
+ updateRuntimeAgentJobInScope(db, input, input.jobId, `status = ?, job_json = ?, updated_at = ?, output_digest = ?, lease_owner = NULL,
23538
+ leased_at = NULL, lease_expires_at = NULL, last_error = ?, dead_lettered_at = COALESCE(?, dead_lettered_at)`, [input.status, stableJson2(job), input.now, input.outputDigest ?? record2.job.outputDigest ?? null, input.error ?? null, deadLetteredAt ?? null]);
23539
+ return requireRuntimeAgentJobInScope(db, input, input.jobId);
23326
23540
  }
23327
23541
  async retryRuntimeAgentJob(input) {
23328
23542
  const db = await this.database();
23329
- const record2 = runtimeAgentJobById(db, input.jobId);
23330
- if (!record2)
23331
- throw new Error(`runtime-agent-job-not-found: ${input.jobId}`);
23543
+ const record2 = requireRuntimeAgentJobInScope(db, input, input.jobId);
23332
23544
  if (record2.attemptCount >= record2.maxAttempts) {
23333
23545
  const failed = runtimeAgentJobWithPatch(record2.job, { status: "failed", updatedAt: input.now });
23334
- db.prepare(`UPDATE runtime_job_queue
23335
- SET status = ?, job_json = ?, updated_at = ?, lease_owner = NULL,
23336
- leased_at = NULL, lease_expires_at = NULL, last_error = ?, dead_lettered_at = ?
23337
- WHERE job_id = ?`).run("failed", stableJson2(failed), input.now, input.reason ?? "max-attempts-exhausted", input.now, input.jobId);
23546
+ updateRuntimeAgentJobInScope(db, input, input.jobId, `status = ?, job_json = ?, updated_at = ?, lease_owner = NULL,
23547
+ leased_at = NULL, lease_expires_at = NULL, last_error = ?, dead_lettered_at = ?`, ["failed", stableJson2(failed), input.now, input.reason ?? "max-attempts-exhausted", input.now]);
23338
23548
  } else {
23339
23549
  const queued = runtimeAgentJobWithPatch(record2.job, { status: "queued", updatedAt: input.now });
23340
- db.prepare(`UPDATE runtime_job_queue
23341
- SET status = ?, job_json = ?, updated_at = ?, lease_owner = NULL,
23342
- leased_at = NULL, lease_expires_at = NULL, last_error = ?, dead_lettered_at = NULL
23343
- WHERE job_id = ?`).run("queued", stableJson2(queued), input.now, input.reason ?? null, input.jobId);
23344
- }
23345
- const updated = runtimeAgentJobById(db, input.jobId);
23346
- if (!updated)
23347
- throw new Error(`runtime-agent-job-not-found: ${input.jobId}`);
23348
- return updated;
23550
+ updateRuntimeAgentJobInScope(db, input, input.jobId, `status = ?, job_json = ?, updated_at = ?, lease_owner = NULL,
23551
+ leased_at = NULL, lease_expires_at = NULL, last_error = ?, dead_lettered_at = NULL`, ["queued", stableJson2(queued), input.now, input.reason ?? null]);
23552
+ }
23553
+ return requireRuntimeAgentJobInScope(db, input, input.jobId);
23349
23554
  }
23350
23555
  async cancelRuntimeAgentJob(input) {
23351
23556
  const db = await this.database();
23352
- const record2 = runtimeAgentJobById(db, input.jobId);
23353
- if (!record2)
23354
- throw new Error(`runtime-agent-job-not-found: ${input.jobId}`);
23557
+ const record2 = requireRuntimeAgentJobInScope(db, input, input.jobId);
23355
23558
  const job = runtimeAgentJobWithPatch(record2.job, { status: input.status, updatedAt: input.now });
23356
- db.prepare(`UPDATE runtime_job_queue
23357
- SET status = ?, job_json = ?, updated_at = ?, lease_owner = NULL,
23358
- leased_at = NULL, lease_expires_at = NULL, last_error = ?, superseded_by_job_id = COALESCE(?, superseded_by_job_id)
23359
- WHERE job_id = ?`).run(input.status, stableJson2(job), input.now, input.reason ?? null, input.supersededByJobId ?? null, input.jobId);
23360
- const updated = runtimeAgentJobById(db, input.jobId);
23361
- if (!updated)
23362
- throw new Error(`runtime-agent-job-not-found: ${input.jobId}`);
23363
- return updated;
23559
+ updateRuntimeAgentJobInScope(db, input, input.jobId, `status = ?, job_json = ?, updated_at = ?, lease_owner = NULL,
23560
+ leased_at = NULL, lease_expires_at = NULL, last_error = ?, superseded_by_job_id = COALESCE(?, superseded_by_job_id)`, [input.status, stableJson2(job), input.now, input.reason ?? null, input.supersededByJobId ?? null]);
23561
+ return requireRuntimeAgentJobInScope(db, input, input.jobId);
23364
23562
  }
23365
23563
  async cancelStaleRuntimeAgentJobs(input) {
23366
23564
  const db = await this.database();
@@ -23373,6 +23571,8 @@ class SqliteLocalStore {
23373
23571
  const cancelled = [];
23374
23572
  for (const record2 of staleRows) {
23375
23573
  cancelled.push(await this.cancelRuntimeAgentJob({
23574
+ repository: input.repository,
23575
+ worktree: input.worktree,
23376
23576
  jobId: record2.job.jobId,
23377
23577
  status: "expired",
23378
23578
  now: input.now,
@@ -23434,29 +23634,58 @@ class SqliteLocalStore {
23434
23634
  (lookup_key, apply_id, journal_id, receipt_json, created_at, updated_at)
23435
23635
  VALUES (?, ?, ?, ?, ?, ?)`).run(receipt.identity.lookupKey, receipt.identity.applyId, journalId, stableJson2(receipt), createdAt, createdAt);
23436
23636
  }
23437
- async consumeProjectionApplyReceipt(lookupKey) {
23637
+ async inspectProjectionApplyReceipt(lookupKey) {
23638
+ const db = await this.database();
23639
+ const row = db.prepare(`SELECT receipt.receipt_json, receipt.refresh_consumed_at, receipt.recovery_proof_json
23640
+ FROM projection_apply_receipts receipt
23641
+ JOIN changeset_journal journal ON journal.journal_id = receipt.journal_id
23642
+ WHERE receipt.lookup_key = ? AND journal.status = 'committed'`).get(lookupKey);
23643
+ if (!row?.receipt_json)
23644
+ return;
23645
+ const receipt = parseProjectionApplyReceipt(String(row.receipt_json));
23646
+ const recoveryProof = row.recovery_proof_json === null ? undefined : parseProjectionApplyRecoveryProof(String(row.recovery_proof_json), receipt);
23647
+ return {
23648
+ receipt,
23649
+ deliveryStatus: row.refresh_consumed_at === null ? "pending" : "delivered",
23650
+ ...recoveryProof ? { recoveryProof } : {}
23651
+ };
23652
+ }
23653
+ async consumeProjectionApplyReceiptRecovery(proof) {
23438
23654
  const db = await this.database();
23439
23655
  db.exec("BEGIN IMMEDIATE");
23440
23656
  try {
23441
- const row = db.prepare(`SELECT receipt.receipt_json, receipt.refresh_consumed_at
23657
+ const row = db.prepare(`SELECT receipt.receipt_json, receipt.refresh_consumed_at, receipt.recovery_proof_json
23442
23658
  FROM projection_apply_receipts receipt
23443
23659
  JOIN changeset_journal journal ON journal.journal_id = receipt.journal_id
23444
- WHERE receipt.lookup_key = ? AND journal.status = 'committed'`).get(lookupKey);
23660
+ WHERE receipt.lookup_key = ? AND journal.status = 'committed'`).get(proof.receipt.lookupKey);
23445
23661
  if (!row?.receipt_json) {
23446
23662
  db.exec("COMMIT");
23447
23663
  return;
23448
23664
  }
23449
- const receipt = JSON.parse(String(row.receipt_json));
23450
- const issues = projectionApplyReceiptInvariantIssues(receipt);
23451
- if (issues.length > 0)
23452
- throw new Error(`projection-apply-receipt-invalid: ${issues.join("; ")}`);
23453
- const refreshSignalsDelivered = row.refresh_consumed_at === null;
23454
- if (refreshSignalsDelivered) {
23455
- const consumedAt = nowIso2();
23456
- db.prepare("UPDATE projection_apply_receipts SET refresh_consumed_at = ?, updated_at = ? WHERE lookup_key = ? AND refresh_consumed_at IS NULL").run(consumedAt, consumedAt, lookupKey);
23665
+ const receipt = parseProjectionApplyReceipt(String(row.receipt_json));
23666
+ const proofIssues = projectionApplyRecoveryProofReceiptInvariantIssues(proof, receipt);
23667
+ if (proofIssues.length > 0)
23668
+ throw new Error(`projection-apply-recovery-proof-invalid: ${proofIssues.join("; ")}`);
23669
+ if (row.refresh_consumed_at !== null) {
23670
+ const stored = row.recovery_proof_json === null ? undefined : parseProjectionApplyRecoveryProof(String(row.recovery_proof_json), receipt);
23671
+ if (!stored)
23672
+ throw new Error("projection-apply-receipt-delivered-without-recovery-proof");
23673
+ db.exec("COMMIT");
23674
+ return {
23675
+ receipt,
23676
+ proof: { ...stored, deliveryStatus: "already-delivered" },
23677
+ refreshSignalsDelivered: false
23678
+ };
23457
23679
  }
23680
+ const deliveredProof = { ...proof, deliveryStatus: "delivered" };
23681
+ const updatedAt = nowIso2();
23682
+ const update = db.prepare(`UPDATE projection_apply_receipts
23683
+ SET refresh_consumed_at = ?, recovery_proof_json = ?, updated_at = ?
23684
+ WHERE lookup_key = ? AND apply_id = ? AND refresh_consumed_at IS NULL AND recovery_proof_json IS NULL`).run(updatedAt, stableJson2(deliveredProof), updatedAt, proof.receipt.lookupKey, proof.receipt.applyId);
23685
+ if (update.changes !== 1)
23686
+ throw new Error("projection-apply-recovery-consume-race");
23458
23687
  db.exec("COMMIT");
23459
- return { receipt, refreshSignalsDelivered };
23688
+ return { receipt, proof: deliveredProof, refreshSignalsDelivered: true };
23460
23689
  } catch (error) {
23461
23690
  db.exec("ROLLBACK");
23462
23691
  throw error;
@@ -26527,51 +26756,48 @@ async function rebuildDerivedLandscapeState(store, input) {
26527
26756
  digest: landscapeDigest(landscape, scopedRelations)
26528
26757
  };
26529
26758
  }
26530
- async function openSqliteDatabase(databasePath) {
26531
- if (databasePath !== ":memory:")
26532
- ensurePrivateDir2(dirname8(databasePath));
26533
- try {
26534
- const nodeSqlite = await import("node:sqlite");
26535
- const db = new nodeSqlite.DatabaseSync(databasePath);
26536
- return {
26537
- exec: (sql) => db.exec(sql),
26538
- prepare: (sql) => db.prepare(sql),
26539
- close: () => db.close()
26540
- };
26541
- } catch {
26542
- const bunSqlite = await import("bun:sqlite");
26543
- const db = new bunSqlite.Database(databasePath);
26544
- return {
26545
- exec: (sql) => db.exec(sql),
26546
- prepare: (sql) => db.query(sql),
26547
- close: () => db.close()
26548
- };
26549
- }
26759
+ function selectSqliteRuntime2() {
26760
+ if (process.versions.bun)
26761
+ return "bun";
26762
+ if (process.versions.node)
26763
+ return "node";
26764
+ throw new Error("Unsupported SQLite runtime: archctx requires the Bun or Node runtime, but neither process.versions.bun nor process.versions.node is set.");
26550
26765
  }
26551
- function openSqliteDatabaseSync2(databasePath) {
26552
- if (databasePath !== ":memory:")
26553
- ensurePrivateDir2(dirname8(databasePath));
26554
- try {
26555
- const nodeSqlite = runtimeRequire2("node:sqlite");
26556
- const db2 = new nodeSqlite.DatabaseSync(databasePath);
26557
- return {
26558
- exec: (sql) => db2.exec(sql),
26559
- prepare: (sql) => db2.prepare(sql),
26560
- close: () => db2.close()
26561
- };
26562
- } catch (error) {
26563
- if (error.code !== "ERR_UNKNOWN_BUILTIN_MODULE" && error.code !== "MODULE_NOT_FOUND") {
26564
- throw error;
26565
- }
26566
- }
26567
- const bunSqlite = runtimeRequire2("bun:sqlite");
26766
+ function adaptBunSqliteDatabase2(bunSqlite, databasePath) {
26568
26767
  const db = new bunSqlite.Database(databasePath);
26768
+ const releaseStatements = () => db.clearQueryCache();
26569
26769
  return {
26570
26770
  exec: (sql) => db.exec(sql),
26571
26771
  prepare: (sql) => db.query(sql),
26772
+ releaseStatements,
26773
+ close: () => {
26774
+ releaseStatements();
26775
+ db.close();
26776
+ }
26777
+ };
26778
+ }
26779
+ function adaptNodeSqliteDatabase2(nodeSqlite, databasePath) {
26780
+ const db = new nodeSqlite.DatabaseSync(databasePath);
26781
+ return {
26782
+ exec: (sql) => db.exec(sql),
26783
+ prepare: (sql) => db.prepare(sql),
26572
26784
  close: () => db.close()
26573
26785
  };
26574
26786
  }
26787
+ async function openSqliteDatabase(databasePath) {
26788
+ if (databasePath !== ":memory:")
26789
+ ensurePrivateDir2(dirname8(databasePath));
26790
+ if (selectSqliteRuntime2() === "bun")
26791
+ return adaptBunSqliteDatabase2(await import("bun:sqlite"), databasePath);
26792
+ return adaptNodeSqliteDatabase2(await import("node:sqlite"), databasePath);
26793
+ }
26794
+ function openSqliteDatabaseSync2(databasePath) {
26795
+ if (databasePath !== ":memory:")
26796
+ ensurePrivateDir2(dirname8(databasePath));
26797
+ if (selectSqliteRuntime2() === "bun")
26798
+ return adaptBunSqliteDatabase2(runtimeRequire2("bun:sqlite"), databasePath);
26799
+ return adaptNodeSqliteDatabase2(runtimeRequire2("node:sqlite"), databasePath);
26800
+ }
26575
26801
  function legacyMigrationResult2(migrated, skippedReason, paths, copiedFiles, details) {
26576
26802
  return {
26577
26803
  schemaVersion: "archcontext.legacy-local-store-migration/v1",
@@ -26623,20 +26849,16 @@ function assertCurrentLocalStoreSchema2(db, path) {
26623
26849
  throw new Error(`SQLite local store schema incomplete for ${path}: missing migrations ${missingMigrations.join(", ")}`);
26624
26850
  }
26625
26851
  }
26626
- function assertUpgradeableLocalStoreTarget2(path) {
26627
- const db = openSqliteDatabaseSync2(path);
26628
- try {
26629
- const integrity = sqliteIntegrityCheckOpenDatabase2(db, path);
26630
- const tables = new Set(db.prepare("SELECT name FROM sqlite_master WHERE type = 'table'").all().map((row) => String(row.name)));
26631
- const hasArchContextMarker = ["schema_migrations", "task_states", "repository_sessions", "snapshots"].some((table) => tables.has(table));
26632
- if (!hasArchContextMarker) {
26633
- throw new Error(`SQLite target is not an ArchContext local store candidate: ${path}`);
26634
- }
26635
- if (integrity !== "ok")
26636
- throw new Error(`SQLite integrity_check failed for ${path}: ${integrity}`);
26637
- } finally {
26638
- db.close();
26852
+ function assertUpgradeableLocalStoreTarget2(db, path) {
26853
+ const integrity = sqliteIntegrityCheckOpenDatabase2(db, path);
26854
+ const tables = new Set(db.prepare("SELECT name FROM sqlite_master WHERE type = 'table'").all().map((row) => String(row.name)));
26855
+ const hasArchContextMarker = ["schema_migrations", "task_states", "repository_sessions", "snapshots"].some((table) => tables.has(table));
26856
+ if (!hasArchContextMarker) {
26857
+ throw new Error(`SQLite target is not an ArchContext local store candidate: ${path}`);
26639
26858
  }
26859
+ if (integrity !== "ok")
26860
+ throw new Error(`SQLite integrity_check failed for ${path}: ${integrity}`);
26861
+ return integrity;
26640
26862
  }
26641
26863
  function assertTrustedLegacyLocalStoreSource2(paths) {
26642
26864
  const stat = lstatSync6(paths.legacyLocalStorePath);
@@ -26665,23 +26887,29 @@ function vacuumLegacySqliteInto2(sourcePath, targetPath) {
26665
26887
  }
26666
26888
  function migrateSqliteDatabaseSync2(databasePath) {
26667
26889
  const db = openSqliteDatabaseSync2(databasePath);
26890
+ let compacted = false;
26668
26891
  try {
26669
- applyLocalSqliteMigrations2(db);
26670
- backfillArchitectureEventDirectScope2(db);
26671
- backfillArchitectureChangeFeed2(db);
26892
+ migrateOpenedSqliteDatabase2(db);
26893
+ compacted = true;
26672
26894
  } finally {
26673
26895
  db.close();
26674
26896
  }
26897
+ if (compacted)
26898
+ removeSqliteSidecars2(databasePath);
26675
26899
  }
26676
- function compactSqliteDatabase2(databasePath) {
26677
- const db = openSqliteDatabaseSync2(databasePath);
26678
- try {
26679
- db.exec("PRAGMA busy_timeout = 5000");
26680
- db.exec("PRAGMA wal_checkpoint(TRUNCATE)");
26681
- db.exec("PRAGMA journal_mode = DELETE");
26682
- } finally {
26683
- db.close();
26684
- }
26900
+ function migrateOpenedSqliteDatabase2(db) {
26901
+ applyLocalSqliteMigrations2(db);
26902
+ backfillArchitectureEventDirectScope2(db);
26903
+ backfillArchitectureChangeFeed2(db);
26904
+ compactMigratedSqliteDatabase2(db);
26905
+ }
26906
+ function compactMigratedSqliteDatabase2(db) {
26907
+ db.exec("PRAGMA busy_timeout = 5000");
26908
+ db.releaseStatements?.();
26909
+ db.exec("PRAGMA wal_checkpoint(TRUNCATE)");
26910
+ db.exec("PRAGMA journal_mode = DELETE");
26911
+ }
26912
+ function removeSqliteSidecars2(databasePath) {
26685
26913
  for (const suffix of ["-wal", "-shm"])
26686
26914
  rmSync5(`${databasePath}${suffix}`, { force: true });
26687
26915
  }
@@ -26812,14 +27040,14 @@ function ensurePrivateDir2(path) {
26812
27040
  mkdirSync5(path, { recursive: true, mode: 448 });
26813
27041
  if (process.platform !== "win32") {
26814
27042
  try {
26815
- chmodSync2(path, 448);
27043
+ chmodSync3(path, 448);
26816
27044
  } catch {}
26817
27045
  }
26818
27046
  }
26819
27047
  function makePrivateFile2(path) {
26820
27048
  if (process.platform !== "win32") {
26821
27049
  try {
26822
- chmodSync2(path, 384);
27050
+ chmodSync3(path, 384);
26823
27051
  } catch {}
26824
27052
  }
26825
27053
  }
@@ -26836,7 +27064,7 @@ function readGitPath2(root, args) {
26836
27064
  }
26837
27065
  }
26838
27066
  function resolveMaybeRelative2(base, path) {
26839
- return isAbsolute5(path) ? resolve16(path) : resolve16(base, path);
27067
+ return isAbsolute6(path) ? resolve16(path) : resolve16(base, path);
26840
27068
  }
26841
27069
  function canonicalPath2(path) {
26842
27070
  const resolved = resolve16(path);
@@ -26849,8 +27077,8 @@ function canonicalPath2(path) {
26849
27077
  function isPathInsideOrSame2(path, parent) {
26850
27078
  const child = resolve16(path);
26851
27079
  const base = resolve16(parent);
26852
- const fromBase = relative8(base, child);
26853
- return fromBase === "" || !!fromBase && !fromBase.startsWith("..") && !isAbsolute5(fromBase);
27080
+ const fromBase = relative9(base, child);
27081
+ return fromBase === "" || !!fromBase && !fromBase.startsWith("..") && !isAbsolute6(fromBase);
26854
27082
  }
26855
27083
  function stableStorageId2(prefix, value) {
26856
27084
  return `${prefix}.${createHash9("sha256").update(value).digest("hex").slice(0, 16)}`;
@@ -26942,10 +27170,29 @@ function insertRuntimeAgentJob(db, input) {
26942
27170
  lease_expires_at, last_error, dead_lettered_at, debounce_until, superseded_by_job_id)
26943
27171
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, NULL, NULL, NULL, NULL, ?, NULL)`).run(input.job.jobId, input.job.repository.repositoryId, input.job.repository.storageRepositoryId, input.job.worktree.workspaceId, input.job.worktree.storageWorkspaceId, input.analysisKind, input.coalesceKey, input.priority, input.job.status, input.job.runnerPort, input.job.fingerprint, input.job.inputDigest, input.job.promptTemplateDigest, input.job.outputDigest ?? null, input.job.stalePolicy, stableJson2(input.job), input.job.queuedAt, input.job.updatedAt, 0, input.maxAttempts, input.debounceUntil ?? null);
26944
27172
  }
26945
- function runtimeAgentJobById(db, jobId) {
26946
- const row = db.prepare("SELECT * FROM runtime_job_queue WHERE job_id = ?").get(jobId);
27173
+ function runtimeAgentJobInScope(db, scope, jobId) {
27174
+ const row = db.prepare(`SELECT * FROM runtime_job_queue
27175
+ WHERE job_id = ?
27176
+ AND storage_repository_id = ?
27177
+ AND storage_workspace_id = ?`).get(jobId, scope.repository.storageRepositoryId, scope.worktree.storageWorkspaceId);
26947
27178
  return row ? runtimeAgentJobRecordFromRow(row) : undefined;
26948
27179
  }
27180
+ function requireRuntimeAgentJobInScope(db, scope, jobId) {
27181
+ const record2 = runtimeAgentJobInScope(db, scope, jobId);
27182
+ if (!record2)
27183
+ throw new Error(`runtime-agent-job-not-found-in-scope: ${jobId}`);
27184
+ return record2;
27185
+ }
27186
+ function updateRuntimeAgentJobInScope(db, scope, jobId, assignments, params) {
27187
+ db.prepare(`UPDATE runtime_job_queue
27188
+ SET ${assignments}
27189
+ WHERE job_id = ?
27190
+ AND storage_repository_id = ?
27191
+ AND storage_workspace_id = ?`).run(...params, jobId, scope.repository.storageRepositoryId, scope.worktree.storageWorkspaceId);
27192
+ }
27193
+ function runtimeAgentJobScope(job) {
27194
+ return { repository: job.repository, worktree: job.worktree };
27195
+ }
26949
27196
  function runtimeAgentJobRecordFromRow(row) {
26950
27197
  return {
26951
27198
  job: JSON.parse(String(row.job_json)),
@@ -26982,6 +27229,30 @@ function runtimeAgentJobWithPatch(job, patch) {
26982
27229
  function nullableString(value) {
26983
27230
  return typeof value === "string" && value.length > 0 ? value : undefined;
26984
27231
  }
27232
+ function parseProjectionApplyReceipt(raw) {
27233
+ let receipt;
27234
+ try {
27235
+ receipt = JSON.parse(raw);
27236
+ } catch (error) {
27237
+ throw new Error(`projection-apply-receipt-invalid-json: ${error instanceof Error ? error.message : String(error)}`);
27238
+ }
27239
+ const issues = projectionApplyReceiptInvariantIssues(receipt);
27240
+ if (issues.length > 0)
27241
+ throw new Error(`projection-apply-receipt-invalid: ${issues.join("; ")}`);
27242
+ return receipt;
27243
+ }
27244
+ function parseProjectionApplyRecoveryProof(raw, receipt) {
27245
+ let proof;
27246
+ try {
27247
+ proof = JSON.parse(raw);
27248
+ } catch (error) {
27249
+ throw new Error(`projection-apply-recovery-proof-invalid-json: ${error instanceof Error ? error.message : String(error)}`);
27250
+ }
27251
+ const issues = projectionApplyRecoveryProofReceiptInvariantIssues(proof, receipt);
27252
+ if (issues.length > 0)
27253
+ throw new Error(`projection-apply-recovery-proof-invalid: ${issues.join("; ")}`);
27254
+ return proof;
27255
+ }
26985
27256
  function nowIso2() {
26986
27257
  return new Date().toISOString();
26987
27258
  }
@@ -27641,13 +27912,13 @@ async function withGithubIssueBodyFile(body, fn, deps = {}) {
27641
27912
  rmSync7(dir, { recursive: true, force: true });
27642
27913
  }
27643
27914
  }
27644
- var SECRET_PATTERNS = [
27645
- /gh[opsu]_[A-Za-z0-9_]+/,
27646
- /Bearer\s+[A-Za-z0-9._-]+/i,
27647
- /-----BEGIN [A-Z ]*PRIVATE KEY-----/,
27648
- /GITHUB_WEBHOOK_SECRET/i,
27649
- /installation[_-]?token/i,
27650
- /jwt/i
27915
+ var SECRET_DETECTORS = [
27916
+ { id: "github-token-prefix", pattern: /gh[opsu]_[A-Za-z0-9_]+/ },
27917
+ { id: "bearer-credential", pattern: /Bearer\s+[A-Za-z0-9._-]+/i },
27918
+ { id: "private-key-header", pattern: /-----BEGIN [A-Z ]*PRIVATE KEY-----/ },
27919
+ { id: "github-webhook-secret", pattern: /GITHUB_WEBHOOK_SECRET/i },
27920
+ { id: "installation-token-value", pattern: /installation[_-]?token["']?\s*[:=]\s*["']?[A-Za-z0-9._-]{16,}/i },
27921
+ { id: "compact-jwt-value", pattern: /\beyJ[A-Za-z0-9_-]{6,}\.[A-Za-z0-9_-]{6,}\.[A-Za-z0-9_-]{6,}/ }
27651
27922
  ];
27652
27923
  var GITHUB_ISSUE_BODY_MAX_LENGTH = 65536;
27653
27924
  function githubIssueFooterMarker(runId, draftDigest) {
@@ -27665,11 +27936,19 @@ function preflightGithubIssueDrafts(runId, drafts) {
27665
27936
 
27666
27937
  ${footer}
27667
27938
  `;
27668
- const payload = [draft.title, body, ...draft.labels].join(`
27669
- `);
27670
- for (const pattern of SECRET_PATTERNS) {
27671
- if (pattern.test(payload)) {
27672
- return { ok: false, reason: `github issue draft ${draft.draftId} matched a secret-shaped pattern; publishing aborted for the entire run` };
27939
+ const scanned = [
27940
+ { field: "title", text: draft.title },
27941
+ { field: "body", text: body },
27942
+ ...draft.labels.map((label) => ({ field: "label", text: label }))
27943
+ ];
27944
+ for (const { field, text } of scanned) {
27945
+ for (const detector of SECRET_DETECTORS) {
27946
+ if (detector.pattern.test(text)) {
27947
+ return {
27948
+ ok: false,
27949
+ reason: `github issue draft ${draft.draftId} matched a secret-shaped pattern (detector ${detector.id}) in its ${field}; publishing aborted for the entire run`
27950
+ };
27951
+ }
27673
27952
  }
27674
27953
  }
27675
27954
  if (body.length > GITHUB_ISSUE_BODY_MAX_LENGTH) {
@@ -28491,6 +28770,8 @@ function auditGithubIssuesEnabledInManifestText(manifestText) {
28491
28770
  return false;
28492
28771
  }
28493
28772
  var RUNTIME_RPC_VERSION = LOCAL_RUNTIME_RPC_SCHEMA_VERSION;
28773
+ var RUNTIME_RPC_MAX_REQUEST_BODY_BYTES = 16 * 1024 * 1024;
28774
+ var RUNTIME_RPC_REQUEST_BODY_TIMEOUT_MS = 30000;
28494
28775
 
28495
28776
  class RuntimeUpdateInputError extends Error {
28496
28777
  }
@@ -29074,12 +29355,15 @@ class ArchctxDaemon {
29074
29355
  const scope = await this.architectureLedgerScope(repositoryRoot);
29075
29356
  const jobs = await this.localStore.listRuntimeAgentJobs(scope);
29076
29357
  const record2 = jobs.find((candidate) => candidate.job.jobId === input.jobId);
29077
- if (record2 && record2.job.status !== "running") {
29358
+ if (!record2)
29359
+ return runtimeAgentJobOutOfScopeEnvelope("jobs.complete", input.jobId);
29360
+ if (record2.job.status !== "running") {
29078
29361
  return errorEnvelope("jobs.complete", "AC_PRECONDITION_FAILED", `runtime agent job completion requires a running job: ${input.jobId}`);
29079
29362
  }
29080
29363
  if (input.status === "succeeded") {
29081
- if (record2 && record2.job.stalePolicy === "cancel-on-head-change" && isRuntimeAgentJobCursorStale(record2.job, scope)) {
29364
+ if (record2.job.stalePolicy === "cancel-on-head-change" && isRuntimeAgentJobCursorStale(record2.job, scope)) {
29082
29365
  await this.localStore.cancelRuntimeAgentJob({
29366
+ ...scope,
29083
29367
  jobId: input.jobId,
29084
29368
  status: "expired",
29085
29369
  now: input.now ?? this.clock(),
@@ -29091,7 +29375,7 @@ class ArchctxDaemon {
29091
29375
  if (input.proposalPlan) {
29092
29376
  const validation = validateRuntimeAgentProposalPlan({
29093
29377
  proposalPlan: input.proposalPlan,
29094
- job: record2?.job,
29378
+ job: record2.job,
29095
29379
  jobId: input.jobId,
29096
29380
  outputDigest: input.outputDigest
29097
29381
  });
@@ -29103,6 +29387,7 @@ class ArchctxDaemon {
29103
29387
  proposalPlan: input.proposalPlan
29104
29388
  } : input.runMetadata;
29105
29389
  const job = await this.localStore.completeRuntimeAgentJob({
29390
+ ...scope,
29106
29391
  jobId: input.jobId,
29107
29392
  status: input.status,
29108
29393
  workerId: input.workerId,
@@ -29115,8 +29400,12 @@ class ArchctxDaemon {
29115
29400
  }
29116
29401
  async jobsRetry(root, input) {
29117
29402
  this.assertRunning();
29118
- await this.architectureLedgerScope(findRepositoryRoot2(root));
29403
+ const scope = await this.architectureLedgerScope(findRepositoryRoot2(root));
29404
+ if (!await this.runtimeAgentJobInScope(scope, input.jobId)) {
29405
+ return runtimeAgentJobOutOfScopeEnvelope("jobs.retry", input.jobId);
29406
+ }
29119
29407
  const job = await this.localStore.retryRuntimeAgentJob({
29408
+ ...scope,
29120
29409
  jobId: input.jobId,
29121
29410
  reason: input.reason,
29122
29411
  now: input.now ?? this.clock()
@@ -29125,8 +29414,12 @@ class ArchctxDaemon {
29125
29414
  }
29126
29415
  async jobsCancel(root, input) {
29127
29416
  this.assertRunning();
29128
- await this.architectureLedgerScope(findRepositoryRoot2(root));
29417
+ const scope = await this.architectureLedgerScope(findRepositoryRoot2(root));
29418
+ if (!await this.runtimeAgentJobInScope(scope, input.jobId)) {
29419
+ return runtimeAgentJobOutOfScopeEnvelope("jobs.cancel", input.jobId);
29420
+ }
29129
29421
  const job = await this.localStore.cancelRuntimeAgentJob({
29422
+ ...scope,
29130
29423
  jobId: input.jobId,
29131
29424
  status: input.status ?? "cancelled",
29132
29425
  reason: input.reason,
@@ -29135,6 +29428,10 @@ class ArchctxDaemon {
29135
29428
  });
29136
29429
  return okEnvelope("jobs.cancel", { job });
29137
29430
  }
29431
+ async runtimeAgentJobInScope(scope, jobId) {
29432
+ const jobs = await this.localStore.listRuntimeAgentJobs(scope);
29433
+ return jobs.find((candidate) => candidate.job.jobId === jobId);
29434
+ }
29138
29435
  async auditRun(root, input = {}) {
29139
29436
  this.assertRunning();
29140
29437
  const repositoryRoot = findRepositoryRoot2(root);
@@ -29358,14 +29655,22 @@ class ArchctxDaemon {
29358
29655
  return typeof manifestRaw === "string" && auditGithubIssuesEnabledInManifestText(manifestRaw);
29359
29656
  }
29360
29657
  async canFileGithubIssues(root) {
29361
- const repoNameWithOwner = repositoryNameWithOwner(root);
29362
- if (repoNameWithOwner === "local/unknown") {
29658
+ const target = readGitRemoteTarget(root);
29659
+ if (!target) {
29363
29660
  return {
29364
29661
  ok: false,
29365
29662
  code: "AC_PRECONDITION_FAILED",
29366
29663
  message: "audit approve requires a resolvable GitHub owner/repo; git remote 'origin' is missing or is not a parseable GitHub URL"
29367
29664
  };
29368
29665
  }
29666
+ if (target.host !== SUPPORTED_GITHUB_REMOTE_HOST) {
29667
+ return {
29668
+ ok: false,
29669
+ code: "AC_PRECONDITION_FAILED",
29670
+ message: `audit approve supports ${SUPPORTED_GITHUB_REMOTE_HOST} remotes only (ADR-0042); git remote 'origin' resolves to host "${target.host}", and ${target.owner}/${target.repo} there is not the same repository as ${target.owner}/${target.repo} on ${SUPPORTED_GITHUB_REMOTE_HOST}`
29671
+ };
29672
+ }
29673
+ const repoNameWithOwner = `${target.owner}/${target.repo}`;
29369
29674
  const token = process.env[AUDIT_APPROVE_GH_TOKEN_ENV];
29370
29675
  if (!token) {
29371
29676
  return {
@@ -29393,7 +29698,7 @@ class ArchctxDaemon {
29393
29698
  message: `audit approve received an unrecognized visibility "${probedVisibility}" for ${repoNameWithOwner}; refusing to guess whether it is safe to publish`
29394
29699
  };
29395
29700
  }
29396
- return { ok: true, repoNameWithOwner, visibility, token };
29701
+ return { ok: true, host: target.host, repoNameWithOwner, visibility, token };
29397
29702
  }
29398
29703
  async auditList(root, input = {}) {
29399
29704
  this.assertRunning();
@@ -29468,9 +29773,9 @@ class ArchctxDaemon {
29468
29773
  const capability = await this.canFileGithubIssues(repositoryRoot);
29469
29774
  if (!capability.ok)
29470
29775
  return errorEnvelope("audit.approve", capability.code, capability.message);
29471
- const expectedConfirmToken = `public:${capability.repoNameWithOwner}:${run.baseSha}:${run.runId}`;
29776
+ const expectedConfirmToken = `public:${capability.host}/${capability.repoNameWithOwner}:${run.baseSha}:${run.runId}`;
29472
29777
  if (capability.visibility !== "private" && input.confirmPublicToken !== expectedConfirmToken) {
29473
- return errorEnvelope("audit.approve", "AC_USER_CONFIRMATION_REQUIRED", `audit run ${run.runId} targets a ${capability.visibility} repository (${capability.repoNameWithOwner}); rerun with explicit confirmation: archctx audit approve ${run.runId} --confirm-public-repo ${expectedConfirmToken}`);
29778
+ return errorEnvelope("audit.approve", "AC_USER_CONFIRMATION_REQUIRED", `audit run ${run.runId} targets a ${capability.visibility} repository (${capability.host}/${capability.repoNameWithOwner}); rerun with explicit confirmation: archctx audit approve ${run.runId} --confirm-public-repo ${expectedConfirmToken}`);
29474
29779
  }
29475
29780
  const preflight = preflightGithubIssueDrafts(run.runId, drafts);
29476
29781
  if (!preflight.ok)
@@ -29701,7 +30006,8 @@ class ArchctxDaemon {
29701
30006
  return errorEnvelope("docs.pin", "AC_SCHEMA_INVALID", "docs pin requires --library-id and --version");
29702
30007
  assertContext7LibraryId(input.libraryId);
29703
30008
  assertContext7Version(input.version);
29704
- const lock = upsertContext7Pin(readContext7Lockfile(session.workspace.root), {
30009
+ const current = readContext7LockfileState(session.workspace.root);
30010
+ const lock = upsertContext7Pin(current.lock, {
29705
30011
  libraryId: input.libraryId,
29706
30012
  version: input.version,
29707
30013
  pinnedAt: this.clock(),
@@ -29715,7 +30021,7 @@ class ArchctxDaemon {
29715
30021
  lock
29716
30022
  });
29717
30023
  }
29718
- writeContext7Lockfile(session.workspace.root, lock);
30024
+ writeContext7Lockfile(session.workspace.root, lock, current.expectedHash);
29719
30025
  return okEnvelope("docs.pin", {
29720
30026
  schemaVersion: "archcontext.context7-pin/v1",
29721
30027
  approved: true,
@@ -30057,12 +30363,55 @@ class ArchctxDaemon {
30057
30363
  });
30058
30364
  });
30059
30365
  }
30060
- async reconcileProjectionApply(root, lookupKey) {
30366
+ async inspectProjectionApplyReceipt(root, lookupKey) {
30367
+ this.assertRunning();
30368
+ await this.openSession(root);
30369
+ const inspection = await this.localStore.inspectProjectionApplyReceipt(lookupKey);
30370
+ return okEnvelope("projection.inspect-receipt", {
30371
+ found: inspection !== undefined,
30372
+ ...inspection ?? {}
30373
+ });
30374
+ }
30375
+ async recoverProjectionApply(root, intent) {
30061
30376
  this.assertRunning();
30377
+ const intentIssues = projectionApplyRecoveryIntentInvariantIssues(intent);
30378
+ if (intentIssues.length > 0) {
30379
+ return errorEnvelope("projection.recover", "AC_SCHEMA_INVALID", `projection recovery intent invariant failed: ${intentIssues.join("; ")}`);
30380
+ }
30062
30381
  return this.withWriter(async () => {
30063
- await this.openSession(root);
30064
- const consumption = await this.localStore.consumeProjectionApplyReceipt(lookupKey);
30065
- return okEnvelope("projection.reconcile", {
30382
+ const session = await this.openSession(root);
30383
+ const inspection = await this.localStore.inspectProjectionApplyReceipt(intent.receipt.lookupKey);
30384
+ if (!inspection || inspection.receipt.identity.applyId !== intent.receipt.applyId) {
30385
+ return errorEnvelope("projection.recover", "AC_PRECONDITION_FAILED", "committed projection apply receipt was not found");
30386
+ }
30387
+ if (inspection.deliveryStatus === "delivered") {
30388
+ if (!inspection.recoveryProof) {
30389
+ return errorEnvelope("projection.recover", "AC_PRECONDITION_FAILED", "committed projection receipt was delivered without a recovery proof");
30390
+ }
30391
+ return okEnvelope("projection.recover", {
30392
+ found: true,
30393
+ receipt: inspection.receipt,
30394
+ proof: { ...inspection.recoveryProof, deliveryStatus: "already-delivered" },
30395
+ refreshSignalsDelivered: false
30396
+ });
30397
+ }
30398
+ let fixedPoint;
30399
+ try {
30400
+ fixedPoint = buildRuntimeProjectionRecoveryFixedPoint(root);
30401
+ } catch (error) {
30402
+ return errorEnvelope("projection.recover", "AC_PRECONDITION_FAILED", error instanceof Error ? error.message : String(error));
30403
+ }
30404
+ const issues = runtimeProjectionRecoveryFixedPointIssues(inspection.receipt, fixedPoint);
30405
+ if (issues.length > 0) {
30406
+ return errorEnvelope("projection.recover", "AC_PRECONDITION_FAILED", `projection recovery proof failed: ${issues.join("; ")}`);
30407
+ }
30408
+ const proof = createRuntimeProjectionRecoveryProof(intent, inspection.receipt, fixedPoint);
30409
+ const currentWorktreeDigest = runtimeWorktreeDigest(root, "architecture-documentation-projection");
30410
+ if (session.workspace.repositoryId !== proof.current.snapshot.repositoryId || session.workspace.headSha !== proof.current.snapshot.headSha || currentWorktreeDigest !== proof.current.snapshot.worktreeDigest) {
30411
+ return errorEnvelope("projection.recover", "AC_PRECONDITION_FAILED", "projection recovery authority changed before delivery");
30412
+ }
30413
+ const consumption = await this.localStore.consumeProjectionApplyReceiptRecovery(proof);
30414
+ return okEnvelope("projection.recover", {
30066
30415
  found: consumption !== undefined,
30067
30416
  ...consumption ?? {}
30068
30417
  });
@@ -30131,7 +30480,6 @@ class ArchctxDaemon {
30131
30480
  await this.changeSetEngine.apply(root, draft, { approved: true });
30132
30481
  }
30133
30482
  async applyArchitectureProjectionRollbackChangeSet(root, projectedFiles, currentFiles, createdAt) {
30134
- const targetPaths = new Set(projectedFiles.map((file) => file.path));
30135
30483
  const backupBase = `.archcontext/backups/ledger-rollback/${safePathSegment(createdAt)}`;
30136
30484
  const backupRelativePath = uniqueBackupPath(root, backupBase);
30137
30485
  const manifestPath = `${backupRelativePath}/manifest.json`;
@@ -30140,7 +30488,7 @@ class ArchctxDaemon {
30140
30488
  path: backupRelativePath,
30141
30489
  manifestPath
30142
30490
  });
30143
- const removedPaths = currentFiles.filter((file) => !targetPaths.has(file.path)).map((file) => file.path);
30491
+ const removedPaths = obsoleteManagedProjectionPaths(currentFiles, projectedFiles);
30144
30492
  await this.applyArchitectureProjectionChangeSet(root, {
30145
30493
  id: `changeset.ledger-rollback-${shortDigest3(digestJson({ createdAt, projectionDigest: architectureLedgerProjectionDigest(projectedFiles) }))}`,
30146
30494
  files: [
@@ -30514,11 +30862,12 @@ class ArchctxDaemon {
30514
30862
  const scope = await this.architectureLedgerScope(root);
30515
30863
  const state = await this.localStore.readArchitectureLedgerState(scope);
30516
30864
  const projectedFiles = projectArchitectureLedgerStateToYamlFiles(state);
30865
+ const removedPaths = obsoleteManagedProjectionPaths(listModelFiles(root).filter((file) => isArchitectureLedgerManagedModelPath(file.path)), projectedFiles);
30517
30866
  if (writes) {
30518
30867
  await this.applyArchitectureProjectionChangeSet(root, {
30519
30868
  id: `changeset.ledger-project-${shortDigest3(architectureLedgerProjectionDigest(projectedFiles))}`,
30520
30869
  files: projectedFiles.map(({ path, body }) => ({ path, body })),
30521
- removedPaths: []
30870
+ removedPaths
30522
30871
  });
30523
30872
  }
30524
30873
  const drift = compareArchitectureLedgerStateToYaml({
@@ -30539,6 +30888,7 @@ class ArchctxDaemon {
30539
30888
  projectionDigest: architectureLedgerProjectionDigest(projectedFiles),
30540
30889
  graphDigest: architectureLedgerStateDigest(state),
30541
30890
  writtenPaths: writes ? projectedFiles.map((file) => file.path) : [],
30891
+ removedPaths: writes ? removedPaths : [],
30542
30892
  projectedFiles: writes ? undefined : projectedFiles,
30543
30893
  drift,
30544
30894
  reconcile
@@ -31015,12 +31365,21 @@ class ArchctxDaemon {
31015
31365
  const paths = createDeveloperReviewRunPaths({
31016
31366
  sourceRoot,
31017
31367
  challengeId: input.challenge.challengeId,
31018
- tempRoot: input.tempRoot,
31019
- stateDir: input.stateDir
31368
+ tempRoot: input.tempRoot
31020
31369
  });
31021
31370
  mkdirSync7(paths.stateDir, { recursive: true });
31022
31371
  mkdirSync7(paths.runRoot, { recursive: true });
31023
31372
  mkdirSync7(paths.worktreeTempRoot, { recursive: true });
31373
+ const createdAt = this.clock();
31374
+ writeDeveloperReviewRunOwnerMarker({
31375
+ runRoot: paths.runRoot,
31376
+ runId: paths.runId,
31377
+ challengeId: input.challenge.challengeId,
31378
+ stateDir: paths.stateDir,
31379
+ manifestPath: paths.manifestPath,
31380
+ lockPath: paths.lockPath,
31381
+ createdAt
31382
+ });
31024
31383
  const preparing = {
31025
31384
  schemaVersion: "archcontext.developer-review-run/v1",
31026
31385
  runId: paths.runId,
@@ -31032,7 +31391,7 @@ class ArchctxDaemon {
31032
31391
  manifestPath: paths.manifestPath,
31033
31392
  lockPath: paths.lockPath,
31034
31393
  pid: process.pid,
31035
- createdAt: this.clock(),
31394
+ createdAt,
31036
31395
  status: "preparing",
31037
31396
  codeGraphTemporaryState: {
31038
31397
  root: paths.runRoot,
@@ -31092,12 +31451,13 @@ class ArchctxDaemon {
31092
31451
  }
31093
31452
  }
31094
31453
  cleanupDeveloperReviewRun(run) {
31454
+ const targets = resolveOwnedDeveloperReviewRunTargets(run);
31095
31455
  const removed = [];
31096
31456
  const errors = [];
31097
- if (run.worktree) {
31457
+ if (targets.worktree) {
31098
31458
  try {
31099
- const hadWorktree = existsSync15(run.worktree.worktreeRoot);
31100
- removeDetachedReviewWorktree(run.worktree);
31459
+ const hadWorktree = existsSync15(targets.worktree.worktreeRoot);
31460
+ removeDetachedReviewWorktree(targets.worktree);
31101
31461
  if (hadWorktree)
31102
31462
  removed.push("worktree");
31103
31463
  } catch (error) {
@@ -31105,10 +31465,12 @@ class ArchctxDaemon {
31105
31465
  }
31106
31466
  }
31107
31467
  for (const [kind, path] of [
31108
- ["run-root", run.runRoot],
31109
- ["manifest", run.manifestPath],
31110
- ["lock", run.lockPath]
31468
+ ["run-root", targets.runRoot],
31469
+ ["manifest", targets.manifestPath],
31470
+ ["lock", targets.lockPath]
31111
31471
  ]) {
31472
+ if (!path)
31473
+ continue;
31112
31474
  try {
31113
31475
  const existed = existsSync15(path);
31114
31476
  removePathWithRetry(path);
@@ -31130,14 +31492,15 @@ class ArchctxDaemon {
31130
31492
  recoverDeveloperReviewRuns(input) {
31131
31493
  this.assertRunning();
31132
31494
  const sourceRoot = findRepositoryRoot2(input.repositoryRoot);
31133
- const stateDir = input.stateDir ? resolve18(input.stateDir) : defaultDeveloperReviewRunStateDir(sourceRoot);
31495
+ const stateDir = defaultDeveloperReviewRunStateDir(sourceRoot);
31134
31496
  const recovery = {
31135
31497
  schemaVersion: "archcontext.developer-review-run-recovery/v1",
31136
31498
  sourceRoot,
31137
31499
  stateDir,
31138
31500
  recovered: [],
31139
31501
  removedLocks: [],
31140
- skippedActive: []
31502
+ skippedActive: [],
31503
+ rejected: []
31141
31504
  };
31142
31505
  if (!existsSync15(stateDir))
31143
31506
  return recovery;
@@ -31145,21 +31508,39 @@ class ArchctxDaemon {
31145
31508
  if (!entry.endsWith(".json"))
31146
31509
  continue;
31147
31510
  const manifestPath = join9(stateDir, entry);
31511
+ const stats = lstatIfExists(manifestPath);
31512
+ if (!stats || !stats.isFile()) {
31513
+ recovery.rejected.push(`${entry}: not-a-regular-file`);
31514
+ continue;
31515
+ }
31148
31516
  const manifest = readDeveloperReviewRunManifest(manifestPath);
31149
31517
  if (!manifest) {
31150
31518
  rmSync8(manifestPath, { force: true });
31151
31519
  continue;
31152
31520
  }
31521
+ if (resolve18(manifest.manifestPath) !== manifestPath) {
31522
+ recovery.rejected.push(`${entry}: manifest-path-mismatch`);
31523
+ continue;
31524
+ }
31153
31525
  if (!input.force && isDeveloperReviewPidAlive(manifest.pid)) {
31154
31526
  recovery.skippedActive.push(manifest.runId);
31155
31527
  continue;
31156
31528
  }
31157
- recovery.recovered.push(this.cleanupDeveloperReviewRun(manifest));
31529
+ try {
31530
+ recovery.recovered.push(this.cleanupDeveloperReviewRun(manifest));
31531
+ } catch (error) {
31532
+ recovery.rejected.push(`${entry}: ${error instanceof Error ? error.message : String(error)}`);
31533
+ }
31158
31534
  }
31159
31535
  for (const entry of readdirSync9(stateDir).sort()) {
31160
31536
  if (!entry.endsWith(".lock"))
31161
31537
  continue;
31162
31538
  const lockPath = join9(stateDir, entry);
31539
+ const stats = lstatIfExists(lockPath);
31540
+ if (!stats || !stats.isFile()) {
31541
+ recovery.rejected.push(`${entry}: not-a-regular-file`);
31542
+ continue;
31543
+ }
31163
31544
  const lock = readJsonObject(lockPath);
31164
31545
  const pid = typeof lock?.pid === "number" ? lock.pid : undefined;
31165
31546
  const runId = typeof lock?.runId === "string" ? lock.runId : entry;
@@ -31321,16 +31702,46 @@ class ArchctxDaemon {
31321
31702
  }
31322
31703
  async repoRemove(repositoryId) {
31323
31704
  this.assertRunning();
31324
- this.sessions.delete(repositoryId);
31705
+ const hadOpenSession = this.sessions.has(repositoryId);
31706
+ const hadPersistedSession = (await this.localStore.listRepositorySessions()).some((session) => session.repositoryId === repositoryId);
31707
+ const registered = this.landscape?.repositories.some((repo) => repo.repositoryId === repositoryId) ?? false;
31708
+ if (!registered && !hadOpenSession && !hadPersistedSession) {
31709
+ return errorEnvelope("repo.remove", "AC_REPO_NOT_FOUND", `repository is not registered: ${repositoryId}`);
31710
+ }
31711
+ let detachedRelationIds = [];
31712
+ let nextLandscape;
31325
31713
  if (this.landscape) {
31326
- this.landscape = {
31714
+ detachedRelationIds = (await this.localStore.listCrossRepoRelations(this.landscape)).filter((relation) => relation.source.repositoryId === repositoryId || relation.target.repositoryId === repositoryId).map((relation) => relation.id).sort();
31715
+ const detached = new Set(detachedRelationIds);
31716
+ const next = {
31327
31717
  ...this.landscape,
31328
31718
  repositories: this.landscape.repositories.filter((repo) => repo.repositoryId !== repositoryId),
31329
- relations: this.landscape.relations
31719
+ relations: this.landscape.relations.filter((relationId) => !detached.has(relationId)),
31720
+ ...this.landscape.scope === undefined ? {} : {
31721
+ scope: {
31722
+ ...this.landscape.scope,
31723
+ defaultActiveRepositories: (this.landscape.scope.defaultActiveRepositories ?? []).filter((activeId) => activeId !== repositoryId)
31724
+ }
31725
+ }
31330
31726
  };
31331
- await this.localStore.saveLandscape(this.landscape);
31727
+ const validation = validateLandscape(next, await this.localStore.listCrossRepoRelations(next));
31728
+ if (!validation.valid) {
31729
+ return errorEnvelope("repo.remove", "AC_SCHEMA_INVALID", validation.errors.join("; "));
31730
+ }
31731
+ nextLandscape = next;
31732
+ }
31733
+ this.sessions.delete(repositoryId);
31734
+ await this.localStore.deleteRepositorySession(repositoryId);
31735
+ if (nextLandscape) {
31736
+ this.landscape = nextLandscape;
31737
+ await this.localStore.saveLandscape(nextLandscape);
31332
31738
  }
31333
- return okEnvelope("repo.remove", { repositoryId, removed: true });
31739
+ return okEnvelope("repo.remove", {
31740
+ repositoryId,
31741
+ removed: true,
31742
+ sessionRemoved: hadOpenSession || hadPersistedSession,
31743
+ detachedRelationIds
31744
+ });
31334
31745
  }
31335
31746
  async loadLandscape(landscape) {
31336
31747
  this.assertRunning();
@@ -32186,16 +32597,76 @@ data: ${payload}
32186
32597
  }
32187
32598
  }
32188
32599
 
32600
+ class RuntimeRpcTransportError extends Error {
32601
+ code;
32602
+ method;
32603
+ timeoutMs;
32604
+ elapsedMs;
32605
+ constructor(code, method, timeoutMs, elapsedMs) {
32606
+ super(code === "RPC_TIMEOUT" ? `runtime RPC timeout: ${method} exceeded ${timeoutMs}ms` : `runtime RPC cancelled: ${method} aborted after ${elapsedMs}ms`);
32607
+ this.code = code;
32608
+ this.method = method;
32609
+ this.timeoutMs = timeoutMs;
32610
+ this.elapsedMs = elapsedMs;
32611
+ this.name = "RuntimeRpcTransportError";
32612
+ }
32613
+ }
32614
+ var RUNTIME_RPC_CLIENT_TIMEOUT_POLICY = {
32615
+ health: 5000,
32616
+ short: 15000,
32617
+ normal: 120000,
32618
+ long: 900000
32619
+ };
32620
+ var RUNTIME_RPC_SHORT_METHODS = new Set([
32621
+ "shutdown",
32622
+ "runtimeStatus",
32623
+ "landscapeStatus",
32624
+ "repoList",
32625
+ "explorerStatus",
32626
+ "explorerServiceContract",
32627
+ "jobsList",
32628
+ "jobsStats",
32629
+ "ledgerState",
32630
+ "ledgerDrift",
32631
+ "stopExplorer",
32632
+ "revokeExplorerToken"
32633
+ ]);
32634
+ var RUNTIME_RPC_LONG_METHODS = new Set([
32635
+ "init",
32636
+ "sync",
32637
+ "prepare",
32638
+ "context",
32639
+ "checkpoint",
32640
+ "auditRun",
32641
+ "auditApprove",
32642
+ "recommendations",
32643
+ "book",
32644
+ "ledgerRebuild",
32645
+ "ledgerMigrate",
32646
+ "startDeveloperReviewRun",
32647
+ "runSignedDeveloperReviewAttestation"
32648
+ ]);
32649
+ function runtimeRpcMethodTimeout(method, policy) {
32650
+ if (RUNTIME_RPC_SHORT_METHODS.has(method))
32651
+ return policy.short;
32652
+ if (RUNTIME_RPC_LONG_METHODS.has(method))
32653
+ return policy.long;
32654
+ return policy.normal;
32655
+ }
32656
+
32189
32657
  class RuntimeRpcClient {
32190
32658
  connection;
32191
- constructor(connection) {
32659
+ options;
32660
+ timeouts;
32661
+ constructor(connection, options = {}) {
32192
32662
  this.connection = connection;
32663
+ this.options = options;
32664
+ this.timeouts = { ...RUNTIME_RPC_CLIENT_TIMEOUT_POLICY, ...options.timeouts };
32193
32665
  }
32194
32666
  async health() {
32195
- const response = await fetch(`${this.connection.url}health`, {
32667
+ return await this.request("health", this.timeouts.health, `${this.connection.url}health`, {
32196
32668
  headers: { "X-ArchContext-RPC-Version": RUNTIME_RPC_VERSION }
32197
32669
  });
32198
- return await response.json();
32199
32670
  }
32200
32671
  async shutdown() {
32201
32672
  return this.call("shutdown", []);
@@ -32279,8 +32750,11 @@ class RuntimeRpcClient {
32279
32750
  applyUpdate(root, input) {
32280
32751
  return this.call("applyUpdate", [root, input]);
32281
32752
  }
32282
- reconcileProjectionApply(root, lookupKey) {
32283
- return this.call("reconcileProjectionApply", [root, lookupKey]);
32753
+ inspectProjectionApplyReceipt(root, lookupKey) {
32754
+ return this.call("inspectProjectionApplyReceipt", [root, lookupKey]);
32755
+ }
32756
+ recoverProjectionApply(root, intent) {
32757
+ return this.call("recoverProjectionApply", [root, intent]);
32284
32758
  }
32285
32759
  ledgerState(root) {
32286
32760
  return this.call("ledgerState", [root]);
@@ -32358,7 +32832,7 @@ class RuntimeRpcClient {
32358
32832
  return unwrapRpcData(await this.call("recoverDeveloperReviewRuns", [input]));
32359
32833
  }
32360
32834
  async call(method, params) {
32361
- const response = await fetch(`${this.connection.url}rpc`, {
32835
+ return await this.request(method, runtimeRpcMethodTimeout(method, this.timeouts), `${this.connection.url}rpc`, {
32362
32836
  method: "POST",
32363
32837
  headers: {
32364
32838
  Authorization: `Bearer ${this.connection.token}`,
@@ -32367,7 +32841,35 @@ class RuntimeRpcClient {
32367
32841
  },
32368
32842
  body: JSON.stringify({ schemaVersion: RUNTIME_RPC_VERSION, method, params })
32369
32843
  });
32370
- return await response.json();
32844
+ }
32845
+ async request(method, timeoutMs, url, init) {
32846
+ const controller = new AbortController;
32847
+ const startedAt = Date.now();
32848
+ let timedOut = false;
32849
+ const timer = setTimeout(() => {
32850
+ timedOut = true;
32851
+ controller.abort();
32852
+ }, timeoutMs);
32853
+ const callerSignal = this.options.signal;
32854
+ const onCallerAbort = () => controller.abort();
32855
+ if (callerSignal?.aborted)
32856
+ controller.abort();
32857
+ else
32858
+ callerSignal?.addEventListener("abort", onCallerAbort, { once: true });
32859
+ try {
32860
+ const response = await fetch(url, { ...init, signal: controller.signal });
32861
+ return await response.json();
32862
+ } catch (error) {
32863
+ const elapsedMs = Date.now() - startedAt;
32864
+ if (timedOut)
32865
+ throw new RuntimeRpcTransportError("RPC_TIMEOUT", method, timeoutMs, elapsedMs);
32866
+ if (callerSignal?.aborted)
32867
+ throw new RuntimeRpcTransportError("RPC_ABORTED", method, timeoutMs, elapsedMs);
32868
+ throw error;
32869
+ } finally {
32870
+ clearTimeout(timer);
32871
+ callerSignal?.removeEventListener("abort", onCallerAbort);
32872
+ }
32371
32873
  }
32372
32874
  }
32373
32875
 
@@ -32418,7 +32920,7 @@ class ArchctxRuntimeRpcServer {
32418
32920
  startedAt: (this.options.clock ?? (() => new Date().toISOString()))()
32419
32921
  };
32420
32922
  writeFileSync6(connectionPath, JSON.stringify(this.connection, null, 2), { mode: 384 });
32421
- chmodSync3(connectionPath, 384);
32923
+ chmodSync4(connectionPath, 384);
32422
32924
  this.armIdleTimer();
32423
32925
  return this.connection;
32424
32926
  }
@@ -32512,13 +33014,26 @@ class ArchctxRuntimeRpcServer {
32512
33014
  writeJson(response, 401, { schemaVersion: RUNTIME_RPC_VERSION, ok: false, error: "runtime RPC token required" });
32513
33015
  return;
32514
33016
  }
32515
- const body = await readRequestJson(request);
32516
- if (body.schemaVersion !== RUNTIME_RPC_VERSION) {
32517
- writeJson(response, 400, { schemaVersion: RUNTIME_RPC_VERSION, ok: false, error: "runtime RPC version mismatch" });
32518
- return;
32519
- }
32520
33017
  this.inFlightRpcRequests += 1;
32521
33018
  try {
33019
+ const read = await readRpcRequestBody(request, {
33020
+ maxBytes: this.options.maxRequestBodyBytes ?? RUNTIME_RPC_MAX_REQUEST_BODY_BYTES,
33021
+ timeoutMs: this.options.requestBodyTimeoutMs ?? RUNTIME_RPC_REQUEST_BODY_TIMEOUT_MS
33022
+ });
33023
+ if (!read.ok) {
33024
+ if (read.kind === "rejected")
33025
+ writeJson(response, read.status, { schemaVersion: RUNTIME_RPC_VERSION, ok: false, error: read.error });
33026
+ return;
33027
+ }
33028
+ if (!read.value || typeof read.value !== "object" || Array.isArray(read.value)) {
33029
+ writeJson(response, 400, { schemaVersion: RUNTIME_RPC_VERSION, ok: false, error: "runtime RPC request body must be an object" });
33030
+ return;
33031
+ }
33032
+ const body = read.value;
33033
+ if (body.schemaVersion !== RUNTIME_RPC_VERSION) {
33034
+ writeJson(response, 400, { schemaVersion: RUNTIME_RPC_VERSION, ok: false, error: "runtime RPC version mismatch" });
33035
+ return;
33036
+ }
32522
33037
  const result = await this.dispatch(body.method ?? "", body.params ?? []);
32523
33038
  writeJson(response, 200, result);
32524
33039
  if (body.method === "shutdown")
@@ -32585,8 +33100,10 @@ class ArchctxRuntimeRpcServer {
32585
33100
  return this.daemon.completeTask(params[0], params[1]);
32586
33101
  case "applyUpdate":
32587
33102
  return this.daemon.applyUpdate(params[0], params[1]);
32588
- case "reconcileProjectionApply":
32589
- return this.daemon.reconcileProjectionApply(params[0], params[1]);
33103
+ case "inspectProjectionApplyReceipt":
33104
+ return this.daemon.inspectProjectionApplyReceipt(params[0], params[1]);
33105
+ case "recoverProjectionApply":
33106
+ return this.daemon.recoverProjectionApply(params[0], params[1]);
32590
33107
  case "ledgerState":
32591
33108
  return this.daemon.ledgerState(params[0]);
32592
33109
  case "ledgerDrift":
@@ -32630,13 +33147,13 @@ class ArchctxRuntimeRpcServer {
32630
33147
  case "runtimeStatus":
32631
33148
  return this.daemon.runtimeStatus(params[0]);
32632
33149
  case "startDeveloperReviewRun":
32633
- return okEnvelope("developerReview.startRun", this.daemon.startDeveloperReviewRun(params[0]));
33150
+ return okEnvelope("developerReview.startRun", this.daemon.startDeveloperReviewRun(decodeStartDeveloperReviewRunParams(params)));
32634
33151
  case "runSignedDeveloperReviewAttestation":
32635
- return okEnvelope("developerReview.attestation", await this.daemon.runSignedDeveloperReviewAttestation(params[0]));
33152
+ return okEnvelope("developerReview.attestation", await this.daemon.runSignedDeveloperReviewAttestation(decodeSignedDeveloperReviewAttestationParams(params)));
32636
33153
  case "cleanupDeveloperReviewRun":
32637
- return okEnvelope("developerReview.cleanupRun", this.daemon.cleanupDeveloperReviewRun(params[0]));
33154
+ return okEnvelope("developerReview.cleanupRun", this.daemon.cleanupDeveloperReviewRun(decodeDeveloperReviewRunManifest(params[0], "cleanupDeveloperReviewRun")));
32638
33155
  case "recoverDeveloperReviewRuns":
32639
- return okEnvelope("developerReview.recoverRuns", this.daemon.recoverDeveloperReviewRuns(params[0]));
33156
+ return okEnvelope("developerReview.recoverRuns", this.daemon.recoverDeveloperReviewRuns(decodeRecoverDeveloperReviewRunsParams(params)));
32640
33157
  case "shutdown":
32641
33158
  return okEnvelope("daemon.stop", { stopping: true });
32642
33159
  default:
@@ -32814,6 +33331,9 @@ function shouldSkipGeneratedProjectionJob(metadata, input) {
32814
33331
  function isRuntimeAgentJobCursorStale(job, scope) {
32815
33332
  return job.worktree.headSha !== scope.worktree.headSha || job.worktree.worktreeDigest !== scope.worktree.worktreeDigest;
32816
33333
  }
33334
+ function runtimeAgentJobOutOfScopeEnvelope(requestId, jobId) {
33335
+ return errorEnvelope(requestId, "AC_PRECONDITION_FAILED", `runtime agent job does not belong to this repository/worktree: ${jobId}`, "runtime-agent-job-out-of-scope");
33336
+ }
32817
33337
  function runtimeWorktreeDigest(root, profile) {
32818
33338
  switch (profile) {
32819
33339
  case "repository":
@@ -32824,6 +33344,137 @@ function runtimeWorktreeDigest(root, profile) {
32824
33344
  throw new RuntimeUpdateInputError(`unsupported worktree digest profile: ${String(profile)}`);
32825
33345
  }
32826
33346
  }
33347
+ function buildRuntimeProjectionRecoveryFixedPoint(root) {
33348
+ const loaded = loadArchitectureDocumentationInputs(root, REPO_HARNESS_PROJECTION_PROFILE);
33349
+ const sourceDigest = digestJson({
33350
+ model: loaded.model,
33351
+ profile: REPO_HARNESS_PROJECTION_PROFILE,
33352
+ decisions: loaded.decisions.map((decision) => ({ id: decision.id, path: decision.path, title: decision.title, status: decision.status }))
33353
+ });
33354
+ const codeGraphInputs = prepareArchitectureDocumentationProjectionSnapshot(root, loaded.model);
33355
+ const provenance = codeGraphInputs.provenance;
33356
+ const projection3 = renderArchitectureDocumentationProjection({
33357
+ model: loaded.model,
33358
+ profile: REPO_HARNESS_PROJECTION_PROFILE,
33359
+ decisions: loaded.decisions,
33360
+ existingFiles: loaded.existingFiles,
33361
+ verifiedAgainst: assertArchitectureProjectionVerifiedAgainst({
33362
+ branch: readCurrentBranch(root),
33363
+ commit: readHeadSha2(root),
33364
+ committedAt: readHeadCommittedAt(root)
33365
+ }),
33366
+ sourceChangesSinceStamp: loadCapabilitySourceChangesSinceStamps(root, loaded.model),
33367
+ sourceScaleSignals: loadCapabilitySourceScaleSignals(root, loaded.model),
33368
+ importGraphs: codeGraphInputs.importGraphs,
33369
+ selectorEvidence: codeGraphInputs.selectorEvidence,
33370
+ provenance,
33371
+ sourceDigest,
33372
+ generatedAt: new Date(0).toISOString(),
33373
+ refreshContext: {
33374
+ repositoryId: repositoryFingerprint2(root),
33375
+ workspaceId: runtimeProjectionWorkspaceId(root),
33376
+ headSha: provenance.baseHeadSha,
33377
+ worktreeDigest: provenance.worktreeDigest
33378
+ }
33379
+ });
33380
+ const snapshot = {
33381
+ repositoryId: repositoryFingerprint2(root),
33382
+ workspaceId: runtimeProjectionWorkspaceId(root),
33383
+ headSha: readHeadSha2(root),
33384
+ worktreeDigest: architectureDocumentationProjectionWorktreeDigest(root, loaded.model),
33385
+ baseHeadSha: projection3.provenance.baseHeadSha,
33386
+ sourceTreeDigest: projection3.provenance.sourceTreeDigest,
33387
+ modelDigest: projection3.provenance.modelDigest,
33388
+ codeGraphDigest: projection3.provenance.codeGraphDigest,
33389
+ indexedWorktreeDigest: projection3.provenance.indexedWorktreeDigest,
33390
+ projectionInputDigest: projection3.provenance.projectionInputDigest,
33391
+ rendererVersion: projection3.provenance.rendererVersion,
33392
+ layoutVersion: projection3.provenance.layoutVersion,
33393
+ generatedFrom: projection3.provenance.generatedFrom
33394
+ };
33395
+ return {
33396
+ projection: projection3,
33397
+ snapshot,
33398
+ ownedOutputDigest: runtimeProjectionOwnedOutputDigest({ files: [...projection3.files, projection3.manifest] })
33399
+ };
33400
+ }
33401
+ function runtimeProjectionRecoveryFixedPointIssues(receipt, fixedPoint) {
33402
+ const binding = receipt.recovery;
33403
+ if (!binding)
33404
+ return ["committed projection receipt does not support semantic recovery"];
33405
+ const issues = [];
33406
+ const projection3 = fixedPoint.projection;
33407
+ if (!projection3.drift.ok || projection3.rejected.length > 0)
33408
+ issues.push("projection owned outputs are not a clean fixed point");
33409
+ if (projection3.majorChange.mode !== "none" || projection3.majorChange.reasonCodes.length > 0 || projection3.majorChange.affectedNodeIds.length > 0) {
33410
+ issues.push("current architecture state contains an unresolved major change");
33411
+ }
33412
+ if (projection3.refreshSignals.length > 0)
33413
+ issues.push("current architecture state has unresolved refresh signals");
33414
+ for (const field of ["repositoryId", "workspaceId", "headSha", "worktreeDigest"]) {
33415
+ if (fixedPoint.snapshot[field] !== binding.originalExpectedSnapshot[field]) {
33416
+ issues.push(`current projection snapshot differs from the approved snapshot: ${field}`);
33417
+ }
33418
+ }
33419
+ if (binding.generatedFrom.codeGraphStatus !== "ready")
33420
+ issues.push("approved CodeGraph snapshot is unavailable");
33421
+ if (projection3.provenance.generatedFrom.codeGraphStatus !== "ready")
33422
+ issues.push("current CodeGraph snapshot is unavailable");
33423
+ if (digestJson(projection3.architectureDigests) !== digestJson(binding.expectedResultingDigests)) {
33424
+ issues.push("current model, source, flow-proof, or projection digest differs from the approved result");
33425
+ }
33426
+ if (projection3.provenance.projectionInputDigest !== receipt.result.outputSnapshot.projectionInputDigest || projection3.provenance.codeGraphDigest !== receipt.result.outputSnapshot.codeGraphDigest || projection3.provenance.rendererVersion !== binding.rendererVersion || projection3.provenance.layoutVersion !== binding.layoutVersion || digestJson(projection3.provenance.generatedFrom) !== digestJson(binding.generatedFrom)) {
33427
+ issues.push("current renderer, layout, or CodeGraph provenance differs from the approved result");
33428
+ }
33429
+ if (fixedPoint.ownedOutputDigest !== binding.ownedOutputDigest)
33430
+ issues.push("current projection-owned output bytes differ from the approved result");
33431
+ if (fixedPoint.snapshot.generatedFrom.codeGraphStatus !== "ready")
33432
+ issues.push("current proof snapshot requires CodeGraph ready");
33433
+ return issues;
33434
+ }
33435
+ function createRuntimeProjectionRecoveryProof(intent, receipt, fixedPoint) {
33436
+ const current = {
33437
+ snapshot: fixedPoint.snapshot,
33438
+ resultingDigests: fixedPoint.projection.architectureDigests,
33439
+ ownedOutputDigest: fixedPoint.ownedOutputDigest,
33440
+ fixedPointDigest: digestJson({
33441
+ schemaVersion: "archcontext.projection-apply-recovery-fixed-point/v1",
33442
+ snapshot: fixedPoint.snapshot,
33443
+ resultingDigests: fixedPoint.projection.architectureDigests,
33444
+ ownedOutputDigest: fixedPoint.ownedOutputDigest,
33445
+ drift: fixedPoint.projection.drift,
33446
+ majorChange: fixedPoint.projection.majorChange,
33447
+ refreshSignalIds: fixedPoint.projection.refreshSignals.map((signal) => signal.signalId)
33448
+ })
33449
+ };
33450
+ const payload = {
33451
+ schemaVersion: "archcontext.projection-apply-recovery-proof/v1",
33452
+ requestId: intent.requestId,
33453
+ requestDigest: digestJson(intent),
33454
+ receipt: {
33455
+ lookupKey: receipt.identity.lookupKey,
33456
+ applyId: receipt.identity.applyId,
33457
+ receiptDigest: receipt.result.receiptDigest
33458
+ },
33459
+ acceptedChange: receipt.identity.acceptedChange,
33460
+ expectedResultingDigests: receipt.recovery.expectedResultingDigests,
33461
+ current
33462
+ };
33463
+ return {
33464
+ ...payload,
33465
+ proofDigest: projectionApplyRecoveryProofDigest(payload),
33466
+ deliveryStatus: "delivered"
33467
+ };
33468
+ }
33469
+ function runtimeProjectionOwnedOutputDigest(projection3) {
33470
+ return digestJson({
33471
+ schemaVersion: "archcontext.projection-owned-output/v1",
33472
+ files: projection3.files.map((file) => ({ path: file.path, body: file.body })).sort((left, right) => left.path.localeCompare(right.path))
33473
+ });
33474
+ }
33475
+ function runtimeProjectionWorkspaceId(root) {
33476
+ return `workspace.${digestJson({ root: canonicalRepositoryRoot2(root) }).replace(/^sha256:/, "").slice(0, 16)}`;
33477
+ }
32827
33478
  function isArchContextGeneratedProjectionPath(path) {
32828
33479
  return path.replace(/\\/g, "/").startsWith(".archcontext/generated/");
32829
33480
  }
@@ -33175,13 +33826,18 @@ function runtimeAttestationIdentity(snapshot, composition) {
33175
33826
  })
33176
33827
  };
33177
33828
  }
33829
+ var DEVELOPER_REVIEW_RUN_ROOT_PREFIX = "archctx-developer-review-";
33830
+ var DEVELOPER_REVIEW_RUN_OWNER_MARKER_FILE = ".archctx-developer-review-run.json";
33831
+ var DEVELOPER_REVIEW_RUN_OWNER_SCHEMA_VERSION = "archcontext.developer-review-run-owner/v1";
33832
+ var DEVELOPER_REVIEW_RUN_ROOT_SUFFIX = /^[A-Za-z0-9]{6}$/;
33833
+ var DEVELOPER_REVIEW_RUN_ID_SUFFIX = /^[0-9a-f]{12}$/;
33178
33834
  function createDeveloperReviewRunPaths(input) {
33179
33835
  const safeChallengeId = safeControlFileSegment(input.challengeId);
33180
33836
  const runId = `${safeChallengeId}-${randomBytes(6).toString("hex")}`;
33181
- const stateDir = input.stateDir ? resolve18(input.stateDir) : defaultDeveloperReviewRunStateDir(input.sourceRoot);
33837
+ const stateDir = defaultDeveloperReviewRunStateDir(input.sourceRoot);
33182
33838
  const tempParent = input.tempRoot ? resolve18(input.tempRoot) : tmpdir3();
33183
33839
  mkdirSync7(tempParent, { recursive: true });
33184
- const runRoot = mkdtempSync4(join9(tempParent, `archctx-developer-review-${safeChallengeId.slice(0, 32)}-`));
33840
+ const runRoot = mkdtempSync4(join9(tempParent, `${DEVELOPER_REVIEW_RUN_ROOT_PREFIX}${safeChallengeId.slice(0, 32)}-`));
33185
33841
  return {
33186
33842
  runId,
33187
33843
  stateDir,
@@ -33191,6 +33847,92 @@ function createDeveloperReviewRunPaths(input) {
33191
33847
  lockPath: join9(stateDir, `${safeChallengeId}.lock`)
33192
33848
  };
33193
33849
  }
33850
+ function writeDeveloperReviewRunOwnerMarker(input) {
33851
+ const marker = {
33852
+ schemaVersion: DEVELOPER_REVIEW_RUN_OWNER_SCHEMA_VERSION,
33853
+ runId: input.runId,
33854
+ challengeId: input.challengeId,
33855
+ stateDir: input.stateDir,
33856
+ manifestPath: input.manifestPath,
33857
+ lockPath: input.lockPath,
33858
+ pid: process.pid,
33859
+ createdAt: input.createdAt
33860
+ };
33861
+ writePrivateJson3(join9(input.runRoot, DEVELOPER_REVIEW_RUN_OWNER_MARKER_FILE), marker, "wx");
33862
+ }
33863
+ function readDeveloperReviewRunOwnerMarker(path) {
33864
+ const stats = lstatIfExists(path);
33865
+ if (!stats || !stats.isFile())
33866
+ return;
33867
+ const parsed = readJsonObject(path);
33868
+ if (!parsed || parsed.schemaVersion !== DEVELOPER_REVIEW_RUN_OWNER_SCHEMA_VERSION)
33869
+ return;
33870
+ if (typeof parsed.runId !== "string" || typeof parsed.challengeId !== "string")
33871
+ return;
33872
+ if (typeof parsed.manifestPath !== "string" || typeof parsed.lockPath !== "string")
33873
+ return;
33874
+ return parsed;
33875
+ }
33876
+ function developerReviewRunNotOwned(reason) {
33877
+ return new Error(`developer-review-run-not-owned: ${reason}`);
33878
+ }
33879
+ function resolveOwnedDeveloperReviewRunTargets(run) {
33880
+ const safeChallengeId = safeControlFileSegment(run.challengeId);
33881
+ const runIdSuffix = run.runId.startsWith(`${safeChallengeId}-`) ? run.runId.slice(safeChallengeId.length + 1) : undefined;
33882
+ if (!runIdSuffix || !DEVELOPER_REVIEW_RUN_ID_SUFFIX.test(runIdSuffix))
33883
+ throw developerReviewRunNotOwned("run-id");
33884
+ const stateDir = defaultDeveloperReviewRunStateDir(run.sourceRoot);
33885
+ const manifestPath = join9(stateDir, `${safeChallengeId}.json`);
33886
+ const lockPath = join9(stateDir, `${safeChallengeId}.lock`);
33887
+ if (resolve18(run.manifestPath) !== manifestPath)
33888
+ throw developerReviewRunNotOwned("manifest-path");
33889
+ if (resolve18(run.lockPath) !== lockPath)
33890
+ throw developerReviewRunNotOwned("lock-path");
33891
+ const runRoot = resolve18(run.runRoot);
33892
+ const runRootPrefix = `${DEVELOPER_REVIEW_RUN_ROOT_PREFIX}${safeChallengeId.slice(0, 32)}-`;
33893
+ const runRootName = basename6(runRoot);
33894
+ if (!runRootName.startsWith(runRootPrefix) || !DEVELOPER_REVIEW_RUN_ROOT_SUFFIX.test(runRootName.slice(runRootPrefix.length))) {
33895
+ throw developerReviewRunNotOwned("run-root-name");
33896
+ }
33897
+ const worktreeTempRoot = join9(runRoot, "worktrees");
33898
+ if (resolve18(run.worktreeTempRoot) !== worktreeTempRoot)
33899
+ throw developerReviewRunNotOwned("worktree-temp-root");
33900
+ if (resolve18(run.codeGraphTemporaryState.root) !== runRoot)
33901
+ throw developerReviewRunNotOwned("codegraph-temporary-state-root");
33902
+ const runRootStats = lstatIfExists(runRoot);
33903
+ if (!runRootStats)
33904
+ return { stateDir, manifestPath, lockPath };
33905
+ if (!runRootStats.isDirectory())
33906
+ throw developerReviewRunNotOwned("run-root-not-a-directory");
33907
+ const marker = readDeveloperReviewRunOwnerMarker(join9(runRoot, DEVELOPER_REVIEW_RUN_OWNER_MARKER_FILE));
33908
+ if (!marker)
33909
+ throw developerReviewRunNotOwned("run-root-owner-marker-missing");
33910
+ if (marker.runId !== run.runId || marker.challengeId !== run.challengeId)
33911
+ throw developerReviewRunNotOwned("run-root-owner-marker-mismatch");
33912
+ if (marker.manifestPath !== manifestPath || marker.lockPath !== lockPath)
33913
+ throw developerReviewRunNotOwned("run-root-owner-marker-mismatch");
33914
+ if (!run.worktree)
33915
+ return { stateDir, manifestPath, lockPath, runRoot };
33916
+ const temporaryRoot = resolve18(run.worktree.temporaryRoot);
33917
+ const worktreeRoot = resolve18(run.worktree.worktreeRoot);
33918
+ if (!isContainedPath(worktreeTempRoot, temporaryRoot))
33919
+ throw developerReviewRunNotOwned("worktree-temporary-root");
33920
+ if (!isContainedPath(temporaryRoot, worktreeRoot))
33921
+ throw developerReviewRunNotOwned("worktree-root");
33922
+ if (defaultDeveloperReviewRunStateDir(run.worktree.sourceRoot) !== stateDir)
33923
+ throw developerReviewRunNotOwned("worktree-source-root");
33924
+ return { stateDir, manifestPath, lockPath, runRoot, worktree: { ...run.worktree, temporaryRoot, worktreeRoot } };
33925
+ }
33926
+ function isContainedPath(parent, child) {
33927
+ return child === parent || child.startsWith(parent.endsWith(sep7) ? parent : `${parent}${sep7}`);
33928
+ }
33929
+ function lstatIfExists(path) {
33930
+ try {
33931
+ return lstatSync7(path);
33932
+ } catch {
33933
+ return;
33934
+ }
33935
+ }
33194
33936
  function safeControlFileSegment(value) {
33195
33937
  const sanitized = value.replace(/[^A-Za-z0-9_.-]/g, "_").slice(0, 80);
33196
33938
  return sanitized.length > 0 ? sanitized : "developer-review";
@@ -33257,9 +33999,7 @@ function appendExternalDocumentationToContext(context, resource, candidate, maxB
33257
33999
  const resources = context.resources.some((entry) => entry.uri === resource.uri) ? context.resources : [...context.resources, externalResource];
33258
34000
  const unknown = `External documentation is advisory and untrusted for ${candidate.packageName}@${candidate.version}: ${candidate.intent}`;
33259
34001
  const unknowns = context.unknowns.includes(unknown) ? context.unknowns : [...context.unknowns, unknown];
33260
- const extensionWithoutDigest = { ...context.extensions };
33261
- delete extensionWithoutDigest.digest;
33262
- const withoutDigest = {
34002
+ const augmented = {
33263
34003
  ...context,
33264
34004
  unknowns,
33265
34005
  resources,
@@ -33278,7 +34018,7 @@ function appendExternalDocumentationToContext(context, resource, candidate, maxB
33278
34018
  }
33279
34019
  },
33280
34020
  extensions: {
33281
- ...extensionWithoutDigest,
34021
+ ...context.extensions,
33282
34022
  externalDocumentationDigest: digestJson({
33283
34023
  provider: resource.provider,
33284
34024
  libraryId: candidate.libraryId,
@@ -33289,22 +34029,7 @@ function appendExternalDocumentationToContext(context, resource, candidate, maxB
33289
34029
  })
33290
34030
  }
33291
34031
  };
33292
- const byteLength = Buffer.byteLength(JSON.stringify(withoutDigest), "utf8");
33293
- const withMetadata = {
33294
- ...withoutDigest,
33295
- extensions: {
33296
- ...withoutDigest.extensions,
33297
- byteLength,
33298
- budgetExceeded: byteLength > maxBytes
33299
- }
33300
- };
33301
- return {
33302
- ...withMetadata,
33303
- extensions: {
33304
- ...withMetadata.extensions,
33305
- digest: digestJson(withMetadata)
33306
- }
33307
- };
34032
+ return finalizeContextBudgetMetadata(augmented, maxBytes);
33308
34033
  }
33309
34034
  function prepareContextHasVersionRelatedUnknown(context) {
33310
34035
  const unknowns = context.unknowns.join(" ").toLowerCase();
@@ -33402,15 +34127,22 @@ function isExactPackageVersion(value) {
33402
34127
  return /^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?$/.test(value);
33403
34128
  }
33404
34129
  function readContext7Lockfile(root) {
33405
- const path = resolve18(root, CONTEXT7_LOCKFILE);
34130
+ return readContext7LockfileState(root).lock;
34131
+ }
34132
+ function readContext7LockfileState(root) {
34133
+ const path = assertPathHasNoSymlinkSegments(root, CONTEXT7_LOCKFILE);
33406
34134
  if (!existsSync15(path)) {
33407
34135
  return {
33408
- schemaVersion: CONTEXT7_LOCKFILE_SCHEMA_VERSION,
33409
- provider: "context7",
33410
- libraries: []
34136
+ lock: {
34137
+ schemaVersion: CONTEXT7_LOCKFILE_SCHEMA_VERSION,
34138
+ provider: "context7",
34139
+ libraries: []
34140
+ },
34141
+ expectedHash: "missing"
33411
34142
  };
33412
34143
  }
33413
- const parsed = JSON.parse(readFileSync14(path, "utf8"));
34144
+ const body = readFileSync14(path, "utf8");
34145
+ const parsed = JSON.parse(body);
33414
34146
  if (parsed.schemaVersion !== CONTEXT7_LOCKFILE_SCHEMA_VERSION || parsed.provider !== "context7" || !Array.isArray(parsed.libraries)) {
33415
34147
  throw new Error("Invalid Context7 lockfile");
33416
34148
  }
@@ -33419,8 +34151,11 @@ function readContext7Lockfile(root) {
33419
34151
  assertContext7Version(library.version);
33420
34152
  }
33421
34153
  return {
33422
- ...parsed,
33423
- libraries: [...parsed.libraries].sort((a, b) => a.libraryId.localeCompare(b.libraryId))
34154
+ lock: {
34155
+ ...parsed,
34156
+ libraries: [...parsed.libraries].sort((a, b) => a.libraryId.localeCompare(b.libraryId))
34157
+ },
34158
+ expectedHash: digestJson({ body })
33424
34159
  };
33425
34160
  }
33426
34161
  function upsertContext7Pin(lock, pin) {
@@ -33430,8 +34165,14 @@ function upsertContext7Pin(lock, pin) {
33430
34165
  libraries: [...lock.libraries.filter((library) => library.libraryId !== pin.libraryId), pin].sort((a, b) => a.libraryId.localeCompare(b.libraryId))
33431
34166
  };
33432
34167
  }
33433
- function writeContext7Lockfile(root, lock) {
33434
- writePrivateJson3(resolve18(root, CONTEXT7_LOCKFILE), lock);
34168
+ function writeContext7Lockfile(root, lock, expectedHash) {
34169
+ writeFileWithoutFollowingSymlinks({
34170
+ root,
34171
+ path: CONTEXT7_LOCKFILE,
34172
+ body: JSON.stringify(lock, null, 2),
34173
+ mode: 384,
34174
+ expectedHash
34175
+ });
33435
34176
  }
33436
34177
  function writeDeveloperReviewRunManifest(manifest) {
33437
34178
  writePrivateJson3(manifest.manifestPath, manifest);
@@ -33439,25 +34180,192 @@ function writeDeveloperReviewRunManifest(manifest) {
33439
34180
  function writePrivateJson3(path, value, flag = "w") {
33440
34181
  mkdirSync7(dirname10(path), { recursive: true });
33441
34182
  writeFileSync6(path, JSON.stringify(value, null, 2), { mode: 384, flag });
33442
- chmodSync3(path, 384);
34183
+ chmodSync4(path, 384);
33443
34184
  }
33444
34185
  function readDeveloperReviewRunManifest(path) {
33445
- const parsed = readJsonObject(path);
33446
- if (!parsed || parsed.schemaVersion !== "archcontext.developer-review-run/v1")
33447
- return;
33448
- if (typeof parsed.runId !== "string" || typeof parsed.challengeId !== "string")
33449
- return;
33450
- if (typeof parsed.repositoryId !== "number" || typeof parsed.sourceRoot !== "string")
33451
- return;
33452
- if (typeof parsed.runRoot !== "string" || typeof parsed.worktreeTempRoot !== "string")
33453
- return;
33454
- if (typeof parsed.manifestPath !== "string" || typeof parsed.lockPath !== "string")
33455
- return;
33456
- if (typeof parsed.pid !== "number" || typeof parsed.createdAt !== "string")
33457
- return;
33458
- if (parsed.status !== "preparing" && parsed.status !== "running")
34186
+ try {
34187
+ return decodeDeveloperReviewRunManifest(readJsonObject(path), "developer-review-run-manifest");
34188
+ } catch {
33459
34189
  return;
33460
- return parsed;
34190
+ }
34191
+ }
34192
+ function rpcInputInvalid(context, detail) {
34193
+ return new Error(`runtime-rpc-input-invalid: ${context} ${detail}`);
34194
+ }
34195
+ function decodeRpcRecord(value, context, label, allowedKeys) {
34196
+ if (!value || typeof value !== "object" || Array.isArray(value))
34197
+ throw rpcInputInvalid(context, `${label} must be an object`);
34198
+ const record2 = value;
34199
+ for (const key of Object.keys(record2)) {
34200
+ if (!allowedKeys.includes(key))
34201
+ throw rpcInputInvalid(context, `${label} has unknown field ${key}`);
34202
+ }
34203
+ return record2;
34204
+ }
34205
+ function rpcString(record2, context, label) {
34206
+ const value = record2[label];
34207
+ if (typeof value !== "string" || value.length === 0)
34208
+ throw rpcInputInvalid(context, `${label} must be a non-empty string`);
34209
+ return value;
34210
+ }
34211
+ function rpcOptionalString(record2, context, label) {
34212
+ return record2[label] === undefined ? undefined : rpcString(record2, context, label);
34213
+ }
34214
+ function rpcNumber(record2, context, label) {
34215
+ const value = record2[label];
34216
+ if (typeof value !== "number" || !Number.isFinite(value))
34217
+ throw rpcInputInvalid(context, `${label} must be a number`);
34218
+ return value;
34219
+ }
34220
+ function rpcLiteral(record2, context, label, allowed) {
34221
+ const value = record2[label];
34222
+ if (typeof value !== "string" || !allowed.includes(value))
34223
+ throw rpcInputInvalid(context, `${label} is not an accepted value`);
34224
+ return value;
34225
+ }
34226
+ function decodeRpcReviewChallengeV2(value, context) {
34227
+ const record2 = decodeRpcRecord(value, context, "challenge", [
34228
+ "schemaVersion",
34229
+ "challengeId",
34230
+ "installationId",
34231
+ "repositoryId",
34232
+ "pullRequestNumber",
34233
+ "headSha",
34234
+ "baseSha",
34235
+ "nonce",
34236
+ "requiredTrust",
34237
+ "policyProfileId",
34238
+ "createdAt",
34239
+ "expiresAt",
34240
+ "status"
34241
+ ]);
34242
+ return {
34243
+ schemaVersion: rpcLiteral(record2, context, "schemaVersion", ["archcontext.review-challenge/v2"]),
34244
+ challengeId: rpcString(record2, context, "challengeId"),
34245
+ installationId: rpcNumber(record2, context, "installationId"),
34246
+ repositoryId: rpcNumber(record2, context, "repositoryId"),
34247
+ pullRequestNumber: rpcNumber(record2, context, "pullRequestNumber"),
34248
+ headSha: rpcString(record2, context, "headSha"),
34249
+ baseSha: rpcString(record2, context, "baseSha"),
34250
+ nonce: rpcString(record2, context, "nonce"),
34251
+ requiredTrust: rpcLiteral(record2, context, "requiredTrust", ["developer", "organization"]),
34252
+ policyProfileId: rpcString(record2, context, "policyProfileId"),
34253
+ createdAt: rpcString(record2, context, "createdAt"),
34254
+ expiresAt: rpcString(record2, context, "expiresAt"),
34255
+ status: rpcLiteral(record2, context, "status", ["PENDING", "LEASED", "SUBMITTED", "VERIFIED", "REJECTED", "SUPERSEDED", "EXPIRED"])
34256
+ };
34257
+ }
34258
+ function decodeRpcDetachedReviewWorktree(value, context, label = "worktree") {
34259
+ const record2 = decodeRpcRecord(value, context, label, [
34260
+ "schemaVersion",
34261
+ "sourceRoot",
34262
+ "worktreeRoot",
34263
+ "temporaryRoot",
34264
+ "headSha",
34265
+ "headTreeOid",
34266
+ "detached",
34267
+ "clean"
34268
+ ]);
34269
+ if (record2.detached !== true)
34270
+ throw rpcInputInvalid(context, `${label}.detached must be true`);
34271
+ if (record2.clean !== true)
34272
+ throw rpcInputInvalid(context, `${label}.clean must be true`);
34273
+ return {
34274
+ schemaVersion: rpcLiteral(record2, context, "schemaVersion", ["archcontext.detached-review-worktree/v1"]),
34275
+ sourceRoot: rpcString(record2, context, "sourceRoot"),
34276
+ worktreeRoot: rpcString(record2, context, "worktreeRoot"),
34277
+ temporaryRoot: rpcString(record2, context, "temporaryRoot"),
34278
+ headSha: rpcString(record2, context, "headSha"),
34279
+ headTreeOid: rpcString(record2, context, "headTreeOid"),
34280
+ detached: true,
34281
+ clean: true
34282
+ };
34283
+ }
34284
+ function decodeDeveloperReviewRunManifest(value, context) {
34285
+ const record2 = decodeRpcRecord(value, context, "run", [
34286
+ "schemaVersion",
34287
+ "runId",
34288
+ "challengeId",
34289
+ "repositoryId",
34290
+ "sourceRoot",
34291
+ "runRoot",
34292
+ "worktreeTempRoot",
34293
+ "manifestPath",
34294
+ "lockPath",
34295
+ "pid",
34296
+ "createdAt",
34297
+ "status",
34298
+ "codeGraphTemporaryState",
34299
+ "worktree"
34300
+ ]);
34301
+ const codeGraphTemporaryState = decodeRpcRecord(record2.codeGraphTemporaryState, context, "run.codeGraphTemporaryState", ["root", "cleanup"]);
34302
+ return {
34303
+ schemaVersion: rpcLiteral(record2, context, "schemaVersion", ["archcontext.developer-review-run/v1"]),
34304
+ runId: rpcString(record2, context, "runId"),
34305
+ challengeId: rpcString(record2, context, "challengeId"),
34306
+ repositoryId: rpcNumber(record2, context, "repositoryId"),
34307
+ sourceRoot: rpcString(record2, context, "sourceRoot"),
34308
+ runRoot: rpcString(record2, context, "runRoot"),
34309
+ worktreeTempRoot: rpcString(record2, context, "worktreeTempRoot"),
34310
+ manifestPath: rpcString(record2, context, "manifestPath"),
34311
+ lockPath: rpcString(record2, context, "lockPath"),
34312
+ pid: rpcNumber(record2, context, "pid"),
34313
+ createdAt: rpcString(record2, context, "createdAt"),
34314
+ status: rpcLiteral(record2, context, "status", ["preparing", "running"]),
34315
+ codeGraphTemporaryState: {
34316
+ root: rpcString(codeGraphTemporaryState, context, "root"),
34317
+ cleanup: rpcLiteral(codeGraphTemporaryState, context, "cleanup", ["remove-run-root"])
34318
+ },
34319
+ ...record2.worktree === undefined ? {} : { worktree: decodeRpcDetachedReviewWorktree(record2.worktree, context, "run.worktree") }
34320
+ };
34321
+ }
34322
+ function decodeStartDeveloperReviewRunParams(params) {
34323
+ const context = "startDeveloperReviewRun";
34324
+ const record2 = decodeRpcRecord(params[0], context, "params[0]", ["repositoryRoot", "challenge", "expectedHeadTreeOid"]);
34325
+ const expectedHeadTreeOid = rpcOptionalString(record2, context, "expectedHeadTreeOid");
34326
+ return {
34327
+ repositoryRoot: rpcString(record2, context, "repositoryRoot"),
34328
+ challenge: decodeRpcReviewChallengeV2(record2.challenge, context),
34329
+ ...expectedHeadTreeOid === undefined ? {} : { expectedHeadTreeOid }
34330
+ };
34331
+ }
34332
+ function decodeSignedDeveloperReviewAttestationParams(params) {
34333
+ const context = "runSignedDeveloperReviewAttestation";
34334
+ const record2 = decodeRpcRecord(params[0], context, "params[0]", [
34335
+ "challenge",
34336
+ "worktree",
34337
+ "keyRef",
34338
+ "principalId",
34339
+ "publicKeyId",
34340
+ "taskSessionId",
34341
+ "mergeBaseSha",
34342
+ "startedAt",
34343
+ "completedAt"
34344
+ ]);
34345
+ const optional = {
34346
+ taskSessionId: rpcOptionalString(record2, context, "taskSessionId"),
34347
+ mergeBaseSha: rpcOptionalString(record2, context, "mergeBaseSha"),
34348
+ startedAt: rpcOptionalString(record2, context, "startedAt"),
34349
+ completedAt: rpcOptionalString(record2, context, "completedAt")
34350
+ };
34351
+ return {
34352
+ challenge: decodeRpcReviewChallengeV2(record2.challenge, context),
34353
+ worktree: decodeRpcDetachedReviewWorktree(record2.worktree, context),
34354
+ keyRef: rpcString(record2, context, "keyRef"),
34355
+ principalId: rpcString(record2, context, "principalId"),
34356
+ publicKeyId: rpcString(record2, context, "publicKeyId"),
34357
+ ...Object.fromEntries(Object.entries(optional).filter(([, value]) => value !== undefined))
34358
+ };
34359
+ }
34360
+ function decodeRecoverDeveloperReviewRunsParams(params) {
34361
+ const context = "recoverDeveloperReviewRuns";
34362
+ const record2 = decodeRpcRecord(params[0], context, "params[0]", ["repositoryRoot", "force"]);
34363
+ if (record2.force !== undefined && typeof record2.force !== "boolean")
34364
+ throw rpcInputInvalid(context, "force must be a boolean");
34365
+ return {
34366
+ repositoryRoot: rpcString(record2, context, "repositoryRoot"),
34367
+ ...record2.force === undefined ? {} : { force: record2.force }
34368
+ };
33461
34369
  }
33462
34370
  function readJsonObject(path) {
33463
34371
  try {
@@ -33642,33 +34550,42 @@ function readCurrentBranch(root) {
33642
34550
  }
33643
34551
  }
33644
34552
  function repositoryNameWithOwner(root) {
34553
+ const target = readGitRemoteTarget(root);
34554
+ return target ? `${target.owner}/${target.repo}` : "local/unknown";
34555
+ }
34556
+ var SUPPORTED_GITHUB_REMOTE_HOST = "github.com";
34557
+ function readGitRemoteTarget(root) {
33645
34558
  try {
33646
34559
  const url = execFileSync7("git", ["remote", "get-url", "origin"], {
33647
34560
  cwd: root,
33648
34561
  encoding: "utf8",
33649
34562
  stdio: ["ignore", "pipe", "ignore"]
33650
34563
  }).trim();
33651
- return parseGitRemoteOwnerRepo(url) ?? "local/unknown";
34564
+ return parseGitRemoteTarget(url);
33652
34565
  } catch {
33653
- return "local/unknown";
34566
+ return;
33654
34567
  }
33655
34568
  }
33656
- function parseGitRemoteOwnerRepo(url) {
34569
+ function parseGitRemoteTarget(url) {
33657
34570
  const stripped = url.trim().replace(/\.git$/, "");
33658
- const scpMatch = /^[^/@]+@[^:/]+:(.+)$/.exec(stripped);
34571
+ const scpMatch = /^[^/@]+@([^:/]+):(.+)$/.exec(stripped);
33659
34572
  if (scpMatch)
33660
- return normalizeOwnerRepoPath(scpMatch[1]);
34573
+ return gitRemoteTarget(scpMatch[1], scpMatch[2]);
33661
34574
  try {
33662
- return normalizeOwnerRepoPath(new URL(stripped).pathname);
34575
+ const parsed = new URL(stripped);
34576
+ return gitRemoteTarget(parsed.hostname, parsed.pathname);
33663
34577
  } catch {
33664
34578
  return;
33665
34579
  }
33666
34580
  }
33667
- function normalizeOwnerRepoPath(path) {
34581
+ function gitRemoteTarget(host, path) {
34582
+ const canonicalHost = host.trim().toLowerCase();
34583
+ if (!canonicalHost)
34584
+ return;
33668
34585
  const segments = path.split("/").map((segment) => segment.trim()).filter(Boolean);
33669
34586
  if (segments.length < 2)
33670
34587
  return;
33671
- return segments.slice(-2).join("/");
34588
+ return { host: canonicalHost, owner: segments[segments.length - 2], repo: segments[segments.length - 1] };
33672
34589
  }
33673
34590
  function normalizeGithubRepoVisibility(value) {
33674
34591
  const lowered = value.trim().toLowerCase();
@@ -33684,6 +34601,10 @@ function auditApproveResultPayload(runId, status, totalCount, issuedIssues) {
33684
34601
  issuedIssues
33685
34602
  };
33686
34603
  }
34604
+ function obsoleteManagedProjectionPaths(currentFiles, projectedFiles) {
34605
+ const targetPaths = new Set(projectedFiles.map((file) => file.path));
34606
+ return currentFiles.filter((file) => !targetPaths.has(file.path)).map((file) => file.path);
34607
+ }
33687
34608
  function expectedFileHash(root, path) {
33688
34609
  const absolute = resolve18(root, path);
33689
34610
  return existsSync15(absolute) ? digestJson({ body: readFileSync14(absolute, "utf8") }) : "missing";
@@ -33978,13 +34899,63 @@ function isRpcVersionHeaderCompatible(request) {
33978
34899
  const header = requestRpcVersionHeader(request);
33979
34900
  return header === undefined || header === RUNTIME_RPC_VERSION;
33980
34901
  }
33981
- async function readRequestJson(request) {
33982
- const chunks = [];
33983
- for await (const chunk of request)
33984
- chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
33985
- if (chunks.length === 0)
33986
- return {};
33987
- return JSON.parse(Buffer.concat(chunks).toString("utf8"));
34902
+ async function readRpcRequestBody(request, limits) {
34903
+ const declaredLength = Number(request.headers["content-length"]);
34904
+ if (Number.isFinite(declaredLength) && declaredLength > limits.maxBytes) {
34905
+ request.pause();
34906
+ return { ok: false, kind: "rejected", status: 413, error: "runtime RPC request body exceeds the configured limit" };
34907
+ }
34908
+ return await new Promise((resolveBody) => {
34909
+ let chunks = [];
34910
+ let size = 0;
34911
+ let settled = false;
34912
+ const timer = setTimeout(() => {
34913
+ settle({ ok: false, kind: "rejected", status: 408, error: "runtime RPC request body read timeout" });
34914
+ }, limits.timeoutMs);
34915
+ const settle = (result) => {
34916
+ if (settled)
34917
+ return;
34918
+ settled = true;
34919
+ clearTimeout(timer);
34920
+ chunks = [];
34921
+ request.off("data", onData);
34922
+ request.off("end", onEnd);
34923
+ request.off("aborted", onAborted);
34924
+ request.off("error", onAborted);
34925
+ request.off("close", onClose);
34926
+ if (!result.ok)
34927
+ request.pause();
34928
+ resolveBody(result);
34929
+ };
34930
+ const onData = (chunk) => {
34931
+ const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
34932
+ size += buffer.length;
34933
+ if (size > limits.maxBytes) {
34934
+ settle({ ok: false, kind: "rejected", status: 413, error: "runtime RPC request body exceeds the configured limit" });
34935
+ return;
34936
+ }
34937
+ chunks.push(buffer);
34938
+ };
34939
+ const onEnd = () => {
34940
+ if (size === 0) {
34941
+ settle({ ok: true, value: {} });
34942
+ return;
34943
+ }
34944
+ const body = Buffer.concat(chunks).toString("utf8");
34945
+ try {
34946
+ settle({ ok: true, value: JSON.parse(body) });
34947
+ } catch {
34948
+ settle({ ok: false, kind: "rejected", status: 400, error: "runtime RPC request body is not valid JSON" });
34949
+ }
34950
+ };
34951
+ const onAborted = () => settle({ ok: false, kind: "aborted" });
34952
+ const onClose = () => settle({ ok: false, kind: "aborted" });
34953
+ request.on("data", onData);
34954
+ request.on("end", onEnd);
34955
+ request.on("aborted", onAborted);
34956
+ request.on("error", onAborted);
34957
+ request.on("close", onClose);
34958
+ });
33988
34959
  }
33989
34960
  function writeJson(response, statusCode, body) {
33990
34961
  response.writeHead(statusCode, { "Content-Type": "application/json; charset=utf-8" });
@@ -34140,20 +35111,82 @@ function structurizrElementType(kind) {
34140
35111
  init_src();
34141
35112
 
34142
35113
  // packages/local-runtime/runtime-daemon/src/index.ts
34143
- import { chmodSync as chmodSync4, closeSync as closeSync7, existsSync as existsSync16, mkdirSync as mkdirSync8, mkdtempSync as mkdtempSync5, openSync as openSync7, readdirSync as readdirSync10, readFileSync as readFileSync15, rmSync as rmSync9, statSync as statSync10, writeFileSync as writeFileSync7 } from "node:fs";
35114
+ import { chmodSync as chmodSync5, closeSync as closeSync7, existsSync as existsSync16, lstatSync as lstatSync8, mkdirSync as mkdirSync8, mkdtempSync as mkdtempSync5, openSync as openSync7, readdirSync as readdirSync10, readFileSync as readFileSync15, rmSync as rmSync9, statSync as statSync10, writeFileSync as writeFileSync7 } from "node:fs";
35115
+ init_src();
34144
35116
  init_src();
34145
35117
  var DEFAULT_DAEMON_IDLE_TIMEOUT_MS2 = 30 * 60000;
34146
35118
  var RUNTIME_RPC_VERSION2 = LOCAL_RUNTIME_RPC_SCHEMA_VERSION;
35119
+ var RUNTIME_RPC_MAX_REQUEST_BODY_BYTES2 = 16 * 1024 * 1024;
35120
+ class RuntimeRpcTransportError2 extends Error {
35121
+ code;
35122
+ method;
35123
+ timeoutMs;
35124
+ elapsedMs;
35125
+ constructor(code, method, timeoutMs, elapsedMs) {
35126
+ super(code === "RPC_TIMEOUT" ? `runtime RPC timeout: ${method} exceeded ${timeoutMs}ms` : `runtime RPC cancelled: ${method} aborted after ${elapsedMs}ms`);
35127
+ this.code = code;
35128
+ this.method = method;
35129
+ this.timeoutMs = timeoutMs;
35130
+ this.elapsedMs = elapsedMs;
35131
+ this.name = "RuntimeRpcTransportError";
35132
+ }
35133
+ }
35134
+ var RUNTIME_RPC_CLIENT_TIMEOUT_POLICY2 = {
35135
+ health: 5000,
35136
+ short: 15000,
35137
+ normal: 120000,
35138
+ long: 900000
35139
+ };
35140
+ var RUNTIME_RPC_SHORT_METHODS2 = new Set([
35141
+ "shutdown",
35142
+ "runtimeStatus",
35143
+ "landscapeStatus",
35144
+ "repoList",
35145
+ "explorerStatus",
35146
+ "explorerServiceContract",
35147
+ "jobsList",
35148
+ "jobsStats",
35149
+ "ledgerState",
35150
+ "ledgerDrift",
35151
+ "stopExplorer",
35152
+ "revokeExplorerToken"
35153
+ ]);
35154
+ var RUNTIME_RPC_LONG_METHODS2 = new Set([
35155
+ "init",
35156
+ "sync",
35157
+ "prepare",
35158
+ "context",
35159
+ "checkpoint",
35160
+ "auditRun",
35161
+ "auditApprove",
35162
+ "recommendations",
35163
+ "book",
35164
+ "ledgerRebuild",
35165
+ "ledgerMigrate",
35166
+ "startDeveloperReviewRun",
35167
+ "runSignedDeveloperReviewAttestation"
35168
+ ]);
35169
+ function runtimeRpcMethodTimeout2(method, policy) {
35170
+ if (RUNTIME_RPC_SHORT_METHODS2.has(method))
35171
+ return policy.short;
35172
+ if (RUNTIME_RPC_LONG_METHODS2.has(method))
35173
+ return policy.long;
35174
+ return policy.normal;
35175
+ }
35176
+
34147
35177
  class RuntimeRpcClient2 {
34148
35178
  connection;
34149
- constructor(connection) {
35179
+ options;
35180
+ timeouts;
35181
+ constructor(connection, options = {}) {
34150
35182
  this.connection = connection;
35183
+ this.options = options;
35184
+ this.timeouts = { ...RUNTIME_RPC_CLIENT_TIMEOUT_POLICY2, ...options.timeouts };
34151
35185
  }
34152
35186
  async health() {
34153
- const response = await fetch(`${this.connection.url}health`, {
35187
+ return await this.request("health", this.timeouts.health, `${this.connection.url}health`, {
34154
35188
  headers: { "X-ArchContext-RPC-Version": RUNTIME_RPC_VERSION2 }
34155
35189
  });
34156
- return await response.json();
34157
35190
  }
34158
35191
  async shutdown() {
34159
35192
  return this.call("shutdown", []);
@@ -34237,8 +35270,11 @@ class RuntimeRpcClient2 {
34237
35270
  applyUpdate(root, input) {
34238
35271
  return this.call("applyUpdate", [root, input]);
34239
35272
  }
34240
- reconcileProjectionApply(root, lookupKey) {
34241
- return this.call("reconcileProjectionApply", [root, lookupKey]);
35273
+ inspectProjectionApplyReceipt(root, lookupKey) {
35274
+ return this.call("inspectProjectionApplyReceipt", [root, lookupKey]);
35275
+ }
35276
+ recoverProjectionApply(root, intent) {
35277
+ return this.call("recoverProjectionApply", [root, intent]);
34242
35278
  }
34243
35279
  ledgerState(root) {
34244
35280
  return this.call("ledgerState", [root]);
@@ -34316,7 +35352,7 @@ class RuntimeRpcClient2 {
34316
35352
  return unwrapRpcData2(await this.call("recoverDeveloperReviewRuns", [input]));
34317
35353
  }
34318
35354
  async call(method, params) {
34319
- const response = await fetch(`${this.connection.url}rpc`, {
35355
+ return await this.request(method, runtimeRpcMethodTimeout2(method, this.timeouts), `${this.connection.url}rpc`, {
34320
35356
  method: "POST",
34321
35357
  headers: {
34322
35358
  Authorization: `Bearer ${this.connection.token}`,
@@ -34325,7 +35361,35 @@ class RuntimeRpcClient2 {
34325
35361
  },
34326
35362
  body: JSON.stringify({ schemaVersion: RUNTIME_RPC_VERSION2, method, params })
34327
35363
  });
34328
- return await response.json();
35364
+ }
35365
+ async request(method, timeoutMs, url, init) {
35366
+ const controller = new AbortController;
35367
+ const startedAt = Date.now();
35368
+ let timedOut = false;
35369
+ const timer = setTimeout(() => {
35370
+ timedOut = true;
35371
+ controller.abort();
35372
+ }, timeoutMs);
35373
+ const callerSignal = this.options.signal;
35374
+ const onCallerAbort = () => controller.abort();
35375
+ if (callerSignal?.aborted)
35376
+ controller.abort();
35377
+ else
35378
+ callerSignal?.addEventListener("abort", onCallerAbort, { once: true });
35379
+ try {
35380
+ const response = await fetch(url, { ...init, signal: controller.signal });
35381
+ return await response.json();
35382
+ } catch (error) {
35383
+ const elapsedMs = Date.now() - startedAt;
35384
+ if (timedOut)
35385
+ throw new RuntimeRpcTransportError2("RPC_TIMEOUT", method, timeoutMs, elapsedMs);
35386
+ if (callerSignal?.aborted)
35387
+ throw new RuntimeRpcTransportError2("RPC_ABORTED", method, timeoutMs, elapsedMs);
35388
+ throw error;
35389
+ } finally {
35390
+ clearTimeout(timer);
35391
+ callerSignal?.removeEventListener("abort", onCallerAbort);
35392
+ }
34329
35393
  }
34330
35394
  }
34331
35395
  function defaultDaemonConnectionPath2(root = process.cwd()) {
@@ -35100,7 +36164,7 @@ async function runCliUnchecked(command2 = "help", args2 = [], cwd, deps = {}) {
35100
36164
  requestId: "help",
35101
36165
  data: {
35102
36166
  commands: ["capabilities", "projection", "init", "sync", "validate", "context", "status", "daemon", "state", "repo", "landscape", "ledger", "book", "recommendations", "explore", "prepare", "practices", "checkpoint", "hook", "hooks", "investigate", "agents", "jobs", "audit", "plan", "apply", "review", "complete", "github", "config", "mcp", "install", "uninstall", "doctor", "update", "paths", "privacy-audit", "export", "import", "resolve", "tunnel"],
35103
- examples: ["archctx init --name MyApp", "archctx state recover --from-git", "archctx ledger migrate --from-yaml --dry-run", "archctx ledger promote --mode authoritative --preflight --rollback-plan", "archctx book recommendations --open --explain", "archctx recommendations accept --id recommendation.<id> --reason 'Accepted after local readback.'", "archctx recommendations metrics", "archctx practices validate --strict", "archctx practices list --json", "archctx practices waivers", "archctx practices waive --practice-id modularity.no-new-cycle --owner team-architecture --reason 'External migration window requires this edge until cutover.' --review-at 2026-07-10T00:00:00.000Z --expires-at 2026-07-24T00:00:00.000Z --evidence-digest sha256:<64-hex> --subject module.a->module.b", "archctx checkpoint --task-session-id task_cli", "archctx investigate --runner-port codex", "archctx agents status --status queued,running", "archctx agents budget", "archctx hook enqueue --event post-edit --path src/app.ts", "archctx jobs list --status queued", "archctx audit run --reason 'quarterly architecture audit'", "archctx audit run --no-wait", "archctx audit list --status pending", "archctx audit show audit_run.<id>", "archctx audit approve audit_run.<id>", "archctx audit approve audit_run.<id> --confirm-public-repo public:<owner/repo>:<baseSha>:<runId>", "archctx audit approve audit_run.<id> --resume", "archctx hooks install --host codex", "archctx paths", "archctx update --check", "archctx doctor --check-updates", "archctx github connect", "archctx github status", "archctx daemon start", "archctx explore start --foreground", "archctx export likec4", "archctx import structurizr --content '<json>'", "archctx resolve --path packages/core/projection-engine/src/index.ts", "archctx tunnel"]
36167
+ examples: ["archctx init --name MyApp", "archctx state recover --from-git", "archctx ledger migrate --from-yaml --dry-run", "archctx ledger promote --mode authoritative --preflight --rollback-plan", "archctx book recommendations --open --explain", "archctx recommendations accept --id recommendation.<id> --reason 'Accepted after local readback.'", "archctx recommendations metrics", "archctx practices validate --strict", "archctx practices list --json", "archctx practices waivers", "archctx practices waive --practice-id modularity.no-new-cycle --owner team-architecture --reason 'External migration window requires this edge until cutover.' --review-at 2026-07-10T00:00:00.000Z --expires-at 2026-07-24T00:00:00.000Z --evidence-digest sha256:<64-hex> --subject module.a->module.b", "archctx checkpoint --task-session-id task_cli", "archctx investigate --runner-port codex", "archctx agents status --status queued,running", "archctx agents budget", "archctx hook enqueue --event post-edit --path src/app.ts", "archctx jobs list --status queued", "archctx audit run --reason 'quarterly architecture audit'", "archctx audit run --no-wait", "archctx audit list --status pending", "archctx audit show audit_run.<id>", "archctx audit approve audit_run.<id>", "archctx audit approve audit_run.<id> --confirm-public-repo public:<host>/<owner>/<repo>:<baseSha>:<runId>", "archctx audit approve audit_run.<id> --resume", "archctx hooks install --host codex", "archctx paths", "archctx update --check", "archctx doctor --check-updates", "archctx github connect", "archctx github status", "archctx daemon start", "archctx explore start --foreground", "archctx export likec4", "archctx import structurizr --content '<json>'", "archctx resolve --path packages/core/projection-engine/src/index.ts", "archctx tunnel"]
35104
36168
  }
35105
36169
  };
35106
36170
  }
@@ -35792,9 +36856,11 @@ async function runArchitectureDocsAdoptionCommand(args2, root, daemon, projectio
35792
36856
  });
35793
36857
  }
35794
36858
  async function runProjectionProtocolCommand(args2, cwd, daemon) {
35795
- if ((args2[0] ?? "run") !== "run") {
35796
- return errorEnvelope("projection", "AC_SCHEMA_INVALID", "projection requires run --request-json <ProjectionRequestV1>");
35797
- }
36859
+ const subcommand = args2[0] ?? "run";
36860
+ if (subcommand === "recover")
36861
+ return runProjectionApplyRecoveryCommand(args2.slice(1), cwd, daemon);
36862
+ if (subcommand !== "run")
36863
+ return errorEnvelope("projection", "AC_SCHEMA_INVALID", "projection requires run|recover --request-json <request>");
35798
36864
  const raw = readFlag(args2, "--request-json");
35799
36865
  if (!raw)
35800
36866
  return errorEnvelope("projection.run", "AC_SCHEMA_INVALID", "projection run requires --request-json");
@@ -35809,21 +36875,19 @@ async function runProjectionProtocolCommand(args2, cwd, daemon) {
35809
36875
  if (request.mode === "apply" && request.acceptedChange) {
35810
36876
  try {
35811
36877
  assertProjectionExpectedSnapshotAgainstModel(request, root, loadNativeModelFromArchContext(root));
36878
+ const inspected = await daemon.inspectProjectionApplyReceipt(root, projectionApplyLookupKey({
36879
+ repositoryId: request.expected.repositoryId,
36880
+ workspaceId: request.expected.workspaceId,
36881
+ acceptedChange: request.acceptedChange
36882
+ }));
36883
+ if (!inspected.ok)
36884
+ return inspected;
36885
+ if (inspected.data.found === true) {
36886
+ return errorEnvelope("projection.run", "AC_PRECONDITION_FAILED", "committed projection receipt requires explicit projection recover");
36887
+ }
35812
36888
  } catch (error) {
35813
36889
  return errorEnvelope("projection.run", "AC_PRECONDITION_FAILED", error instanceof Error ? error.message : String(error));
35814
36890
  }
35815
- const reconciled = await daemon.reconcileProjectionApply(root, projectionApplyLookupKey({
35816
- repositoryId: request.expected.repositoryId,
35817
- workspaceId: request.expected.workspaceId,
35818
- acceptedChange: request.acceptedChange
35819
- }));
35820
- if (!reconciled.ok)
35821
- return reconciled;
35822
- const data = reconciled.data;
35823
- if (data.found === true) {
35824
- const receipt = data.receipt;
35825
- return projectionProtocolResultEnvelope(data.refreshSignalsDelivered === true ? projectionResultDelivery(receipt.result, "applied", receipt.result.refreshSignals, request.requestId) : projectionResultDelivery(receipt.result, "noop", [], request.requestId));
35826
- }
35827
36891
  }
35828
36892
  let projection3;
35829
36893
  try {
@@ -35864,21 +36928,51 @@ async function runProjectionProtocolCommand(args2, cwd, daemon) {
35864
36928
  }
35865
36929
  if (request.mode === "apply" && !projection3.plan.drift.ok) {
35866
36930
  const changeSetId = `changeset.docs-projection-${projection3.plan.projectionDigest.replace(/^sha256:/, "").slice(0, 16)}`;
36931
+ let fixedPointProjection;
36932
+ try {
36933
+ fixedPointProjection = request.acceptedChange ? buildArchitectureDocsProjection(root, generatedAt, REPO_HARNESS_PROJECTION_PROFILE, projection3.files) : projection3;
36934
+ if (request.acceptedChange && (fixedPointProjection.plan.rejected.length > 0 || fixedPointProjection.plan.majorChange.mode !== "none" || fixedPointProjection.plan.refreshSignals.length > 0)) {
36935
+ return errorEnvelope("projection.run", "AC_PRECONDITION_FAILED", "accepted projection did not produce a no-accepted-change semantic fixed point");
36936
+ }
36937
+ } catch (error) {
36938
+ return errorEnvelope("projection.run", "AC_PRECONDITION_FAILED", error instanceof Error ? error.message : String(error));
36939
+ }
36940
+ const committedFiles = projectionProtocolFilesForExpectedOutput(root, fixedPointProjection);
36941
+ const committedSignals = request.acceptedChange ? projection3.plan.refreshSignals.map((signal) => ({
36942
+ ...signal,
36943
+ resultingDigests: fixedPointProjection.plan.architectureDigests
36944
+ })) : fixedPointProjection.plan.refreshSignals;
35867
36945
  const applyIdentity = request.acceptedChange ? createProjectionApplyIdentity({
35868
36946
  repositoryId: request.expected.repositoryId,
35869
36947
  workspaceId: request.expected.workspaceId,
35870
36948
  acceptedChange: request.acceptedChange,
35871
36949
  changeSetId,
35872
36950
  idempotencyKey: `idem_${changeSetId}`,
35873
- files: projectionProtocolFiles(projection3),
35874
- refreshSignals: projection3.plan.refreshSignals
36951
+ files: committedFiles,
36952
+ refreshSignals: committedSignals
35875
36953
  }) : undefined;
35876
- const appliedResult = projectionProtocolResult(request, projection3, "applied", projection3, applyIdentity);
35877
- const applyReceipt = applyIdentity ? { schemaVersion: "archcontext.projection-apply-receipt/v1", identity: applyIdentity, result: appliedResult } : undefined;
36954
+ const appliedResult = projectionProtocolResult(request, projection3, "applied", fixedPointProjection, applyIdentity, {
36955
+ files: committedFiles,
36956
+ refreshSignals: committedSignals
36957
+ });
36958
+ let recoveryBinding;
36959
+ if (applyIdentity) {
36960
+ try {
36961
+ recoveryBinding = createProjectionApplyRecoveryBinding(request, fixedPointProjection, appliedResult);
36962
+ } catch (error) {
36963
+ return errorEnvelope("projection.run", "AC_PRECONDITION_FAILED", error instanceof Error ? error.message : String(error));
36964
+ }
36965
+ }
36966
+ const applyReceipt = applyIdentity ? {
36967
+ schemaVersion: "archcontext.projection-apply-receipt/v1",
36968
+ identity: applyIdentity,
36969
+ result: appliedResult,
36970
+ recovery: recoveryBinding
36971
+ } : undefined;
35878
36972
  const planned = await daemon.planUpdate(root, {
35879
36973
  id: changeSetId,
35880
36974
  reason: { taskSessionId: request.requestId },
35881
- operations: [architectureDocsRenderProjectionOperation(root, projection3.files)],
36975
+ operations: [architectureDocsRenderProjectionOperation(root, fixedPointProjection.files)],
35882
36976
  worktreeDigestPrecondition: {
35883
36977
  profile: "architecture-documentation-projection",
35884
36978
  expectedDigest: request.expected.worktreeDigest
@@ -35912,18 +37006,120 @@ async function runProjectionProtocolCommand(args2, cwd, daemon) {
35912
37006
  if (!postApplyMatches) {
35913
37007
  return projectionProtocolResultEnvelope(projectionResultDelivery(appliedResult, "applied-reconcile-required", []));
35914
37008
  }
35915
- const reconciled = await daemon.reconcileProjectionApply(root, applyReceipt.identity.lookupKey);
35916
- if (!reconciled.ok)
35917
- return reconciled;
35918
- const consumption = reconciled.data;
35919
- if (consumption.found !== true || consumption.refreshSignalsDelivered !== true) {
37009
+ const delivery = await daemon.recoverProjectionApply(root, {
37010
+ schemaVersion: PROJECTION_APPLY_RECOVERY_INTENT_SCHEMA_VERSION,
37011
+ requestId: request.requestId,
37012
+ profile: REPO_HARNESS_PROJECTION_PROFILE,
37013
+ receipt: {
37014
+ lookupKey: applyReceipt.identity.lookupKey,
37015
+ applyId: applyReceipt.identity.applyId
37016
+ }
37017
+ });
37018
+ if (!delivery.ok)
37019
+ return delivery;
37020
+ const delivered = delivery.data;
37021
+ if (delivered.found !== true || delivered.refreshSignalsDelivered !== true) {
35920
37022
  return errorEnvelope("projection.run", "AC_PRECONDITION_FAILED", "committed projection apply receipt was not available for first delivery");
35921
37023
  }
35922
- return projectionProtocolResultEnvelope(consumption.receipt.result);
37024
+ return projectionProtocolResultEnvelope(projectionResultDelivery(appliedResult, "applied", delivered.receipt.result.refreshSignals, request.requestId));
35923
37025
  }
35924
37026
  const status = projection3.plan.drift.ok ? "noop" : "planned";
35925
37027
  return projectionProtocolEnvelope(request, projection3, status);
35926
37028
  }
37029
+ async function runProjectionApplyRecoveryCommand(args2, cwd, daemon) {
37030
+ const raw = readFlag(args2, "--request-json");
37031
+ if (!raw)
37032
+ return errorEnvelope("projection.recover", "AC_SCHEMA_INVALID", "projection recover requires --request-json");
37033
+ let intent;
37034
+ try {
37035
+ intent = parseProjectionApplyRecoveryIntent(raw);
37036
+ } catch (error) {
37037
+ return errorEnvelope("projection.recover", "AC_SCHEMA_INVALID", error instanceof Error ? error.message : String(error));
37038
+ }
37039
+ return recoverProjectionApplyIntent(intent, findRepositoryRoot(cwd), daemon);
37040
+ }
37041
+ async function recoverProjectionApplyIntent(intent, root, daemon) {
37042
+ let recovered;
37043
+ try {
37044
+ recovered = await daemon.recoverProjectionApply(root, intent);
37045
+ } catch (error) {
37046
+ return errorEnvelope("projection.recover", "AC_PRECONDITION_FAILED", error instanceof Error ? error.message : String(error));
37047
+ }
37048
+ if (!recovered.ok)
37049
+ return recovered;
37050
+ const consumption = recovered.data;
37051
+ if (consumption.found !== true || !consumption.receipt || !consumption.proof) {
37052
+ return errorEnvelope("projection.recover", "AC_PRECONDITION_FAILED", "committed projection apply receipt was unavailable for recovery delivery");
37053
+ }
37054
+ return projectionApplyRecoveryResultEnvelope({
37055
+ schemaVersion: PROJECTION_APPLY_RECOVERY_RESULT_SCHEMA_VERSION,
37056
+ proof: consumption.proof,
37057
+ refreshSignals: consumption.refreshSignalsDelivered ? consumption.receipt.result.refreshSignals : []
37058
+ });
37059
+ }
37060
+ function createProjectionApplyRecoveryBinding(request, projection3, result) {
37061
+ const provenance = projection3.plan.provenance;
37062
+ return {
37063
+ schemaVersion: "archcontext.projection-apply-recovery-binding/v1",
37064
+ targets: [...request.targets],
37065
+ changedPaths: [...request.changedPaths],
37066
+ originalExpectedSnapshot: { ...request.expected },
37067
+ expectedResultingDigests: projection3.plan.architectureDigests,
37068
+ rendererVersion: provenance.rendererVersion,
37069
+ layoutVersion: provenance.layoutVersion,
37070
+ generatedFrom: provenance.generatedFrom,
37071
+ ownedOutputDigest: projectionOwnedOutputDigest(projection3),
37072
+ receiptDigest: result.receiptDigest
37073
+ };
37074
+ }
37075
+ function projectionOwnedOutputDigest(projection3) {
37076
+ return digestJson({
37077
+ schemaVersion: "archcontext.projection-owned-output/v1",
37078
+ files: projection3.files.map((file) => ({ path: file.path, body: file.body })).sort((left, right) => left.path.localeCompare(right.path))
37079
+ });
37080
+ }
37081
+ function projectionApplyRecoveryResultEnvelope(result) {
37082
+ const issues = projectionApplyRecoveryResultInvariantIssues(result);
37083
+ return issues.length === 0 ? okEnvelope("projection.recover", result) : errorEnvelope("projection.recover", "AC_SCHEMA_INVALID", `projection recovery result invariant failed: ${issues.join("; ")}`);
37084
+ }
37085
+ function parseProjectionApplyRecoveryIntent(raw) {
37086
+ let value;
37087
+ try {
37088
+ value = JSON.parse(raw);
37089
+ } catch {
37090
+ throw new Error("projection recovery intent is not valid JSON");
37091
+ }
37092
+ if (!value || typeof value !== "object" || Array.isArray(value))
37093
+ throw new Error("projection recovery intent must be an object");
37094
+ const input = value;
37095
+ const allowedKeys = new Set(["schemaVersion", "requestId", "profile", "receipt"]);
37096
+ const unsupported = Object.keys(input).find((key) => !allowedKeys.has(key));
37097
+ if (unsupported)
37098
+ throw new Error(`projection recovery intent contains unsupported property: ${unsupported}`);
37099
+ if (input.schemaVersion !== PROJECTION_APPLY_RECOVERY_INTENT_SCHEMA_VERSION)
37100
+ throw new Error("projection recovery intent schemaVersion mismatch");
37101
+ if (input.profile !== REPO_HARNESS_PROJECTION_PROFILE)
37102
+ throw new Error(`projection recovery intent profile must be ${REPO_HARNESS_PROJECTION_PROFILE}`);
37103
+ if (typeof input.requestId !== "string")
37104
+ throw new Error("projection recovery intent requestId is required");
37105
+ const receipt = input.receipt;
37106
+ if (!receipt || typeof receipt !== "object" || Array.isArray(receipt))
37107
+ throw new Error("projection recovery intent receipt must be an object");
37108
+ const receiptInput = receipt;
37109
+ const receiptUnsupported = Object.keys(receiptInput).find((key) => key !== "lookupKey" && key !== "applyId");
37110
+ if (receiptUnsupported)
37111
+ throw new Error(`projection recovery intent receipt contains unsupported property: ${receiptUnsupported}`);
37112
+ for (const field of ["lookupKey", "applyId"]) {
37113
+ if (typeof receiptInput[field] !== "string" || !/^sha256:[a-f0-9]{64}$/.test(receiptInput[field])) {
37114
+ throw new Error(`projection recovery intent receipt.${field} is invalid`);
37115
+ }
37116
+ }
37117
+ const intent = input;
37118
+ const issues = projectionApplyRecoveryIntentInvariantIssues(intent);
37119
+ if (issues.length > 0)
37120
+ throw new Error(`projection recovery intent invariant failed: ${issues.join("; ")}`);
37121
+ return intent;
37122
+ }
35927
37123
  function parseProjectionProtocolRequest(raw) {
35928
37124
  let value;
35929
37125
  try {
@@ -36019,10 +37215,10 @@ function projectionProtocolHumanStatus(request, projection3) {
36019
37215
  function projectionProtocolEnvelope(request, input, status, output = input) {
36020
37216
  return projectionProtocolResultEnvelope(projectionProtocolResult(request, input, status, output));
36021
37217
  }
36022
- function projectionProtocolResult(request, input, status, output = input, applyReceipt) {
37218
+ function projectionProtocolResult(request, input, status, output = input, applyReceipt, overrides) {
36023
37219
  const inputSnapshot = projectionProtocolSnapshot(request, input.plan.provenance);
36024
37220
  const outputSnapshot = projectionProtocolSnapshot(request, output.plan.provenance);
36025
- const files = projectionProtocolFiles(input);
37221
+ const files = overrides?.files ?? projectionProtocolFiles(input);
36026
37222
  const affectedNodeIds = projectionProtocolAffectedNodes(input);
36027
37223
  const requestPayloadDigest = digestJson(request);
36028
37224
  const humanActions = [];
@@ -36035,7 +37231,7 @@ function projectionProtocolResult(request, input, status, output = input, applyR
36035
37231
  requestPayloadDigest
36036
37232
  });
36037
37233
  }
36038
- const refreshSignals = [...output.plan.refreshSignals].sort((left, right) => left.signalId < right.signalId ? -1 : left.signalId > right.signalId ? 1 : 0);
37234
+ const refreshSignals = [...overrides?.refreshSignals ?? output.plan.refreshSignals].sort((left, right) => left.signalId < right.signalId ? -1 : left.signalId > right.signalId ? 1 : 0);
36039
37235
  const withoutReceipt = {
36040
37236
  schemaVersion: "archcontext.projection-result/v2",
36041
37237
  requestId: request.requestId,
@@ -36103,6 +37299,19 @@ function projectionProtocolFiles(projection3) {
36103
37299
  return [];
36104
37300
  }).sort((left, right) => left.path < right.path ? -1 : left.path > right.path ? 1 : 0);
36105
37301
  }
37302
+ function projectionProtocolFilesForExpectedOutput(root, projection3) {
37303
+ return projection3.files.flatMap((file) => {
37304
+ const preimageDigest = currentProjectionFileDigest(root, file.path);
37305
+ const outputDigest = digestJson({ path: file.path, body: file.body });
37306
+ if (preimageDigest === outputDigest)
37307
+ return [];
37308
+ return preimageDigest === "missing" ? [{ path: file.path, action: "create", preimageDigest: null, outputDigest }] : [{ path: file.path, action: "update", preimageDigest, outputDigest }];
37309
+ }).sort((left, right) => left.path.localeCompare(right.path));
37310
+ }
37311
+ function currentProjectionFileDigest(root, path) {
37312
+ const absolute = resolve19(root, path);
37313
+ return existsSync17(absolute) ? digestJson({ path, body: readFileSync16(absolute, "utf8") }) : "missing";
37314
+ }
36106
37315
  function projectionProtocolAffectedNodes(projection3) {
36107
37316
  const targetNodes = new Map(projection3.plan.targets.flatMap((target) => target.scope.kind === "entity" ? [[target.targetId, target.scope.id]] : []));
36108
37317
  return [...new Set([
@@ -37148,7 +38357,7 @@ async function writeGithubDeveloperReviewState(cwd, state) {
37148
38357
  assertNoCliSecretMaterial(serialized);
37149
38358
  writeFileSync8(path, serialized, { mode: 384 });
37150
38359
  if (process.platform !== "win32")
37151
- chmodSync5(path, 384);
38360
+ chmodSync6(path, 384);
37152
38361
  return { state, path };
37153
38362
  }
37154
38363
  function readGithubDeveloperReviewState(path) {
@@ -37222,7 +38431,7 @@ function writeGithubConnection(path, record2) {
37222
38431
  assertNoCliSecretMaterial(serialized);
37223
38432
  writeFileSync8(path, serialized, { mode: 384 });
37224
38433
  if (process.platform !== "win32")
37225
- chmodSync5(path, 384);
38434
+ chmodSync6(path, 384);
37226
38435
  }
37227
38436
  function sanitizeGithubConnection(record2, connectionPath) {
37228
38437
  return {