archctx 0.4.7 → 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.
|
|
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-
|
|
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
|
|
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
|
|
8458
|
+
return architectureDocumentationStickyProvenanceDigest(prior) === architectureDocumentationStickyProvenanceDigest(current) ? prior : current;
|
|
8343
8459
|
} catch {
|
|
8344
8460
|
return current;
|
|
8345
8461
|
}
|
|
8346
8462
|
}
|
|
8347
|
-
function
|
|
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
|
|
@@ -13959,6 +14073,12 @@ var LOCAL_SQLITE_MIGRATIONS = [
|
|
|
13959
14073
|
)`,
|
|
13960
14074
|
"CREATE INDEX IF NOT EXISTS idx_projection_apply_receipts_journal ON projection_apply_receipts(journal_id)"
|
|
13961
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
|
+
]
|
|
13962
14082
|
}
|
|
13963
14083
|
];
|
|
13964
14084
|
var ARCHCONTEXT_STATE_DIR_ENV = "ARCHCONTEXT_STATE_DIR";
|
|
@@ -14128,7 +14248,6 @@ function recoverRuntimeStateTarget(input) {
|
|
|
14128
14248
|
ensurePrivateDir(stagingDir);
|
|
14129
14249
|
assertRuntimeStateRecoveryPrivatePermissions(stagingDir, 448);
|
|
14130
14250
|
migrateSqliteDatabaseSync(stagingPath);
|
|
14131
|
-
compactSqliteDatabase(stagingPath);
|
|
14132
14251
|
const stagingIntegrity = assertCurrentLocalStore(stagingPath);
|
|
14133
14252
|
receiptPath = join2(quarantineDirectory, RUNTIME_STATE_RECOVERY_RECEIPT_FILE);
|
|
14134
14253
|
writeRuntimeStateRecoveryReceipt(receiptPath, {
|
|
@@ -14257,7 +14376,6 @@ function migrateLegacyLocalStoreIfNeeded(root = process.cwd(), env = process.env
|
|
|
14257
14376
|
integrityCheck.legacy = vacuumLegacySqliteInto(paths.legacyLocalStorePath, stagingPath);
|
|
14258
14377
|
makePrivateFile(stagingPath);
|
|
14259
14378
|
migrateSqliteDatabaseSync(stagingPath);
|
|
14260
|
-
compactSqliteDatabase(stagingPath);
|
|
14261
14379
|
integrityCheck.staging = assertCurrentLocalStore(stagingPath);
|
|
14262
14380
|
publishStagedLocalStore(stagingPath, paths.localStorePath);
|
|
14263
14381
|
integrityCheck.target = assertCurrentLocalStore(paths.localStorePath);
|
|
@@ -14278,10 +14396,17 @@ function migrateLegacyLocalStoreIfNeeded(root = process.cwd(), env = process.env
|
|
|
14278
14396
|
function upgradeExistingLocalStoreTarget(paths, integrityCheck) {
|
|
14279
14397
|
const lock = acquireLegacyMigrationLock(paths);
|
|
14280
14398
|
try {
|
|
14281
|
-
|
|
14282
|
-
|
|
14283
|
-
|
|
14284
|
-
|
|
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);
|
|
14285
14410
|
integrityCheck.target = assertCurrentLocalStore(paths.localStorePath);
|
|
14286
14411
|
delete integrityCheck.error;
|
|
14287
14412
|
const markerPath = writeLegacyMigrationMarker(paths, integrityCheck, []);
|
|
@@ -14909,30 +15034,41 @@ function readAppliedLocalSqliteMigrations(db) {
|
|
|
14909
15034
|
return new Set;
|
|
14910
15035
|
return new Set(db.prepare("SELECT id FROM schema_migrations").all().map((row) => String(row.id)));
|
|
14911
15036
|
}
|
|
14912
|
-
function
|
|
14913
|
-
if (
|
|
14914
|
-
|
|
14915
|
-
|
|
14916
|
-
|
|
14917
|
-
|
|
14918
|
-
|
|
14919
|
-
|
|
14920
|
-
prepare: (sql) => db2.prepare(sql),
|
|
14921
|
-
close: () => db2.close()
|
|
14922
|
-
};
|
|
14923
|
-
} catch (error) {
|
|
14924
|
-
if (error.code !== "ERR_UNKNOWN_BUILTIN_MODULE" && error.code !== "MODULE_NOT_FOUND") {
|
|
14925
|
-
throw error;
|
|
14926
|
-
}
|
|
14927
|
-
}
|
|
14928
|
-
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) {
|
|
14929
15045
|
const db = new bunSqlite.Database(databasePath);
|
|
15046
|
+
const releaseStatements = () => db.clearQueryCache();
|
|
14930
15047
|
return {
|
|
14931
15048
|
exec: (sql) => db.exec(sql),
|
|
14932
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),
|
|
14933
15062
|
close: () => db.close()
|
|
14934
15063
|
};
|
|
14935
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
|
+
}
|
|
14936
15072
|
function legacyMigrationResult(migrated, skippedReason, paths, copiedFiles, details) {
|
|
14937
15073
|
return {
|
|
14938
15074
|
schemaVersion: "archcontext.legacy-local-store-migration/v1",
|
|
@@ -14979,7 +15115,6 @@ function runtimeStateRecoveryStartupProbe(paths, sourceFiles) {
|
|
|
14979
15115
|
makePrivateFile(target);
|
|
14980
15116
|
}
|
|
14981
15117
|
migrateSqliteDatabaseSync(probePath);
|
|
14982
|
-
compactSqliteDatabase(probePath);
|
|
14983
15118
|
assertCurrentLocalStore(probePath);
|
|
14984
15119
|
return { ok: true };
|
|
14985
15120
|
} catch (error) {
|
|
@@ -15194,20 +15329,16 @@ function assertCurrentLocalStoreSchema(db, path) {
|
|
|
15194
15329
|
throw new Error(`SQLite local store schema incomplete for ${path}: missing migrations ${missingMigrations.join(", ")}`);
|
|
15195
15330
|
}
|
|
15196
15331
|
}
|
|
15197
|
-
function assertUpgradeableLocalStoreTarget(path) {
|
|
15198
|
-
const
|
|
15199
|
-
|
|
15200
|
-
|
|
15201
|
-
|
|
15202
|
-
|
|
15203
|
-
if (!hasArchContextMarker) {
|
|
15204
|
-
throw new Error(`SQLite target is not an ArchContext local store candidate: ${path}`);
|
|
15205
|
-
}
|
|
15206
|
-
if (integrity !== "ok")
|
|
15207
|
-
throw new Error(`SQLite integrity_check failed for ${path}: ${integrity}`);
|
|
15208
|
-
} finally {
|
|
15209
|
-
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}`);
|
|
15210
15338
|
}
|
|
15339
|
+
if (integrity !== "ok")
|
|
15340
|
+
throw new Error(`SQLite integrity_check failed for ${path}: ${integrity}`);
|
|
15341
|
+
return integrity;
|
|
15211
15342
|
}
|
|
15212
15343
|
function assertTrustedLegacyLocalStoreSource(paths) {
|
|
15213
15344
|
const stat = lstatSync2(paths.legacyLocalStorePath);
|
|
@@ -15236,23 +15367,29 @@ function vacuumLegacySqliteInto(sourcePath, targetPath) {
|
|
|
15236
15367
|
}
|
|
15237
15368
|
function migrateSqliteDatabaseSync(databasePath) {
|
|
15238
15369
|
const db = openSqliteDatabaseSync(databasePath);
|
|
15370
|
+
let compacted = false;
|
|
15239
15371
|
try {
|
|
15240
|
-
|
|
15241
|
-
|
|
15242
|
-
backfillArchitectureChangeFeed(db);
|
|
15372
|
+
migrateOpenedSqliteDatabase(db);
|
|
15373
|
+
compacted = true;
|
|
15243
15374
|
} finally {
|
|
15244
15375
|
db.close();
|
|
15245
15376
|
}
|
|
15377
|
+
if (compacted)
|
|
15378
|
+
removeSqliteSidecars(databasePath);
|
|
15246
15379
|
}
|
|
15247
|
-
function
|
|
15248
|
-
|
|
15249
|
-
|
|
15250
|
-
|
|
15251
|
-
|
|
15252
|
-
|
|
15253
|
-
|
|
15254
|
-
|
|
15255
|
-
|
|
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) {
|
|
15256
15393
|
for (const suffix of ["-wal", "-shm"])
|
|
15257
15394
|
rmSync(`${databasePath}${suffix}`, { force: true });
|
|
15258
15395
|
}
|
|
@@ -15939,15 +16076,15 @@ function runCodeGraphCli2(binary, workspaceRoot, args) {
|
|
|
15939
16076
|
// packages/local-runtime/runtime-daemon/src/index.ts
|
|
15940
16077
|
import { randomBytes } from "node:crypto";
|
|
15941
16078
|
import { execFileSync as execFileSync7 } from "node:child_process";
|
|
15942
|
-
import { chmodSync as
|
|
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";
|
|
15943
16080
|
import { createServer } from "node:http";
|
|
15944
16081
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
15945
|
-
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";
|
|
15946
16083
|
|
|
15947
16084
|
// packages/core/changeset-engine/src/index.ts
|
|
15948
16085
|
init_src();
|
|
15949
|
-
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";
|
|
15950
|
-
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";
|
|
15951
16088
|
|
|
15952
16089
|
// packages/core/policy-engine/src/index.ts
|
|
15953
16090
|
import { existsSync as existsSync7, realpathSync as realpathSync6 } from "node:fs";
|
|
@@ -16218,14 +16355,57 @@ function assertSafeTarget(root, path, scope) {
|
|
|
16218
16355
|
throw new Error(`Refusing to write symlink target: ${path}`);
|
|
16219
16356
|
}
|
|
16220
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
|
+
}
|
|
16221
16399
|
function assertExpectedHash(path, expectedHash) {
|
|
16222
16400
|
const actual = digestJson({ body: readFileSync9(path, "utf8") });
|
|
16223
16401
|
if (expectedHash !== actual)
|
|
16224
16402
|
throw new Error(`Expected hash mismatch: ${path}`);
|
|
16225
16403
|
}
|
|
16226
|
-
function atomicWriteFile(path, tempPath, body) {
|
|
16404
|
+
function atomicWriteFile(path, tempPath, body, mode) {
|
|
16227
16405
|
mkdirSync3(dirname5(path), { recursive: true });
|
|
16228
|
-
writeFileSync2(tempPath, body, "utf8");
|
|
16406
|
+
writeFileSync2(tempPath, body, mode === undefined ? "utf8" : { encoding: "utf8", mode, flag: "wx" });
|
|
16407
|
+
if (mode !== undefined)
|
|
16408
|
+
chmodSync2(tempPath, mode);
|
|
16229
16409
|
fsyncFile2(tempPath);
|
|
16230
16410
|
renameSync2(tempPath, path);
|
|
16231
16411
|
fsyncDirectory2(dirname5(path));
|
|
@@ -16511,7 +16691,7 @@ init_src();
|
|
|
16511
16691
|
// packages/core/practice-catalog/src/index.ts
|
|
16512
16692
|
init_src();
|
|
16513
16693
|
import { existsSync as existsSync9, lstatSync as lstatSync4, readdirSync as readdirSync6, readFileSync as readFileSync10, realpathSync as realpathSync7 } from "node:fs";
|
|
16514
|
-
import { dirname as dirname6, relative as
|
|
16694
|
+
import { dirname as dirname6, relative as relative7, resolve as resolve12, sep as sep5 } from "node:path";
|
|
16515
16695
|
import { fileURLToPath } from "node:url";
|
|
16516
16696
|
var PRACTICE_CATALOG_VERSION = "2026.06.0";
|
|
16517
16697
|
var BUILTIN_PRACTICE_ASSETS_DIR = resolve12(dirname6(fileURLToPath(import.meta.url)), "../assets");
|
|
@@ -16763,7 +16943,7 @@ function loadRepoOverlayAssets(root, errors) {
|
|
|
16763
16943
|
return [];
|
|
16764
16944
|
const out = [];
|
|
16765
16945
|
for (const path of listDataFiles(overlayRoot, overlayRoot, errors)) {
|
|
16766
|
-
const relativePath = `.archcontext/practices/${
|
|
16946
|
+
const relativePath = `.archcontext/practices/${relative7(overlayRoot, path).split(sep5).join("/")}`;
|
|
16767
16947
|
try {
|
|
16768
16948
|
assertRepoRelativePath(relativePath);
|
|
16769
16949
|
assertRealChild(root, path);
|
|
@@ -17170,11 +17350,11 @@ function safeRealpath(path) {
|
|
|
17170
17350
|
}
|
|
17171
17351
|
}
|
|
17172
17352
|
function isRealChild(rootReal, pathReal) {
|
|
17173
|
-
const rel =
|
|
17174
|
-
return rel === "" || !rel.startsWith("..") && !rel.includes(`..${
|
|
17353
|
+
const rel = relative7(rootReal, pathReal);
|
|
17354
|
+
return rel === "" || !rel.startsWith("..") && !rel.includes(`..${sep5}`) && !resolve12(rel).startsWith("..");
|
|
17175
17355
|
}
|
|
17176
17356
|
function displayPath(path) {
|
|
17177
|
-
return path.split(
|
|
17357
|
+
return path.split(sep5).join("/");
|
|
17178
17358
|
}
|
|
17179
17359
|
function isRecord2(value) {
|
|
17180
17360
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
@@ -17736,7 +17916,7 @@ function checkResult(input, result) {
|
|
|
17736
17916
|
// packages/core/practice-engine/src/enforcement.ts
|
|
17737
17917
|
init_src();
|
|
17738
17918
|
import { existsSync as existsSync10, lstatSync as lstatSync5, readdirSync as readdirSync7, readFileSync as readFileSync11 } from "node:fs";
|
|
17739
|
-
import { basename as basename5, join as join5, relative as
|
|
17919
|
+
import { basename as basename5, join as join5, relative as relative8, resolve as resolve13, sep as sep6 } from "node:path";
|
|
17740
17920
|
var ENFORCEMENT_RANK = { advisory: 0, checkpoint: 1, complete: 2 };
|
|
17741
17921
|
var POLICY_MODES = new Set(["advisory", "active", "fail-open", "fail-closed"]);
|
|
17742
17922
|
var DEFAULT_POLICY = {
|
|
@@ -18168,8 +18348,8 @@ function assertRepoPolicyFile(root, path, relativePath) {
|
|
|
18168
18348
|
throw new Error(`practice-policy-symlink-denied: ${relativePath}`);
|
|
18169
18349
|
const rootResolved = resolve13(root);
|
|
18170
18350
|
const pathResolved = resolve13(path);
|
|
18171
|
-
const rel =
|
|
18172
|
-
if (rel === "" || rel.startsWith("..") || rel.includes(`..${
|
|
18351
|
+
const rel = relative8(rootResolved, pathResolved);
|
|
18352
|
+
if (rel === "" || rel.startsWith("..") || rel.includes(`..${sep6}`))
|
|
18173
18353
|
throw new Error(`practice-policy-path-escape: ${relativePath}`);
|
|
18174
18354
|
if (basename5(pathResolved).startsWith("."))
|
|
18175
18355
|
throw new Error(`practice-policy-hidden-file-denied: ${relativePath}`);
|
|
@@ -18900,6 +19080,32 @@ function createInterventionProposal(input) {
|
|
|
18900
19080
|
}
|
|
18901
19081
|
|
|
18902
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
|
+
}
|
|
18903
19109
|
async function compileTaskContext(input) {
|
|
18904
19110
|
const model = await input.modelStore.validateModel(input.workspace);
|
|
18905
19111
|
const ledgerReadback = await input.architectureLedger?.queryForTask({
|
|
@@ -19110,7 +19316,6 @@ async function compileLandscapeTaskContext(input) {
|
|
|
19110
19316
|
});
|
|
19111
19317
|
}
|
|
19112
19318
|
function finalizeContext(context, digests) {
|
|
19113
|
-
const byteLength = Buffer.byteLength(JSON.stringify(context), "utf8");
|
|
19114
19319
|
const withMetadata = {
|
|
19115
19320
|
...context,
|
|
19116
19321
|
extensions: {
|
|
@@ -19126,18 +19331,10 @@ function finalizeContext(context, digests) {
|
|
|
19126
19331
|
codeFactsMode: digests.codeFactsMode,
|
|
19127
19332
|
landscapeDigest: digests.landscapeDigest,
|
|
19128
19333
|
activeRepositories: digests.activeRepositories,
|
|
19129
|
-
crossRepoRelations: digests.crossRepoRelations
|
|
19130
|
-
byteLength,
|
|
19131
|
-
budgetExceeded: byteLength > digests.maxBytes
|
|
19132
|
-
}
|
|
19133
|
-
};
|
|
19134
|
-
return {
|
|
19135
|
-
...withMetadata,
|
|
19136
|
-
extensions: {
|
|
19137
|
-
...withMetadata.extensions,
|
|
19138
|
-
digest: digestJson(withMetadata)
|
|
19334
|
+
crossRepoRelations: digests.crossRepoRelations
|
|
19139
19335
|
}
|
|
19140
19336
|
};
|
|
19337
|
+
return finalizeContextBudgetMetadata(withMetadata, digests.maxBytes);
|
|
19141
19338
|
}
|
|
19142
19339
|
function trimPracticeGuidance(guidance, maxMatches) {
|
|
19143
19340
|
const matches2 = guidance.matches.slice(0, maxMatches);
|
|
@@ -21779,6 +21976,7 @@ function clampInteger(value, min, max) {
|
|
|
21779
21976
|
|
|
21780
21977
|
// packages/local-runtime/runtime-daemon/src/index.ts
|
|
21781
21978
|
init_src();
|
|
21979
|
+
init_src();
|
|
21782
21980
|
|
|
21783
21981
|
// packages/local-runtime/git-adapter/src/index.ts
|
|
21784
21982
|
import { execFileSync as execFileSync5, spawnSync as spawnSync2 } from "node:child_process";
|
|
@@ -22067,11 +22265,11 @@ function isGitWorktreeError(error) {
|
|
|
22067
22265
|
// packages/local-runtime/local-store-sqlite/src/index.ts
|
|
22068
22266
|
import { execFileSync as execFileSync6 } from "node:child_process";
|
|
22069
22267
|
import { createHash as createHash9, randomUUID as randomUUID2 } from "node:crypto";
|
|
22070
|
-
import { chmodSync as
|
|
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";
|
|
22071
22269
|
import { readdir, readFile } from "node:fs/promises";
|
|
22072
22270
|
import { createRequire as createRequire5 } from "node:module";
|
|
22073
22271
|
import { homedir as homedir2 } from "node:os";
|
|
22074
|
-
import { dirname as dirname8, isAbsolute as
|
|
22272
|
+
import { dirname as dirname8, isAbsolute as isAbsolute6, join as join7, relative as relative9, resolve as resolve16 } from "node:path";
|
|
22075
22273
|
init_src();
|
|
22076
22274
|
var runtimeRequire2 = createRequire5(import.meta.url);
|
|
22077
22275
|
var SQLITE_SIDECAR_SUFFIXES2 = ["", "-wal", "-shm"];
|
|
@@ -22887,6 +23085,12 @@ var LOCAL_SQLITE_MIGRATIONS2 = [
|
|
|
22887
23085
|
)`,
|
|
22888
23086
|
"CREATE INDEX IF NOT EXISTS idx_projection_apply_receipts_journal ON projection_apply_receipts(journal_id)"
|
|
22889
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
|
+
]
|
|
22890
23094
|
}
|
|
22891
23095
|
];
|
|
22892
23096
|
var CHANGESET_STARTUP_CLEANUP_LIMIT = 100;
|
|
@@ -22984,7 +23188,6 @@ function migrateLegacyLocalStoreIfNeeded2(root = process.cwd(), env = process.en
|
|
|
22984
23188
|
integrityCheck.legacy = vacuumLegacySqliteInto2(paths.legacyLocalStorePath, stagingPath);
|
|
22985
23189
|
makePrivateFile2(stagingPath);
|
|
22986
23190
|
migrateSqliteDatabaseSync2(stagingPath);
|
|
22987
|
-
compactSqliteDatabase2(stagingPath);
|
|
22988
23191
|
integrityCheck.staging = assertCurrentLocalStore2(stagingPath);
|
|
22989
23192
|
publishStagedLocalStore2(stagingPath, paths.localStorePath);
|
|
22990
23193
|
integrityCheck.target = assertCurrentLocalStore2(paths.localStorePath);
|
|
@@ -23005,10 +23208,17 @@ function migrateLegacyLocalStoreIfNeeded2(root = process.cwd(), env = process.en
|
|
|
23005
23208
|
function upgradeExistingLocalStoreTarget2(paths, integrityCheck) {
|
|
23006
23209
|
const lock = acquireLegacyMigrationLock2(paths);
|
|
23007
23210
|
try {
|
|
23008
|
-
|
|
23009
|
-
|
|
23010
|
-
|
|
23011
|
-
|
|
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);
|
|
23012
23222
|
integrityCheck.target = assertCurrentLocalStore2(paths.localStorePath);
|
|
23013
23223
|
delete integrityCheck.error;
|
|
23014
23224
|
const markerPath = writeLegacyMigrationMarker2(paths, integrityCheck, []);
|
|
@@ -23091,6 +23301,14 @@ class SqliteLocalStore {
|
|
|
23091
23301
|
updatedAt: String(row.updated_at)
|
|
23092
23302
|
}));
|
|
23093
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
|
+
}
|
|
23094
23312
|
async enqueueRuntimeAgentJob(input) {
|
|
23095
23313
|
if (input.job.status !== "queued")
|
|
23096
23314
|
throw new Error("runtime-agent-job-enqueue-requires-queued-status");
|
|
@@ -23201,7 +23419,7 @@ class SqliteLocalStore {
|
|
|
23201
23419
|
maxAttempts,
|
|
23202
23420
|
debounceUntil: input.debounceUntil
|
|
23203
23421
|
});
|
|
23204
|
-
const inserted =
|
|
23422
|
+
const inserted = runtimeAgentJobInScope(db, runtimeAgentJobScope(input.job), input.job.jobId);
|
|
23205
23423
|
if (!inserted)
|
|
23206
23424
|
throw new Error(`runtime-agent-job-insert-failed: ${input.job.jobId}`);
|
|
23207
23425
|
const backpressure = maxQueuedJobs === undefined ? undefined : {
|
|
@@ -23285,21 +23503,15 @@ class SqliteLocalStore {
|
|
|
23285
23503
|
const nextAttempt = record2.attemptCount + 1;
|
|
23286
23504
|
if (nextAttempt > record2.maxAttempts) {
|
|
23287
23505
|
const failed = runtimeAgentJobWithPatch(record2.job, { status: "failed", updatedAt: input.now });
|
|
23288
|
-
db.
|
|
23289
|
-
|
|
23290
|
-
leased_at = NULL, lease_expires_at = NULL, last_error = ?, dead_lettered_at = ?
|
|
23291
|
-
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]);
|
|
23292
23508
|
db.exec("COMMIT");
|
|
23293
23509
|
return;
|
|
23294
23510
|
}
|
|
23295
23511
|
const running = runtimeAgentJobWithPatch(record2.job, { status: "running", updatedAt: input.now });
|
|
23296
|
-
db.
|
|
23297
|
-
|
|
23298
|
-
|
|
23299
|
-
WHERE job_id = ?`).run("running", stableJson2(running), input.now, nextAttempt, input.workerId, input.now, leaseExpiresAt, record2.job.jobId);
|
|
23300
|
-
const claimed = runtimeAgentJobById(db, record2.job.jobId);
|
|
23301
|
-
if (!claimed)
|
|
23302
|
-
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);
|
|
23303
23515
|
db.exec("COMMIT");
|
|
23304
23516
|
return claimed;
|
|
23305
23517
|
} catch (error) {
|
|
@@ -23309,9 +23521,7 @@ class SqliteLocalStore {
|
|
|
23309
23521
|
}
|
|
23310
23522
|
async completeRuntimeAgentJob(input) {
|
|
23311
23523
|
const db = await this.database();
|
|
23312
|
-
const record2 =
|
|
23313
|
-
if (!record2)
|
|
23314
|
-
throw new Error(`runtime-agent-job-not-found: ${input.jobId}`);
|
|
23524
|
+
const record2 = requireRuntimeAgentJobInScope(db, input, input.jobId);
|
|
23315
23525
|
if (record2.job.status !== "running")
|
|
23316
23526
|
throw new Error(`runtime-agent-job-complete-requires-running: ${input.jobId}`);
|
|
23317
23527
|
if (input.workerId && record2.leaseOwner && record2.leaseOwner !== input.workerId) {
|
|
@@ -23324,52 +23534,31 @@ class SqliteLocalStore {
|
|
|
23324
23534
|
outputDigest: input.outputDigest,
|
|
23325
23535
|
runMetadata: input.runMetadata
|
|
23326
23536
|
});
|
|
23327
|
-
db.
|
|
23328
|
-
|
|
23329
|
-
|
|
23330
|
-
WHERE job_id = ?`).run(input.status, stableJson2(job), input.now, input.outputDigest ?? record2.job.outputDigest ?? null, input.error ?? null, deadLetteredAt ?? null, input.jobId);
|
|
23331
|
-
const updated = runtimeAgentJobById(db, input.jobId);
|
|
23332
|
-
if (!updated)
|
|
23333
|
-
throw new Error(`runtime-agent-job-not-found: ${input.jobId}`);
|
|
23334
|
-
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);
|
|
23335
23540
|
}
|
|
23336
23541
|
async retryRuntimeAgentJob(input) {
|
|
23337
23542
|
const db = await this.database();
|
|
23338
|
-
const record2 =
|
|
23339
|
-
if (!record2)
|
|
23340
|
-
throw new Error(`runtime-agent-job-not-found: ${input.jobId}`);
|
|
23543
|
+
const record2 = requireRuntimeAgentJobInScope(db, input, input.jobId);
|
|
23341
23544
|
if (record2.attemptCount >= record2.maxAttempts) {
|
|
23342
23545
|
const failed = runtimeAgentJobWithPatch(record2.job, { status: "failed", updatedAt: input.now });
|
|
23343
|
-
db.
|
|
23344
|
-
|
|
23345
|
-
leased_at = NULL, lease_expires_at = NULL, last_error = ?, dead_lettered_at = ?
|
|
23346
|
-
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]);
|
|
23347
23548
|
} else {
|
|
23348
23549
|
const queued = runtimeAgentJobWithPatch(record2.job, { status: "queued", updatedAt: input.now });
|
|
23349
|
-
db.
|
|
23350
|
-
|
|
23351
|
-
|
|
23352
|
-
|
|
23353
|
-
}
|
|
23354
|
-
const updated = runtimeAgentJobById(db, input.jobId);
|
|
23355
|
-
if (!updated)
|
|
23356
|
-
throw new Error(`runtime-agent-job-not-found: ${input.jobId}`);
|
|
23357
|
-
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);
|
|
23358
23554
|
}
|
|
23359
23555
|
async cancelRuntimeAgentJob(input) {
|
|
23360
23556
|
const db = await this.database();
|
|
23361
|
-
const record2 =
|
|
23362
|
-
if (!record2)
|
|
23363
|
-
throw new Error(`runtime-agent-job-not-found: ${input.jobId}`);
|
|
23557
|
+
const record2 = requireRuntimeAgentJobInScope(db, input, input.jobId);
|
|
23364
23558
|
const job = runtimeAgentJobWithPatch(record2.job, { status: input.status, updatedAt: input.now });
|
|
23365
|
-
db.
|
|
23366
|
-
|
|
23367
|
-
|
|
23368
|
-
WHERE job_id = ?`).run(input.status, stableJson2(job), input.now, input.reason ?? null, input.supersededByJobId ?? null, input.jobId);
|
|
23369
|
-
const updated = runtimeAgentJobById(db, input.jobId);
|
|
23370
|
-
if (!updated)
|
|
23371
|
-
throw new Error(`runtime-agent-job-not-found: ${input.jobId}`);
|
|
23372
|
-
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);
|
|
23373
23562
|
}
|
|
23374
23563
|
async cancelStaleRuntimeAgentJobs(input) {
|
|
23375
23564
|
const db = await this.database();
|
|
@@ -23382,6 +23571,8 @@ class SqliteLocalStore {
|
|
|
23382
23571
|
const cancelled = [];
|
|
23383
23572
|
for (const record2 of staleRows) {
|
|
23384
23573
|
cancelled.push(await this.cancelRuntimeAgentJob({
|
|
23574
|
+
repository: input.repository,
|
|
23575
|
+
worktree: input.worktree,
|
|
23385
23576
|
jobId: record2.job.jobId,
|
|
23386
23577
|
status: "expired",
|
|
23387
23578
|
now: input.now,
|
|
@@ -23443,29 +23634,58 @@ class SqliteLocalStore {
|
|
|
23443
23634
|
(lookup_key, apply_id, journal_id, receipt_json, created_at, updated_at)
|
|
23444
23635
|
VALUES (?, ?, ?, ?, ?, ?)`).run(receipt.identity.lookupKey, receipt.identity.applyId, journalId, stableJson2(receipt), createdAt, createdAt);
|
|
23445
23636
|
}
|
|
23446
|
-
async
|
|
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) {
|
|
23447
23654
|
const db = await this.database();
|
|
23448
23655
|
db.exec("BEGIN IMMEDIATE");
|
|
23449
23656
|
try {
|
|
23450
|
-
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
|
|
23451
23658
|
FROM projection_apply_receipts receipt
|
|
23452
23659
|
JOIN changeset_journal journal ON journal.journal_id = receipt.journal_id
|
|
23453
|
-
WHERE receipt.lookup_key = ? AND journal.status = 'committed'`).get(lookupKey);
|
|
23660
|
+
WHERE receipt.lookup_key = ? AND journal.status = 'committed'`).get(proof.receipt.lookupKey);
|
|
23454
23661
|
if (!row?.receipt_json) {
|
|
23455
23662
|
db.exec("COMMIT");
|
|
23456
23663
|
return;
|
|
23457
23664
|
}
|
|
23458
|
-
const receipt =
|
|
23459
|
-
const
|
|
23460
|
-
if (
|
|
23461
|
-
throw new Error(`projection-apply-
|
|
23462
|
-
|
|
23463
|
-
|
|
23464
|
-
|
|
23465
|
-
|
|
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
|
+
};
|
|
23466
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");
|
|
23467
23687
|
db.exec("COMMIT");
|
|
23468
|
-
return { receipt, refreshSignalsDelivered };
|
|
23688
|
+
return { receipt, proof: deliveredProof, refreshSignalsDelivered: true };
|
|
23469
23689
|
} catch (error) {
|
|
23470
23690
|
db.exec("ROLLBACK");
|
|
23471
23691
|
throw error;
|
|
@@ -26536,51 +26756,48 @@ async function rebuildDerivedLandscapeState(store, input) {
|
|
|
26536
26756
|
digest: landscapeDigest(landscape, scopedRelations)
|
|
26537
26757
|
};
|
|
26538
26758
|
}
|
|
26539
|
-
|
|
26540
|
-
if (
|
|
26541
|
-
|
|
26542
|
-
|
|
26543
|
-
|
|
26544
|
-
|
|
26545
|
-
return {
|
|
26546
|
-
exec: (sql) => db.exec(sql),
|
|
26547
|
-
prepare: (sql) => db.prepare(sql),
|
|
26548
|
-
close: () => db.close()
|
|
26549
|
-
};
|
|
26550
|
-
} catch {
|
|
26551
|
-
const bunSqlite = await import("bun:sqlite");
|
|
26552
|
-
const db = new bunSqlite.Database(databasePath);
|
|
26553
|
-
return {
|
|
26554
|
-
exec: (sql) => db.exec(sql),
|
|
26555
|
-
prepare: (sql) => db.query(sql),
|
|
26556
|
-
close: () => db.close()
|
|
26557
|
-
};
|
|
26558
|
-
}
|
|
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.");
|
|
26559
26765
|
}
|
|
26560
|
-
function
|
|
26561
|
-
if (databasePath !== ":memory:")
|
|
26562
|
-
ensurePrivateDir2(dirname8(databasePath));
|
|
26563
|
-
try {
|
|
26564
|
-
const nodeSqlite = runtimeRequire2("node:sqlite");
|
|
26565
|
-
const db2 = new nodeSqlite.DatabaseSync(databasePath);
|
|
26566
|
-
return {
|
|
26567
|
-
exec: (sql) => db2.exec(sql),
|
|
26568
|
-
prepare: (sql) => db2.prepare(sql),
|
|
26569
|
-
close: () => db2.close()
|
|
26570
|
-
};
|
|
26571
|
-
} catch (error) {
|
|
26572
|
-
if (error.code !== "ERR_UNKNOWN_BUILTIN_MODULE" && error.code !== "MODULE_NOT_FOUND") {
|
|
26573
|
-
throw error;
|
|
26574
|
-
}
|
|
26575
|
-
}
|
|
26576
|
-
const bunSqlite = runtimeRequire2("bun:sqlite");
|
|
26766
|
+
function adaptBunSqliteDatabase2(bunSqlite, databasePath) {
|
|
26577
26767
|
const db = new bunSqlite.Database(databasePath);
|
|
26768
|
+
const releaseStatements = () => db.clearQueryCache();
|
|
26578
26769
|
return {
|
|
26579
26770
|
exec: (sql) => db.exec(sql),
|
|
26580
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),
|
|
26581
26784
|
close: () => db.close()
|
|
26582
26785
|
};
|
|
26583
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
|
+
}
|
|
26584
26801
|
function legacyMigrationResult2(migrated, skippedReason, paths, copiedFiles, details) {
|
|
26585
26802
|
return {
|
|
26586
26803
|
schemaVersion: "archcontext.legacy-local-store-migration/v1",
|
|
@@ -26632,20 +26849,16 @@ function assertCurrentLocalStoreSchema2(db, path) {
|
|
|
26632
26849
|
throw new Error(`SQLite local store schema incomplete for ${path}: missing migrations ${missingMigrations.join(", ")}`);
|
|
26633
26850
|
}
|
|
26634
26851
|
}
|
|
26635
|
-
function assertUpgradeableLocalStoreTarget2(path) {
|
|
26636
|
-
const
|
|
26637
|
-
|
|
26638
|
-
|
|
26639
|
-
|
|
26640
|
-
|
|
26641
|
-
if (!hasArchContextMarker) {
|
|
26642
|
-
throw new Error(`SQLite target is not an ArchContext local store candidate: ${path}`);
|
|
26643
|
-
}
|
|
26644
|
-
if (integrity !== "ok")
|
|
26645
|
-
throw new Error(`SQLite integrity_check failed for ${path}: ${integrity}`);
|
|
26646
|
-
} finally {
|
|
26647
|
-
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}`);
|
|
26648
26858
|
}
|
|
26859
|
+
if (integrity !== "ok")
|
|
26860
|
+
throw new Error(`SQLite integrity_check failed for ${path}: ${integrity}`);
|
|
26861
|
+
return integrity;
|
|
26649
26862
|
}
|
|
26650
26863
|
function assertTrustedLegacyLocalStoreSource2(paths) {
|
|
26651
26864
|
const stat = lstatSync6(paths.legacyLocalStorePath);
|
|
@@ -26674,23 +26887,29 @@ function vacuumLegacySqliteInto2(sourcePath, targetPath) {
|
|
|
26674
26887
|
}
|
|
26675
26888
|
function migrateSqliteDatabaseSync2(databasePath) {
|
|
26676
26889
|
const db = openSqliteDatabaseSync2(databasePath);
|
|
26890
|
+
let compacted = false;
|
|
26677
26891
|
try {
|
|
26678
|
-
|
|
26679
|
-
|
|
26680
|
-
backfillArchitectureChangeFeed2(db);
|
|
26892
|
+
migrateOpenedSqliteDatabase2(db);
|
|
26893
|
+
compacted = true;
|
|
26681
26894
|
} finally {
|
|
26682
26895
|
db.close();
|
|
26683
26896
|
}
|
|
26897
|
+
if (compacted)
|
|
26898
|
+
removeSqliteSidecars2(databasePath);
|
|
26684
26899
|
}
|
|
26685
|
-
function
|
|
26686
|
-
|
|
26687
|
-
|
|
26688
|
-
|
|
26689
|
-
|
|
26690
|
-
|
|
26691
|
-
|
|
26692
|
-
|
|
26693
|
-
|
|
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) {
|
|
26694
26913
|
for (const suffix of ["-wal", "-shm"])
|
|
26695
26914
|
rmSync5(`${databasePath}${suffix}`, { force: true });
|
|
26696
26915
|
}
|
|
@@ -26821,14 +27040,14 @@ function ensurePrivateDir2(path) {
|
|
|
26821
27040
|
mkdirSync5(path, { recursive: true, mode: 448 });
|
|
26822
27041
|
if (process.platform !== "win32") {
|
|
26823
27042
|
try {
|
|
26824
|
-
|
|
27043
|
+
chmodSync3(path, 448);
|
|
26825
27044
|
} catch {}
|
|
26826
27045
|
}
|
|
26827
27046
|
}
|
|
26828
27047
|
function makePrivateFile2(path) {
|
|
26829
27048
|
if (process.platform !== "win32") {
|
|
26830
27049
|
try {
|
|
26831
|
-
|
|
27050
|
+
chmodSync3(path, 384);
|
|
26832
27051
|
} catch {}
|
|
26833
27052
|
}
|
|
26834
27053
|
}
|
|
@@ -26845,7 +27064,7 @@ function readGitPath2(root, args) {
|
|
|
26845
27064
|
}
|
|
26846
27065
|
}
|
|
26847
27066
|
function resolveMaybeRelative2(base, path) {
|
|
26848
|
-
return
|
|
27067
|
+
return isAbsolute6(path) ? resolve16(path) : resolve16(base, path);
|
|
26849
27068
|
}
|
|
26850
27069
|
function canonicalPath2(path) {
|
|
26851
27070
|
const resolved = resolve16(path);
|
|
@@ -26858,8 +27077,8 @@ function canonicalPath2(path) {
|
|
|
26858
27077
|
function isPathInsideOrSame2(path, parent) {
|
|
26859
27078
|
const child = resolve16(path);
|
|
26860
27079
|
const base = resolve16(parent);
|
|
26861
|
-
const fromBase =
|
|
26862
|
-
return fromBase === "" || !!fromBase && !fromBase.startsWith("..") && !
|
|
27080
|
+
const fromBase = relative9(base, child);
|
|
27081
|
+
return fromBase === "" || !!fromBase && !fromBase.startsWith("..") && !isAbsolute6(fromBase);
|
|
26863
27082
|
}
|
|
26864
27083
|
function stableStorageId2(prefix, value) {
|
|
26865
27084
|
return `${prefix}.${createHash9("sha256").update(value).digest("hex").slice(0, 16)}`;
|
|
@@ -26951,10 +27170,29 @@ function insertRuntimeAgentJob(db, input) {
|
|
|
26951
27170
|
lease_expires_at, last_error, dead_lettered_at, debounce_until, superseded_by_job_id)
|
|
26952
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);
|
|
26953
27172
|
}
|
|
26954
|
-
function
|
|
26955
|
-
const row = db.prepare(
|
|
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);
|
|
26956
27178
|
return row ? runtimeAgentJobRecordFromRow(row) : undefined;
|
|
26957
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
|
+
}
|
|
26958
27196
|
function runtimeAgentJobRecordFromRow(row) {
|
|
26959
27197
|
return {
|
|
26960
27198
|
job: JSON.parse(String(row.job_json)),
|
|
@@ -26991,6 +27229,30 @@ function runtimeAgentJobWithPatch(job, patch) {
|
|
|
26991
27229
|
function nullableString(value) {
|
|
26992
27230
|
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
26993
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
|
+
}
|
|
26994
27256
|
function nowIso2() {
|
|
26995
27257
|
return new Date().toISOString();
|
|
26996
27258
|
}
|
|
@@ -27650,13 +27912,13 @@ async function withGithubIssueBodyFile(body, fn, deps = {}) {
|
|
|
27650
27912
|
rmSync7(dir, { recursive: true, force: true });
|
|
27651
27913
|
}
|
|
27652
27914
|
}
|
|
27653
|
-
var
|
|
27654
|
-
/gh[opsu]_[A-Za-z0-9_]
|
|
27655
|
-
/Bearer\s+[A-Za-z0-9._-]+/i,
|
|
27656
|
-
/-----BEGIN [A-Z ]*PRIVATE KEY
|
|
27657
|
-
/GITHUB_WEBHOOK_SECRET/i,
|
|
27658
|
-
/installation[_-]?token/i,
|
|
27659
|
-
|
|
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,}/ }
|
|
27660
27922
|
];
|
|
27661
27923
|
var GITHUB_ISSUE_BODY_MAX_LENGTH = 65536;
|
|
27662
27924
|
function githubIssueFooterMarker(runId, draftDigest) {
|
|
@@ -27674,11 +27936,19 @@ function preflightGithubIssueDrafts(runId, drafts) {
|
|
|
27674
27936
|
|
|
27675
27937
|
${footer}
|
|
27676
27938
|
`;
|
|
27677
|
-
const
|
|
27678
|
-
|
|
27679
|
-
|
|
27680
|
-
|
|
27681
|
-
|
|
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
|
+
}
|
|
27682
27952
|
}
|
|
27683
27953
|
}
|
|
27684
27954
|
if (body.length > GITHUB_ISSUE_BODY_MAX_LENGTH) {
|
|
@@ -28500,6 +28770,8 @@ function auditGithubIssuesEnabledInManifestText(manifestText) {
|
|
|
28500
28770
|
return false;
|
|
28501
28771
|
}
|
|
28502
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;
|
|
28503
28775
|
|
|
28504
28776
|
class RuntimeUpdateInputError extends Error {
|
|
28505
28777
|
}
|
|
@@ -29083,12 +29355,15 @@ class ArchctxDaemon {
|
|
|
29083
29355
|
const scope = await this.architectureLedgerScope(repositoryRoot);
|
|
29084
29356
|
const jobs = await this.localStore.listRuntimeAgentJobs(scope);
|
|
29085
29357
|
const record2 = jobs.find((candidate) => candidate.job.jobId === input.jobId);
|
|
29086
|
-
if (record2
|
|
29358
|
+
if (!record2)
|
|
29359
|
+
return runtimeAgentJobOutOfScopeEnvelope("jobs.complete", input.jobId);
|
|
29360
|
+
if (record2.job.status !== "running") {
|
|
29087
29361
|
return errorEnvelope("jobs.complete", "AC_PRECONDITION_FAILED", `runtime agent job completion requires a running job: ${input.jobId}`);
|
|
29088
29362
|
}
|
|
29089
29363
|
if (input.status === "succeeded") {
|
|
29090
|
-
if (record2
|
|
29364
|
+
if (record2.job.stalePolicy === "cancel-on-head-change" && isRuntimeAgentJobCursorStale(record2.job, scope)) {
|
|
29091
29365
|
await this.localStore.cancelRuntimeAgentJob({
|
|
29366
|
+
...scope,
|
|
29092
29367
|
jobId: input.jobId,
|
|
29093
29368
|
status: "expired",
|
|
29094
29369
|
now: input.now ?? this.clock(),
|
|
@@ -29100,7 +29375,7 @@ class ArchctxDaemon {
|
|
|
29100
29375
|
if (input.proposalPlan) {
|
|
29101
29376
|
const validation = validateRuntimeAgentProposalPlan({
|
|
29102
29377
|
proposalPlan: input.proposalPlan,
|
|
29103
|
-
job: record2
|
|
29378
|
+
job: record2.job,
|
|
29104
29379
|
jobId: input.jobId,
|
|
29105
29380
|
outputDigest: input.outputDigest
|
|
29106
29381
|
});
|
|
@@ -29112,6 +29387,7 @@ class ArchctxDaemon {
|
|
|
29112
29387
|
proposalPlan: input.proposalPlan
|
|
29113
29388
|
} : input.runMetadata;
|
|
29114
29389
|
const job = await this.localStore.completeRuntimeAgentJob({
|
|
29390
|
+
...scope,
|
|
29115
29391
|
jobId: input.jobId,
|
|
29116
29392
|
status: input.status,
|
|
29117
29393
|
workerId: input.workerId,
|
|
@@ -29124,8 +29400,12 @@ class ArchctxDaemon {
|
|
|
29124
29400
|
}
|
|
29125
29401
|
async jobsRetry(root, input) {
|
|
29126
29402
|
this.assertRunning();
|
|
29127
|
-
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
|
+
}
|
|
29128
29407
|
const job = await this.localStore.retryRuntimeAgentJob({
|
|
29408
|
+
...scope,
|
|
29129
29409
|
jobId: input.jobId,
|
|
29130
29410
|
reason: input.reason,
|
|
29131
29411
|
now: input.now ?? this.clock()
|
|
@@ -29134,8 +29414,12 @@ class ArchctxDaemon {
|
|
|
29134
29414
|
}
|
|
29135
29415
|
async jobsCancel(root, input) {
|
|
29136
29416
|
this.assertRunning();
|
|
29137
|
-
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
|
+
}
|
|
29138
29421
|
const job = await this.localStore.cancelRuntimeAgentJob({
|
|
29422
|
+
...scope,
|
|
29139
29423
|
jobId: input.jobId,
|
|
29140
29424
|
status: input.status ?? "cancelled",
|
|
29141
29425
|
reason: input.reason,
|
|
@@ -29144,6 +29428,10 @@ class ArchctxDaemon {
|
|
|
29144
29428
|
});
|
|
29145
29429
|
return okEnvelope("jobs.cancel", { job });
|
|
29146
29430
|
}
|
|
29431
|
+
async runtimeAgentJobInScope(scope, jobId) {
|
|
29432
|
+
const jobs = await this.localStore.listRuntimeAgentJobs(scope);
|
|
29433
|
+
return jobs.find((candidate) => candidate.job.jobId === jobId);
|
|
29434
|
+
}
|
|
29147
29435
|
async auditRun(root, input = {}) {
|
|
29148
29436
|
this.assertRunning();
|
|
29149
29437
|
const repositoryRoot = findRepositoryRoot2(root);
|
|
@@ -29367,14 +29655,22 @@ class ArchctxDaemon {
|
|
|
29367
29655
|
return typeof manifestRaw === "string" && auditGithubIssuesEnabledInManifestText(manifestRaw);
|
|
29368
29656
|
}
|
|
29369
29657
|
async canFileGithubIssues(root) {
|
|
29370
|
-
const
|
|
29371
|
-
if (
|
|
29658
|
+
const target = readGitRemoteTarget(root);
|
|
29659
|
+
if (!target) {
|
|
29372
29660
|
return {
|
|
29373
29661
|
ok: false,
|
|
29374
29662
|
code: "AC_PRECONDITION_FAILED",
|
|
29375
29663
|
message: "audit approve requires a resolvable GitHub owner/repo; git remote 'origin' is missing or is not a parseable GitHub URL"
|
|
29376
29664
|
};
|
|
29377
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}`;
|
|
29378
29674
|
const token = process.env[AUDIT_APPROVE_GH_TOKEN_ENV];
|
|
29379
29675
|
if (!token) {
|
|
29380
29676
|
return {
|
|
@@ -29402,7 +29698,7 @@ class ArchctxDaemon {
|
|
|
29402
29698
|
message: `audit approve received an unrecognized visibility "${probedVisibility}" for ${repoNameWithOwner}; refusing to guess whether it is safe to publish`
|
|
29403
29699
|
};
|
|
29404
29700
|
}
|
|
29405
|
-
return { ok: true, repoNameWithOwner, visibility, token };
|
|
29701
|
+
return { ok: true, host: target.host, repoNameWithOwner, visibility, token };
|
|
29406
29702
|
}
|
|
29407
29703
|
async auditList(root, input = {}) {
|
|
29408
29704
|
this.assertRunning();
|
|
@@ -29477,9 +29773,9 @@ class ArchctxDaemon {
|
|
|
29477
29773
|
const capability = await this.canFileGithubIssues(repositoryRoot);
|
|
29478
29774
|
if (!capability.ok)
|
|
29479
29775
|
return errorEnvelope("audit.approve", capability.code, capability.message);
|
|
29480
|
-
const expectedConfirmToken = `public:${capability.repoNameWithOwner}:${run.baseSha}:${run.runId}`;
|
|
29776
|
+
const expectedConfirmToken = `public:${capability.host}/${capability.repoNameWithOwner}:${run.baseSha}:${run.runId}`;
|
|
29481
29777
|
if (capability.visibility !== "private" && input.confirmPublicToken !== expectedConfirmToken) {
|
|
29482
|
-
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}`);
|
|
29483
29779
|
}
|
|
29484
29780
|
const preflight = preflightGithubIssueDrafts(run.runId, drafts);
|
|
29485
29781
|
if (!preflight.ok)
|
|
@@ -29710,7 +30006,8 @@ class ArchctxDaemon {
|
|
|
29710
30006
|
return errorEnvelope("docs.pin", "AC_SCHEMA_INVALID", "docs pin requires --library-id and --version");
|
|
29711
30007
|
assertContext7LibraryId(input.libraryId);
|
|
29712
30008
|
assertContext7Version(input.version);
|
|
29713
|
-
const
|
|
30009
|
+
const current = readContext7LockfileState(session.workspace.root);
|
|
30010
|
+
const lock = upsertContext7Pin(current.lock, {
|
|
29714
30011
|
libraryId: input.libraryId,
|
|
29715
30012
|
version: input.version,
|
|
29716
30013
|
pinnedAt: this.clock(),
|
|
@@ -29724,7 +30021,7 @@ class ArchctxDaemon {
|
|
|
29724
30021
|
lock
|
|
29725
30022
|
});
|
|
29726
30023
|
}
|
|
29727
|
-
writeContext7Lockfile(session.workspace.root, lock);
|
|
30024
|
+
writeContext7Lockfile(session.workspace.root, lock, current.expectedHash);
|
|
29728
30025
|
return okEnvelope("docs.pin", {
|
|
29729
30026
|
schemaVersion: "archcontext.context7-pin/v1",
|
|
29730
30027
|
approved: true,
|
|
@@ -30066,12 +30363,55 @@ class ArchctxDaemon {
|
|
|
30066
30363
|
});
|
|
30067
30364
|
});
|
|
30068
30365
|
}
|
|
30069
|
-
async
|
|
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) {
|
|
30070
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
|
+
}
|
|
30071
30381
|
return this.withWriter(async () => {
|
|
30072
|
-
await this.openSession(root);
|
|
30073
|
-
const
|
|
30074
|
-
|
|
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", {
|
|
30075
30415
|
found: consumption !== undefined,
|
|
30076
30416
|
...consumption ?? {}
|
|
30077
30417
|
});
|
|
@@ -30140,7 +30480,6 @@ class ArchctxDaemon {
|
|
|
30140
30480
|
await this.changeSetEngine.apply(root, draft, { approved: true });
|
|
30141
30481
|
}
|
|
30142
30482
|
async applyArchitectureProjectionRollbackChangeSet(root, projectedFiles, currentFiles, createdAt) {
|
|
30143
|
-
const targetPaths = new Set(projectedFiles.map((file) => file.path));
|
|
30144
30483
|
const backupBase = `.archcontext/backups/ledger-rollback/${safePathSegment(createdAt)}`;
|
|
30145
30484
|
const backupRelativePath = uniqueBackupPath(root, backupBase);
|
|
30146
30485
|
const manifestPath = `${backupRelativePath}/manifest.json`;
|
|
@@ -30149,7 +30488,7 @@ class ArchctxDaemon {
|
|
|
30149
30488
|
path: backupRelativePath,
|
|
30150
30489
|
manifestPath
|
|
30151
30490
|
});
|
|
30152
|
-
const removedPaths = currentFiles
|
|
30491
|
+
const removedPaths = obsoleteManagedProjectionPaths(currentFiles, projectedFiles);
|
|
30153
30492
|
await this.applyArchitectureProjectionChangeSet(root, {
|
|
30154
30493
|
id: `changeset.ledger-rollback-${shortDigest3(digestJson({ createdAt, projectionDigest: architectureLedgerProjectionDigest(projectedFiles) }))}`,
|
|
30155
30494
|
files: [
|
|
@@ -30523,11 +30862,12 @@ class ArchctxDaemon {
|
|
|
30523
30862
|
const scope = await this.architectureLedgerScope(root);
|
|
30524
30863
|
const state = await this.localStore.readArchitectureLedgerState(scope);
|
|
30525
30864
|
const projectedFiles = projectArchitectureLedgerStateToYamlFiles(state);
|
|
30865
|
+
const removedPaths = obsoleteManagedProjectionPaths(listModelFiles(root).filter((file) => isArchitectureLedgerManagedModelPath(file.path)), projectedFiles);
|
|
30526
30866
|
if (writes) {
|
|
30527
30867
|
await this.applyArchitectureProjectionChangeSet(root, {
|
|
30528
30868
|
id: `changeset.ledger-project-${shortDigest3(architectureLedgerProjectionDigest(projectedFiles))}`,
|
|
30529
30869
|
files: projectedFiles.map(({ path, body }) => ({ path, body })),
|
|
30530
|
-
removedPaths
|
|
30870
|
+
removedPaths
|
|
30531
30871
|
});
|
|
30532
30872
|
}
|
|
30533
30873
|
const drift = compareArchitectureLedgerStateToYaml({
|
|
@@ -30548,6 +30888,7 @@ class ArchctxDaemon {
|
|
|
30548
30888
|
projectionDigest: architectureLedgerProjectionDigest(projectedFiles),
|
|
30549
30889
|
graphDigest: architectureLedgerStateDigest(state),
|
|
30550
30890
|
writtenPaths: writes ? projectedFiles.map((file) => file.path) : [],
|
|
30891
|
+
removedPaths: writes ? removedPaths : [],
|
|
30551
30892
|
projectedFiles: writes ? undefined : projectedFiles,
|
|
30552
30893
|
drift,
|
|
30553
30894
|
reconcile
|
|
@@ -31024,12 +31365,21 @@ class ArchctxDaemon {
|
|
|
31024
31365
|
const paths = createDeveloperReviewRunPaths({
|
|
31025
31366
|
sourceRoot,
|
|
31026
31367
|
challengeId: input.challenge.challengeId,
|
|
31027
|
-
tempRoot: input.tempRoot
|
|
31028
|
-
stateDir: input.stateDir
|
|
31368
|
+
tempRoot: input.tempRoot
|
|
31029
31369
|
});
|
|
31030
31370
|
mkdirSync7(paths.stateDir, { recursive: true });
|
|
31031
31371
|
mkdirSync7(paths.runRoot, { recursive: true });
|
|
31032
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
|
+
});
|
|
31033
31383
|
const preparing = {
|
|
31034
31384
|
schemaVersion: "archcontext.developer-review-run/v1",
|
|
31035
31385
|
runId: paths.runId,
|
|
@@ -31041,7 +31391,7 @@ class ArchctxDaemon {
|
|
|
31041
31391
|
manifestPath: paths.manifestPath,
|
|
31042
31392
|
lockPath: paths.lockPath,
|
|
31043
31393
|
pid: process.pid,
|
|
31044
|
-
createdAt
|
|
31394
|
+
createdAt,
|
|
31045
31395
|
status: "preparing",
|
|
31046
31396
|
codeGraphTemporaryState: {
|
|
31047
31397
|
root: paths.runRoot,
|
|
@@ -31101,12 +31451,13 @@ class ArchctxDaemon {
|
|
|
31101
31451
|
}
|
|
31102
31452
|
}
|
|
31103
31453
|
cleanupDeveloperReviewRun(run) {
|
|
31454
|
+
const targets = resolveOwnedDeveloperReviewRunTargets(run);
|
|
31104
31455
|
const removed = [];
|
|
31105
31456
|
const errors = [];
|
|
31106
|
-
if (
|
|
31457
|
+
if (targets.worktree) {
|
|
31107
31458
|
try {
|
|
31108
|
-
const hadWorktree = existsSync15(
|
|
31109
|
-
removeDetachedReviewWorktree(
|
|
31459
|
+
const hadWorktree = existsSync15(targets.worktree.worktreeRoot);
|
|
31460
|
+
removeDetachedReviewWorktree(targets.worktree);
|
|
31110
31461
|
if (hadWorktree)
|
|
31111
31462
|
removed.push("worktree");
|
|
31112
31463
|
} catch (error) {
|
|
@@ -31114,10 +31465,12 @@ class ArchctxDaemon {
|
|
|
31114
31465
|
}
|
|
31115
31466
|
}
|
|
31116
31467
|
for (const [kind, path] of [
|
|
31117
|
-
["run-root",
|
|
31118
|
-
["manifest",
|
|
31119
|
-
["lock",
|
|
31468
|
+
["run-root", targets.runRoot],
|
|
31469
|
+
["manifest", targets.manifestPath],
|
|
31470
|
+
["lock", targets.lockPath]
|
|
31120
31471
|
]) {
|
|
31472
|
+
if (!path)
|
|
31473
|
+
continue;
|
|
31121
31474
|
try {
|
|
31122
31475
|
const existed = existsSync15(path);
|
|
31123
31476
|
removePathWithRetry(path);
|
|
@@ -31139,14 +31492,15 @@ class ArchctxDaemon {
|
|
|
31139
31492
|
recoverDeveloperReviewRuns(input) {
|
|
31140
31493
|
this.assertRunning();
|
|
31141
31494
|
const sourceRoot = findRepositoryRoot2(input.repositoryRoot);
|
|
31142
|
-
const stateDir =
|
|
31495
|
+
const stateDir = defaultDeveloperReviewRunStateDir(sourceRoot);
|
|
31143
31496
|
const recovery = {
|
|
31144
31497
|
schemaVersion: "archcontext.developer-review-run-recovery/v1",
|
|
31145
31498
|
sourceRoot,
|
|
31146
31499
|
stateDir,
|
|
31147
31500
|
recovered: [],
|
|
31148
31501
|
removedLocks: [],
|
|
31149
|
-
skippedActive: []
|
|
31502
|
+
skippedActive: [],
|
|
31503
|
+
rejected: []
|
|
31150
31504
|
};
|
|
31151
31505
|
if (!existsSync15(stateDir))
|
|
31152
31506
|
return recovery;
|
|
@@ -31154,21 +31508,39 @@ class ArchctxDaemon {
|
|
|
31154
31508
|
if (!entry.endsWith(".json"))
|
|
31155
31509
|
continue;
|
|
31156
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
|
+
}
|
|
31157
31516
|
const manifest = readDeveloperReviewRunManifest(manifestPath);
|
|
31158
31517
|
if (!manifest) {
|
|
31159
31518
|
rmSync8(manifestPath, { force: true });
|
|
31160
31519
|
continue;
|
|
31161
31520
|
}
|
|
31521
|
+
if (resolve18(manifest.manifestPath) !== manifestPath) {
|
|
31522
|
+
recovery.rejected.push(`${entry}: manifest-path-mismatch`);
|
|
31523
|
+
continue;
|
|
31524
|
+
}
|
|
31162
31525
|
if (!input.force && isDeveloperReviewPidAlive(manifest.pid)) {
|
|
31163
31526
|
recovery.skippedActive.push(manifest.runId);
|
|
31164
31527
|
continue;
|
|
31165
31528
|
}
|
|
31166
|
-
|
|
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
|
+
}
|
|
31167
31534
|
}
|
|
31168
31535
|
for (const entry of readdirSync9(stateDir).sort()) {
|
|
31169
31536
|
if (!entry.endsWith(".lock"))
|
|
31170
31537
|
continue;
|
|
31171
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
|
+
}
|
|
31172
31544
|
const lock = readJsonObject(lockPath);
|
|
31173
31545
|
const pid = typeof lock?.pid === "number" ? lock.pid : undefined;
|
|
31174
31546
|
const runId = typeof lock?.runId === "string" ? lock.runId : entry;
|
|
@@ -31330,16 +31702,46 @@ class ArchctxDaemon {
|
|
|
31330
31702
|
}
|
|
31331
31703
|
async repoRemove(repositoryId) {
|
|
31332
31704
|
this.assertRunning();
|
|
31333
|
-
this.sessions.
|
|
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;
|
|
31334
31713
|
if (this.landscape) {
|
|
31335
|
-
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 = {
|
|
31336
31717
|
...this.landscape,
|
|
31337
31718
|
repositories: this.landscape.repositories.filter((repo) => repo.repositoryId !== repositoryId),
|
|
31338
|
-
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
|
+
}
|
|
31339
31726
|
};
|
|
31340
|
-
await this.localStore.
|
|
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);
|
|
31341
31738
|
}
|
|
31342
|
-
return okEnvelope("repo.remove", {
|
|
31739
|
+
return okEnvelope("repo.remove", {
|
|
31740
|
+
repositoryId,
|
|
31741
|
+
removed: true,
|
|
31742
|
+
sessionRemoved: hadOpenSession || hadPersistedSession,
|
|
31743
|
+
detachedRelationIds
|
|
31744
|
+
});
|
|
31343
31745
|
}
|
|
31344
31746
|
async loadLandscape(landscape) {
|
|
31345
31747
|
this.assertRunning();
|
|
@@ -32195,16 +32597,76 @@ data: ${payload}
|
|
|
32195
32597
|
}
|
|
32196
32598
|
}
|
|
32197
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
|
+
|
|
32198
32657
|
class RuntimeRpcClient {
|
|
32199
32658
|
connection;
|
|
32200
|
-
|
|
32659
|
+
options;
|
|
32660
|
+
timeouts;
|
|
32661
|
+
constructor(connection, options = {}) {
|
|
32201
32662
|
this.connection = connection;
|
|
32663
|
+
this.options = options;
|
|
32664
|
+
this.timeouts = { ...RUNTIME_RPC_CLIENT_TIMEOUT_POLICY, ...options.timeouts };
|
|
32202
32665
|
}
|
|
32203
32666
|
async health() {
|
|
32204
|
-
|
|
32667
|
+
return await this.request("health", this.timeouts.health, `${this.connection.url}health`, {
|
|
32205
32668
|
headers: { "X-ArchContext-RPC-Version": RUNTIME_RPC_VERSION }
|
|
32206
32669
|
});
|
|
32207
|
-
return await response.json();
|
|
32208
32670
|
}
|
|
32209
32671
|
async shutdown() {
|
|
32210
32672
|
return this.call("shutdown", []);
|
|
@@ -32288,8 +32750,11 @@ class RuntimeRpcClient {
|
|
|
32288
32750
|
applyUpdate(root, input) {
|
|
32289
32751
|
return this.call("applyUpdate", [root, input]);
|
|
32290
32752
|
}
|
|
32291
|
-
|
|
32292
|
-
return this.call("
|
|
32753
|
+
inspectProjectionApplyReceipt(root, lookupKey) {
|
|
32754
|
+
return this.call("inspectProjectionApplyReceipt", [root, lookupKey]);
|
|
32755
|
+
}
|
|
32756
|
+
recoverProjectionApply(root, intent) {
|
|
32757
|
+
return this.call("recoverProjectionApply", [root, intent]);
|
|
32293
32758
|
}
|
|
32294
32759
|
ledgerState(root) {
|
|
32295
32760
|
return this.call("ledgerState", [root]);
|
|
@@ -32367,7 +32832,7 @@ class RuntimeRpcClient {
|
|
|
32367
32832
|
return unwrapRpcData(await this.call("recoverDeveloperReviewRuns", [input]));
|
|
32368
32833
|
}
|
|
32369
32834
|
async call(method, params) {
|
|
32370
|
-
|
|
32835
|
+
return await this.request(method, runtimeRpcMethodTimeout(method, this.timeouts), `${this.connection.url}rpc`, {
|
|
32371
32836
|
method: "POST",
|
|
32372
32837
|
headers: {
|
|
32373
32838
|
Authorization: `Bearer ${this.connection.token}`,
|
|
@@ -32376,7 +32841,35 @@ class RuntimeRpcClient {
|
|
|
32376
32841
|
},
|
|
32377
32842
|
body: JSON.stringify({ schemaVersion: RUNTIME_RPC_VERSION, method, params })
|
|
32378
32843
|
});
|
|
32379
|
-
|
|
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
|
+
}
|
|
32380
32873
|
}
|
|
32381
32874
|
}
|
|
32382
32875
|
|
|
@@ -32427,7 +32920,7 @@ class ArchctxRuntimeRpcServer {
|
|
|
32427
32920
|
startedAt: (this.options.clock ?? (() => new Date().toISOString()))()
|
|
32428
32921
|
};
|
|
32429
32922
|
writeFileSync6(connectionPath, JSON.stringify(this.connection, null, 2), { mode: 384 });
|
|
32430
|
-
|
|
32923
|
+
chmodSync4(connectionPath, 384);
|
|
32431
32924
|
this.armIdleTimer();
|
|
32432
32925
|
return this.connection;
|
|
32433
32926
|
}
|
|
@@ -32521,13 +33014,26 @@ class ArchctxRuntimeRpcServer {
|
|
|
32521
33014
|
writeJson(response, 401, { schemaVersion: RUNTIME_RPC_VERSION, ok: false, error: "runtime RPC token required" });
|
|
32522
33015
|
return;
|
|
32523
33016
|
}
|
|
32524
|
-
const body = await readRequestJson(request);
|
|
32525
|
-
if (body.schemaVersion !== RUNTIME_RPC_VERSION) {
|
|
32526
|
-
writeJson(response, 400, { schemaVersion: RUNTIME_RPC_VERSION, ok: false, error: "runtime RPC version mismatch" });
|
|
32527
|
-
return;
|
|
32528
|
-
}
|
|
32529
33017
|
this.inFlightRpcRequests += 1;
|
|
32530
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
|
+
}
|
|
32531
33037
|
const result = await this.dispatch(body.method ?? "", body.params ?? []);
|
|
32532
33038
|
writeJson(response, 200, result);
|
|
32533
33039
|
if (body.method === "shutdown")
|
|
@@ -32594,8 +33100,10 @@ class ArchctxRuntimeRpcServer {
|
|
|
32594
33100
|
return this.daemon.completeTask(params[0], params[1]);
|
|
32595
33101
|
case "applyUpdate":
|
|
32596
33102
|
return this.daemon.applyUpdate(params[0], params[1]);
|
|
32597
|
-
case "
|
|
32598
|
-
return this.daemon.
|
|
33103
|
+
case "inspectProjectionApplyReceipt":
|
|
33104
|
+
return this.daemon.inspectProjectionApplyReceipt(params[0], params[1]);
|
|
33105
|
+
case "recoverProjectionApply":
|
|
33106
|
+
return this.daemon.recoverProjectionApply(params[0], params[1]);
|
|
32599
33107
|
case "ledgerState":
|
|
32600
33108
|
return this.daemon.ledgerState(params[0]);
|
|
32601
33109
|
case "ledgerDrift":
|
|
@@ -32639,13 +33147,13 @@ class ArchctxRuntimeRpcServer {
|
|
|
32639
33147
|
case "runtimeStatus":
|
|
32640
33148
|
return this.daemon.runtimeStatus(params[0]);
|
|
32641
33149
|
case "startDeveloperReviewRun":
|
|
32642
|
-
return okEnvelope("developerReview.startRun", this.daemon.startDeveloperReviewRun(params
|
|
33150
|
+
return okEnvelope("developerReview.startRun", this.daemon.startDeveloperReviewRun(decodeStartDeveloperReviewRunParams(params)));
|
|
32643
33151
|
case "runSignedDeveloperReviewAttestation":
|
|
32644
|
-
return okEnvelope("developerReview.attestation", await this.daemon.runSignedDeveloperReviewAttestation(params
|
|
33152
|
+
return okEnvelope("developerReview.attestation", await this.daemon.runSignedDeveloperReviewAttestation(decodeSignedDeveloperReviewAttestationParams(params)));
|
|
32645
33153
|
case "cleanupDeveloperReviewRun":
|
|
32646
|
-
return okEnvelope("developerReview.cleanupRun", this.daemon.cleanupDeveloperReviewRun(params[0]));
|
|
33154
|
+
return okEnvelope("developerReview.cleanupRun", this.daemon.cleanupDeveloperReviewRun(decodeDeveloperReviewRunManifest(params[0], "cleanupDeveloperReviewRun")));
|
|
32647
33155
|
case "recoverDeveloperReviewRuns":
|
|
32648
|
-
return okEnvelope("developerReview.recoverRuns", this.daemon.recoverDeveloperReviewRuns(params
|
|
33156
|
+
return okEnvelope("developerReview.recoverRuns", this.daemon.recoverDeveloperReviewRuns(decodeRecoverDeveloperReviewRunsParams(params)));
|
|
32649
33157
|
case "shutdown":
|
|
32650
33158
|
return okEnvelope("daemon.stop", { stopping: true });
|
|
32651
33159
|
default:
|
|
@@ -32823,6 +33331,9 @@ function shouldSkipGeneratedProjectionJob(metadata, input) {
|
|
|
32823
33331
|
function isRuntimeAgentJobCursorStale(job, scope) {
|
|
32824
33332
|
return job.worktree.headSha !== scope.worktree.headSha || job.worktree.worktreeDigest !== scope.worktree.worktreeDigest;
|
|
32825
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
|
+
}
|
|
32826
33337
|
function runtimeWorktreeDigest(root, profile) {
|
|
32827
33338
|
switch (profile) {
|
|
32828
33339
|
case "repository":
|
|
@@ -32833,6 +33344,137 @@ function runtimeWorktreeDigest(root, profile) {
|
|
|
32833
33344
|
throw new RuntimeUpdateInputError(`unsupported worktree digest profile: ${String(profile)}`);
|
|
32834
33345
|
}
|
|
32835
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
|
+
}
|
|
32836
33478
|
function isArchContextGeneratedProjectionPath(path) {
|
|
32837
33479
|
return path.replace(/\\/g, "/").startsWith(".archcontext/generated/");
|
|
32838
33480
|
}
|
|
@@ -33184,13 +33826,18 @@ function runtimeAttestationIdentity(snapshot, composition) {
|
|
|
33184
33826
|
})
|
|
33185
33827
|
};
|
|
33186
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}$/;
|
|
33187
33834
|
function createDeveloperReviewRunPaths(input) {
|
|
33188
33835
|
const safeChallengeId = safeControlFileSegment(input.challengeId);
|
|
33189
33836
|
const runId = `${safeChallengeId}-${randomBytes(6).toString("hex")}`;
|
|
33190
|
-
const stateDir =
|
|
33837
|
+
const stateDir = defaultDeveloperReviewRunStateDir(input.sourceRoot);
|
|
33191
33838
|
const tempParent = input.tempRoot ? resolve18(input.tempRoot) : tmpdir3();
|
|
33192
33839
|
mkdirSync7(tempParent, { recursive: true });
|
|
33193
|
-
const runRoot = mkdtempSync4(join9(tempParent,
|
|
33840
|
+
const runRoot = mkdtempSync4(join9(tempParent, `${DEVELOPER_REVIEW_RUN_ROOT_PREFIX}${safeChallengeId.slice(0, 32)}-`));
|
|
33194
33841
|
return {
|
|
33195
33842
|
runId,
|
|
33196
33843
|
stateDir,
|
|
@@ -33200,6 +33847,92 @@ function createDeveloperReviewRunPaths(input) {
|
|
|
33200
33847
|
lockPath: join9(stateDir, `${safeChallengeId}.lock`)
|
|
33201
33848
|
};
|
|
33202
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
|
+
}
|
|
33203
33936
|
function safeControlFileSegment(value) {
|
|
33204
33937
|
const sanitized = value.replace(/[^A-Za-z0-9_.-]/g, "_").slice(0, 80);
|
|
33205
33938
|
return sanitized.length > 0 ? sanitized : "developer-review";
|
|
@@ -33266,9 +33999,7 @@ function appendExternalDocumentationToContext(context, resource, candidate, maxB
|
|
|
33266
33999
|
const resources = context.resources.some((entry) => entry.uri === resource.uri) ? context.resources : [...context.resources, externalResource];
|
|
33267
34000
|
const unknown = `External documentation is advisory and untrusted for ${candidate.packageName}@${candidate.version}: ${candidate.intent}`;
|
|
33268
34001
|
const unknowns = context.unknowns.includes(unknown) ? context.unknowns : [...context.unknowns, unknown];
|
|
33269
|
-
const
|
|
33270
|
-
delete extensionWithoutDigest.digest;
|
|
33271
|
-
const withoutDigest = {
|
|
34002
|
+
const augmented = {
|
|
33272
34003
|
...context,
|
|
33273
34004
|
unknowns,
|
|
33274
34005
|
resources,
|
|
@@ -33287,7 +34018,7 @@ function appendExternalDocumentationToContext(context, resource, candidate, maxB
|
|
|
33287
34018
|
}
|
|
33288
34019
|
},
|
|
33289
34020
|
extensions: {
|
|
33290
|
-
...
|
|
34021
|
+
...context.extensions,
|
|
33291
34022
|
externalDocumentationDigest: digestJson({
|
|
33292
34023
|
provider: resource.provider,
|
|
33293
34024
|
libraryId: candidate.libraryId,
|
|
@@ -33298,22 +34029,7 @@ function appendExternalDocumentationToContext(context, resource, candidate, maxB
|
|
|
33298
34029
|
})
|
|
33299
34030
|
}
|
|
33300
34031
|
};
|
|
33301
|
-
|
|
33302
|
-
const withMetadata = {
|
|
33303
|
-
...withoutDigest,
|
|
33304
|
-
extensions: {
|
|
33305
|
-
...withoutDigest.extensions,
|
|
33306
|
-
byteLength,
|
|
33307
|
-
budgetExceeded: byteLength > maxBytes
|
|
33308
|
-
}
|
|
33309
|
-
};
|
|
33310
|
-
return {
|
|
33311
|
-
...withMetadata,
|
|
33312
|
-
extensions: {
|
|
33313
|
-
...withMetadata.extensions,
|
|
33314
|
-
digest: digestJson(withMetadata)
|
|
33315
|
-
}
|
|
33316
|
-
};
|
|
34032
|
+
return finalizeContextBudgetMetadata(augmented, maxBytes);
|
|
33317
34033
|
}
|
|
33318
34034
|
function prepareContextHasVersionRelatedUnknown(context) {
|
|
33319
34035
|
const unknowns = context.unknowns.join(" ").toLowerCase();
|
|
@@ -33411,15 +34127,22 @@ function isExactPackageVersion(value) {
|
|
|
33411
34127
|
return /^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?$/.test(value);
|
|
33412
34128
|
}
|
|
33413
34129
|
function readContext7Lockfile(root) {
|
|
33414
|
-
|
|
34130
|
+
return readContext7LockfileState(root).lock;
|
|
34131
|
+
}
|
|
34132
|
+
function readContext7LockfileState(root) {
|
|
34133
|
+
const path = assertPathHasNoSymlinkSegments(root, CONTEXT7_LOCKFILE);
|
|
33415
34134
|
if (!existsSync15(path)) {
|
|
33416
34135
|
return {
|
|
33417
|
-
|
|
33418
|
-
|
|
33419
|
-
|
|
34136
|
+
lock: {
|
|
34137
|
+
schemaVersion: CONTEXT7_LOCKFILE_SCHEMA_VERSION,
|
|
34138
|
+
provider: "context7",
|
|
34139
|
+
libraries: []
|
|
34140
|
+
},
|
|
34141
|
+
expectedHash: "missing"
|
|
33420
34142
|
};
|
|
33421
34143
|
}
|
|
33422
|
-
const
|
|
34144
|
+
const body = readFileSync14(path, "utf8");
|
|
34145
|
+
const parsed = JSON.parse(body);
|
|
33423
34146
|
if (parsed.schemaVersion !== CONTEXT7_LOCKFILE_SCHEMA_VERSION || parsed.provider !== "context7" || !Array.isArray(parsed.libraries)) {
|
|
33424
34147
|
throw new Error("Invalid Context7 lockfile");
|
|
33425
34148
|
}
|
|
@@ -33428,8 +34151,11 @@ function readContext7Lockfile(root) {
|
|
|
33428
34151
|
assertContext7Version(library.version);
|
|
33429
34152
|
}
|
|
33430
34153
|
return {
|
|
33431
|
-
|
|
33432
|
-
|
|
34154
|
+
lock: {
|
|
34155
|
+
...parsed,
|
|
34156
|
+
libraries: [...parsed.libraries].sort((a, b) => a.libraryId.localeCompare(b.libraryId))
|
|
34157
|
+
},
|
|
34158
|
+
expectedHash: digestJson({ body })
|
|
33433
34159
|
};
|
|
33434
34160
|
}
|
|
33435
34161
|
function upsertContext7Pin(lock, pin) {
|
|
@@ -33439,8 +34165,14 @@ function upsertContext7Pin(lock, pin) {
|
|
|
33439
34165
|
libraries: [...lock.libraries.filter((library) => library.libraryId !== pin.libraryId), pin].sort((a, b) => a.libraryId.localeCompare(b.libraryId))
|
|
33440
34166
|
};
|
|
33441
34167
|
}
|
|
33442
|
-
function writeContext7Lockfile(root, lock) {
|
|
33443
|
-
|
|
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
|
+
});
|
|
33444
34176
|
}
|
|
33445
34177
|
function writeDeveloperReviewRunManifest(manifest) {
|
|
33446
34178
|
writePrivateJson3(manifest.manifestPath, manifest);
|
|
@@ -33448,25 +34180,192 @@ function writeDeveloperReviewRunManifest(manifest) {
|
|
|
33448
34180
|
function writePrivateJson3(path, value, flag = "w") {
|
|
33449
34181
|
mkdirSync7(dirname10(path), { recursive: true });
|
|
33450
34182
|
writeFileSync6(path, JSON.stringify(value, null, 2), { mode: 384, flag });
|
|
33451
|
-
|
|
34183
|
+
chmodSync4(path, 384);
|
|
33452
34184
|
}
|
|
33453
34185
|
function readDeveloperReviewRunManifest(path) {
|
|
33454
|
-
|
|
33455
|
-
|
|
33456
|
-
|
|
33457
|
-
if (typeof parsed.runId !== "string" || typeof parsed.challengeId !== "string")
|
|
33458
|
-
return;
|
|
33459
|
-
if (typeof parsed.repositoryId !== "number" || typeof parsed.sourceRoot !== "string")
|
|
33460
|
-
return;
|
|
33461
|
-
if (typeof parsed.runRoot !== "string" || typeof parsed.worktreeTempRoot !== "string")
|
|
33462
|
-
return;
|
|
33463
|
-
if (typeof parsed.manifestPath !== "string" || typeof parsed.lockPath !== "string")
|
|
33464
|
-
return;
|
|
33465
|
-
if (typeof parsed.pid !== "number" || typeof parsed.createdAt !== "string")
|
|
33466
|
-
return;
|
|
33467
|
-
if (parsed.status !== "preparing" && parsed.status !== "running")
|
|
34186
|
+
try {
|
|
34187
|
+
return decodeDeveloperReviewRunManifest(readJsonObject(path), "developer-review-run-manifest");
|
|
34188
|
+
} catch {
|
|
33468
34189
|
return;
|
|
33469
|
-
|
|
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
|
+
};
|
|
33470
34369
|
}
|
|
33471
34370
|
function readJsonObject(path) {
|
|
33472
34371
|
try {
|
|
@@ -33651,33 +34550,42 @@ function readCurrentBranch(root) {
|
|
|
33651
34550
|
}
|
|
33652
34551
|
}
|
|
33653
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) {
|
|
33654
34558
|
try {
|
|
33655
34559
|
const url = execFileSync7("git", ["remote", "get-url", "origin"], {
|
|
33656
34560
|
cwd: root,
|
|
33657
34561
|
encoding: "utf8",
|
|
33658
34562
|
stdio: ["ignore", "pipe", "ignore"]
|
|
33659
34563
|
}).trim();
|
|
33660
|
-
return
|
|
34564
|
+
return parseGitRemoteTarget(url);
|
|
33661
34565
|
} catch {
|
|
33662
|
-
return
|
|
34566
|
+
return;
|
|
33663
34567
|
}
|
|
33664
34568
|
}
|
|
33665
|
-
function
|
|
34569
|
+
function parseGitRemoteTarget(url) {
|
|
33666
34570
|
const stripped = url.trim().replace(/\.git$/, "");
|
|
33667
|
-
const scpMatch = /^[^/@]+@[^:/]
|
|
34571
|
+
const scpMatch = /^[^/@]+@([^:/]+):(.+)$/.exec(stripped);
|
|
33668
34572
|
if (scpMatch)
|
|
33669
|
-
return
|
|
34573
|
+
return gitRemoteTarget(scpMatch[1], scpMatch[2]);
|
|
33670
34574
|
try {
|
|
33671
|
-
|
|
34575
|
+
const parsed = new URL(stripped);
|
|
34576
|
+
return gitRemoteTarget(parsed.hostname, parsed.pathname);
|
|
33672
34577
|
} catch {
|
|
33673
34578
|
return;
|
|
33674
34579
|
}
|
|
33675
34580
|
}
|
|
33676
|
-
function
|
|
34581
|
+
function gitRemoteTarget(host, path) {
|
|
34582
|
+
const canonicalHost = host.trim().toLowerCase();
|
|
34583
|
+
if (!canonicalHost)
|
|
34584
|
+
return;
|
|
33677
34585
|
const segments = path.split("/").map((segment) => segment.trim()).filter(Boolean);
|
|
33678
34586
|
if (segments.length < 2)
|
|
33679
34587
|
return;
|
|
33680
|
-
return segments.
|
|
34588
|
+
return { host: canonicalHost, owner: segments[segments.length - 2], repo: segments[segments.length - 1] };
|
|
33681
34589
|
}
|
|
33682
34590
|
function normalizeGithubRepoVisibility(value) {
|
|
33683
34591
|
const lowered = value.trim().toLowerCase();
|
|
@@ -33693,6 +34601,10 @@ function auditApproveResultPayload(runId, status, totalCount, issuedIssues) {
|
|
|
33693
34601
|
issuedIssues
|
|
33694
34602
|
};
|
|
33695
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
|
+
}
|
|
33696
34608
|
function expectedFileHash(root, path) {
|
|
33697
34609
|
const absolute = resolve18(root, path);
|
|
33698
34610
|
return existsSync15(absolute) ? digestJson({ body: readFileSync14(absolute, "utf8") }) : "missing";
|
|
@@ -33987,13 +34899,63 @@ function isRpcVersionHeaderCompatible(request) {
|
|
|
33987
34899
|
const header = requestRpcVersionHeader(request);
|
|
33988
34900
|
return header === undefined || header === RUNTIME_RPC_VERSION;
|
|
33989
34901
|
}
|
|
33990
|
-
async function
|
|
33991
|
-
const
|
|
33992
|
-
|
|
33993
|
-
|
|
33994
|
-
|
|
33995
|
-
|
|
33996
|
-
return
|
|
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
|
+
});
|
|
33997
34959
|
}
|
|
33998
34960
|
function writeJson(response, statusCode, body) {
|
|
33999
34961
|
response.writeHead(statusCode, { "Content-Type": "application/json; charset=utf-8" });
|
|
@@ -34149,20 +35111,82 @@ function structurizrElementType(kind) {
|
|
|
34149
35111
|
init_src();
|
|
34150
35112
|
|
|
34151
35113
|
// packages/local-runtime/runtime-daemon/src/index.ts
|
|
34152
|
-
import { chmodSync as
|
|
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();
|
|
34153
35116
|
init_src();
|
|
34154
35117
|
var DEFAULT_DAEMON_IDLE_TIMEOUT_MS2 = 30 * 60000;
|
|
34155
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
|
+
|
|
34156
35177
|
class RuntimeRpcClient2 {
|
|
34157
35178
|
connection;
|
|
34158
|
-
|
|
35179
|
+
options;
|
|
35180
|
+
timeouts;
|
|
35181
|
+
constructor(connection, options = {}) {
|
|
34159
35182
|
this.connection = connection;
|
|
35183
|
+
this.options = options;
|
|
35184
|
+
this.timeouts = { ...RUNTIME_RPC_CLIENT_TIMEOUT_POLICY2, ...options.timeouts };
|
|
34160
35185
|
}
|
|
34161
35186
|
async health() {
|
|
34162
|
-
|
|
35187
|
+
return await this.request("health", this.timeouts.health, `${this.connection.url}health`, {
|
|
34163
35188
|
headers: { "X-ArchContext-RPC-Version": RUNTIME_RPC_VERSION2 }
|
|
34164
35189
|
});
|
|
34165
|
-
return await response.json();
|
|
34166
35190
|
}
|
|
34167
35191
|
async shutdown() {
|
|
34168
35192
|
return this.call("shutdown", []);
|
|
@@ -34246,8 +35270,11 @@ class RuntimeRpcClient2 {
|
|
|
34246
35270
|
applyUpdate(root, input) {
|
|
34247
35271
|
return this.call("applyUpdate", [root, input]);
|
|
34248
35272
|
}
|
|
34249
|
-
|
|
34250
|
-
return this.call("
|
|
35273
|
+
inspectProjectionApplyReceipt(root, lookupKey) {
|
|
35274
|
+
return this.call("inspectProjectionApplyReceipt", [root, lookupKey]);
|
|
35275
|
+
}
|
|
35276
|
+
recoverProjectionApply(root, intent) {
|
|
35277
|
+
return this.call("recoverProjectionApply", [root, intent]);
|
|
34251
35278
|
}
|
|
34252
35279
|
ledgerState(root) {
|
|
34253
35280
|
return this.call("ledgerState", [root]);
|
|
@@ -34325,7 +35352,7 @@ class RuntimeRpcClient2 {
|
|
|
34325
35352
|
return unwrapRpcData2(await this.call("recoverDeveloperReviewRuns", [input]));
|
|
34326
35353
|
}
|
|
34327
35354
|
async call(method, params) {
|
|
34328
|
-
|
|
35355
|
+
return await this.request(method, runtimeRpcMethodTimeout2(method, this.timeouts), `${this.connection.url}rpc`, {
|
|
34329
35356
|
method: "POST",
|
|
34330
35357
|
headers: {
|
|
34331
35358
|
Authorization: `Bearer ${this.connection.token}`,
|
|
@@ -34334,7 +35361,35 @@ class RuntimeRpcClient2 {
|
|
|
34334
35361
|
},
|
|
34335
35362
|
body: JSON.stringify({ schemaVersion: RUNTIME_RPC_VERSION2, method, params })
|
|
34336
35363
|
});
|
|
34337
|
-
|
|
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
|
+
}
|
|
34338
35393
|
}
|
|
34339
35394
|
}
|
|
34340
35395
|
function defaultDaemonConnectionPath2(root = process.cwd()) {
|
|
@@ -35109,7 +36164,7 @@ async function runCliUnchecked(command2 = "help", args2 = [], cwd, deps = {}) {
|
|
|
35109
36164
|
requestId: "help",
|
|
35110
36165
|
data: {
|
|
35111
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"],
|
|
35112
|
-
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
|
|
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"]
|
|
35113
36168
|
}
|
|
35114
36169
|
};
|
|
35115
36170
|
}
|
|
@@ -35801,9 +36856,11 @@ async function runArchitectureDocsAdoptionCommand(args2, root, daemon, projectio
|
|
|
35801
36856
|
});
|
|
35802
36857
|
}
|
|
35803
36858
|
async function runProjectionProtocolCommand(args2, cwd, daemon) {
|
|
35804
|
-
|
|
35805
|
-
|
|
35806
|
-
|
|
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>");
|
|
35807
36864
|
const raw = readFlag(args2, "--request-json");
|
|
35808
36865
|
if (!raw)
|
|
35809
36866
|
return errorEnvelope("projection.run", "AC_SCHEMA_INVALID", "projection run requires --request-json");
|
|
@@ -35818,21 +36875,19 @@ async function runProjectionProtocolCommand(args2, cwd, daemon) {
|
|
|
35818
36875
|
if (request.mode === "apply" && request.acceptedChange) {
|
|
35819
36876
|
try {
|
|
35820
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
|
+
}
|
|
35821
36888
|
} catch (error) {
|
|
35822
36889
|
return errorEnvelope("projection.run", "AC_PRECONDITION_FAILED", error instanceof Error ? error.message : String(error));
|
|
35823
36890
|
}
|
|
35824
|
-
const reconciled = await daemon.reconcileProjectionApply(root, projectionApplyLookupKey({
|
|
35825
|
-
repositoryId: request.expected.repositoryId,
|
|
35826
|
-
workspaceId: request.expected.workspaceId,
|
|
35827
|
-
acceptedChange: request.acceptedChange
|
|
35828
|
-
}));
|
|
35829
|
-
if (!reconciled.ok)
|
|
35830
|
-
return reconciled;
|
|
35831
|
-
const data = reconciled.data;
|
|
35832
|
-
if (data.found === true) {
|
|
35833
|
-
const receipt = data.receipt;
|
|
35834
|
-
return projectionProtocolResultEnvelope(data.refreshSignalsDelivered === true ? projectionResultDelivery(receipt.result, "applied", receipt.result.refreshSignals, request.requestId) : projectionResultDelivery(receipt.result, "noop", [], request.requestId));
|
|
35835
|
-
}
|
|
35836
36891
|
}
|
|
35837
36892
|
let projection3;
|
|
35838
36893
|
try {
|
|
@@ -35873,21 +36928,51 @@ async function runProjectionProtocolCommand(args2, cwd, daemon) {
|
|
|
35873
36928
|
}
|
|
35874
36929
|
if (request.mode === "apply" && !projection3.plan.drift.ok) {
|
|
35875
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;
|
|
35876
36945
|
const applyIdentity = request.acceptedChange ? createProjectionApplyIdentity({
|
|
35877
36946
|
repositoryId: request.expected.repositoryId,
|
|
35878
36947
|
workspaceId: request.expected.workspaceId,
|
|
35879
36948
|
acceptedChange: request.acceptedChange,
|
|
35880
36949
|
changeSetId,
|
|
35881
36950
|
idempotencyKey: `idem_${changeSetId}`,
|
|
35882
|
-
files:
|
|
35883
|
-
refreshSignals:
|
|
36951
|
+
files: committedFiles,
|
|
36952
|
+
refreshSignals: committedSignals
|
|
35884
36953
|
}) : undefined;
|
|
35885
|
-
const appliedResult = projectionProtocolResult(request, projection3, "applied",
|
|
35886
|
-
|
|
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;
|
|
35887
36972
|
const planned = await daemon.planUpdate(root, {
|
|
35888
36973
|
id: changeSetId,
|
|
35889
36974
|
reason: { taskSessionId: request.requestId },
|
|
35890
|
-
operations: [architectureDocsRenderProjectionOperation(root,
|
|
36975
|
+
operations: [architectureDocsRenderProjectionOperation(root, fixedPointProjection.files)],
|
|
35891
36976
|
worktreeDigestPrecondition: {
|
|
35892
36977
|
profile: "architecture-documentation-projection",
|
|
35893
36978
|
expectedDigest: request.expected.worktreeDigest
|
|
@@ -35921,18 +37006,120 @@ async function runProjectionProtocolCommand(args2, cwd, daemon) {
|
|
|
35921
37006
|
if (!postApplyMatches) {
|
|
35922
37007
|
return projectionProtocolResultEnvelope(projectionResultDelivery(appliedResult, "applied-reconcile-required", []));
|
|
35923
37008
|
}
|
|
35924
|
-
const
|
|
35925
|
-
|
|
35926
|
-
|
|
35927
|
-
|
|
35928
|
-
|
|
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) {
|
|
35929
37022
|
return errorEnvelope("projection.run", "AC_PRECONDITION_FAILED", "committed projection apply receipt was not available for first delivery");
|
|
35930
37023
|
}
|
|
35931
|
-
return projectionProtocolResultEnvelope(
|
|
37024
|
+
return projectionProtocolResultEnvelope(projectionResultDelivery(appliedResult, "applied", delivered.receipt.result.refreshSignals, request.requestId));
|
|
35932
37025
|
}
|
|
35933
37026
|
const status = projection3.plan.drift.ok ? "noop" : "planned";
|
|
35934
37027
|
return projectionProtocolEnvelope(request, projection3, status);
|
|
35935
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
|
+
}
|
|
35936
37123
|
function parseProjectionProtocolRequest(raw) {
|
|
35937
37124
|
let value;
|
|
35938
37125
|
try {
|
|
@@ -36028,10 +37215,10 @@ function projectionProtocolHumanStatus(request, projection3) {
|
|
|
36028
37215
|
function projectionProtocolEnvelope(request, input, status, output = input) {
|
|
36029
37216
|
return projectionProtocolResultEnvelope(projectionProtocolResult(request, input, status, output));
|
|
36030
37217
|
}
|
|
36031
|
-
function projectionProtocolResult(request, input, status, output = input, applyReceipt) {
|
|
37218
|
+
function projectionProtocolResult(request, input, status, output = input, applyReceipt, overrides) {
|
|
36032
37219
|
const inputSnapshot = projectionProtocolSnapshot(request, input.plan.provenance);
|
|
36033
37220
|
const outputSnapshot = projectionProtocolSnapshot(request, output.plan.provenance);
|
|
36034
|
-
const files = projectionProtocolFiles(input);
|
|
37221
|
+
const files = overrides?.files ?? projectionProtocolFiles(input);
|
|
36035
37222
|
const affectedNodeIds = projectionProtocolAffectedNodes(input);
|
|
36036
37223
|
const requestPayloadDigest = digestJson(request);
|
|
36037
37224
|
const humanActions = [];
|
|
@@ -36044,7 +37231,7 @@ function projectionProtocolResult(request, input, status, output = input, applyR
|
|
|
36044
37231
|
requestPayloadDigest
|
|
36045
37232
|
});
|
|
36046
37233
|
}
|
|
36047
|
-
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);
|
|
36048
37235
|
const withoutReceipt = {
|
|
36049
37236
|
schemaVersion: "archcontext.projection-result/v2",
|
|
36050
37237
|
requestId: request.requestId,
|
|
@@ -36112,6 +37299,19 @@ function projectionProtocolFiles(projection3) {
|
|
|
36112
37299
|
return [];
|
|
36113
37300
|
}).sort((left, right) => left.path < right.path ? -1 : left.path > right.path ? 1 : 0);
|
|
36114
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
|
+
}
|
|
36115
37315
|
function projectionProtocolAffectedNodes(projection3) {
|
|
36116
37316
|
const targetNodes = new Map(projection3.plan.targets.flatMap((target) => target.scope.kind === "entity" ? [[target.targetId, target.scope.id]] : []));
|
|
36117
37317
|
return [...new Set([
|
|
@@ -37157,7 +38357,7 @@ async function writeGithubDeveloperReviewState(cwd, state) {
|
|
|
37157
38357
|
assertNoCliSecretMaterial(serialized);
|
|
37158
38358
|
writeFileSync8(path, serialized, { mode: 384 });
|
|
37159
38359
|
if (process.platform !== "win32")
|
|
37160
|
-
|
|
38360
|
+
chmodSync6(path, 384);
|
|
37161
38361
|
return { state, path };
|
|
37162
38362
|
}
|
|
37163
38363
|
function readGithubDeveloperReviewState(path) {
|
|
@@ -37231,7 +38431,7 @@ function writeGithubConnection(path, record2) {
|
|
|
37231
38431
|
assertNoCliSecretMaterial(serialized);
|
|
37232
38432
|
writeFileSync8(path, serialized, { mode: 384 });
|
|
37233
38433
|
if (process.platform !== "win32")
|
|
37234
|
-
|
|
38434
|
+
chmodSync6(path, 384);
|
|
37235
38435
|
}
|
|
37236
38436
|
function sanitizeGithubConnection(record2, connectionPath) {
|
|
37237
38437
|
return {
|