archctx 0.2.0 → 0.2.2
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/assets/catalog.yaml +2 -2
- package/bin/archctx.mjs +1039 -449
- package/package.json +1 -1
- package/schemas/runtime/projection-target.schema.json +1 -1
package/bin/archctx.mjs
CHANGED
|
@@ -702,7 +702,7 @@ function productVersionManifest() {
|
|
|
702
702
|
}
|
|
703
703
|
};
|
|
704
704
|
}
|
|
705
|
-
var ARCHCONTEXT_PRODUCT_NAME = "archctx", ARCHCONTEXT_PRODUCT_VERSION = "0.2.
|
|
705
|
+
var ARCHCONTEXT_PRODUCT_NAME = "archctx", ARCHCONTEXT_PRODUCT_VERSION = "0.2.2", ARCHCONTEXT_PACKAGE_MANAGER = "bun@1.3.10", ARCHCONTEXT_NODE_RANGE = ">=24 <26", LOCAL_RUNTIME_RPC_SCHEMA_VERSION = "archcontext.runtime-rpc/v1", ARCHCONTEXT_SCHEMA_SET_VERSION = "2026-06-25.al0-ledger";
|
|
706
706
|
// packages/contracts/src/index.ts
|
|
707
707
|
var init_src = __esm(() => {
|
|
708
708
|
init_control_plane_routes();
|
|
@@ -5145,7 +5145,7 @@ var init_src7 = __esm(() => {
|
|
|
5145
5145
|
|
|
5146
5146
|
// packages/surfaces/cli/src/main.ts
|
|
5147
5147
|
import { execFileSync as execFileSync8, spawn as spawn2, spawnSync as spawnSync3 } from "child_process";
|
|
5148
|
-
import { accessSync, chmodSync as chmodSync5, closeSync as closeSync7, constants, existsSync as
|
|
5148
|
+
import { accessSync, chmodSync as chmodSync5, closeSync as closeSync7, constants, existsSync as existsSync15, mkdirSync as mkdirSync9, openSync as openSync7, readFileSync as readFileSync13, rmSync as rmSync10, statSync as statSync8, writeFileSync as writeFileSync8 } from "fs";
|
|
5149
5149
|
import { dirname as dirname10, join as join10, resolve as resolve17 } from "path";
|
|
5150
5150
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
5151
5151
|
|
|
@@ -7493,7 +7493,7 @@ function planYamlToArchitectureLedgerImport(input) {
|
|
|
7493
7493
|
payloadVersion: "archcontext.architecture-ledger-yaml-import/v1",
|
|
7494
7494
|
repository: input.repository,
|
|
7495
7495
|
worktree: input.worktree,
|
|
7496
|
-
baseDigest:
|
|
7496
|
+
baseDigest: architectureLedgerStateDigest(emptyArchitectureLedgerState()),
|
|
7497
7497
|
resultingDigest: graphDigest,
|
|
7498
7498
|
headSha: input.worktree.headSha,
|
|
7499
7499
|
actor: { kind: "migration", id: "archctx-ledger-yaml-import" },
|
|
@@ -7677,8 +7677,6 @@ function planYamlToArchitectureLedgerRebuild(input) {
|
|
|
7677
7677
|
const plan = planYamlToArchitectureLedgerImport(input);
|
|
7678
7678
|
const previousGraphDigest = architectureLedgerStateDigest(input.previousState);
|
|
7679
7679
|
const deleteOperations = architectureLedgerDeletionOperations(input.previousState, plan.state);
|
|
7680
|
-
if (deleteOperations.length === 0)
|
|
7681
|
-
return plan;
|
|
7682
7680
|
const payload = architectureLedgerPayload(plan.event);
|
|
7683
7681
|
const event = normalizeArchitectureLedgerEvent({
|
|
7684
7682
|
...plan.event,
|
|
@@ -8244,41 +8242,41 @@ function projectArchitectureLedgerStateToYamlFiles(state) {
|
|
|
8244
8242
|
const canonical = canonicalArchitectureLedgerState(state);
|
|
8245
8243
|
const files = [
|
|
8246
8244
|
...canonical.entities.map((entity) => {
|
|
8245
|
+
const declared = declaredProjectionRecord(entity.metadata, ["archcontext.node/v1"], `entity ${entity.entityId}`);
|
|
8246
|
+
const summary = entity.summary;
|
|
8247
|
+
if (!summary)
|
|
8248
|
+
throw new Error(`architecture-ledger-projection-invalid: entity ${entity.entityId} requires summary`);
|
|
8247
8249
|
const body = canonicalArchitectureYaml({
|
|
8250
|
+
...declared,
|
|
8248
8251
|
schemaVersion: "archcontext.node/v1",
|
|
8249
8252
|
id: entity.entityId,
|
|
8250
8253
|
kind: entity.kind,
|
|
8251
8254
|
name: entity.canonicalName,
|
|
8252
8255
|
status: entity.status,
|
|
8253
|
-
|
|
8254
|
-
...entity.summary ? { summary: entity.summary } : {},
|
|
8255
|
-
...entity.metadata ? { metadata: entity.metadata } : {}
|
|
8256
|
+
summary
|
|
8256
8257
|
});
|
|
8257
8258
|
return projectionFile(`.archcontext/model/nodes/${pathSegment(entity.entityId)}.yaml`, body, "entity", entity.entityId);
|
|
8258
8259
|
}),
|
|
8259
8260
|
...canonical.relations.map((relation) => {
|
|
8260
|
-
const
|
|
8261
|
+
const declared = declaredProjectionRecord(relation.metadata, ["archcontext.relation/v1", "archcontext.cross-repo-relation/v1"], `relation ${relation.relationId}`);
|
|
8262
|
+
const schemaVersion = requireStringField(declared, "schemaVersion", `relation ${relation.relationId}`);
|
|
8263
|
+
const body = schemaVersion === "archcontext.cross-repo-relation/v1" ? canonicalArchitectureYaml({ ...declared, id: relation.relationId, kind: relation.kind }) : canonicalArchitectureYaml({
|
|
8264
|
+
...declared,
|
|
8261
8265
|
schemaVersion: "archcontext.relation/v1",
|
|
8262
8266
|
id: relation.relationId,
|
|
8263
8267
|
kind: relation.kind,
|
|
8264
8268
|
source: relation.sourceEntityId,
|
|
8265
|
-
target: relation.targetEntityId
|
|
8266
|
-
status: relation.status,
|
|
8267
|
-
...relation.summary ? { summary: relation.summary } : {},
|
|
8268
|
-
...relation.metadata ? { metadata: relation.metadata } : {}
|
|
8269
|
+
target: relation.targetEntityId
|
|
8269
8270
|
});
|
|
8270
8271
|
return projectionFile(`.archcontext/model/relations/${pathSegment(relation.relationId)}.yaml`, body, "relation", relation.relationId);
|
|
8271
8272
|
}),
|
|
8272
8273
|
...canonical.constraints.map((constraint) => {
|
|
8274
|
+
const declared = declaredProjectionRecord(constraint.metadata, ["archcontext.constraint/v1"], `constraint ${constraint.constraintId}`);
|
|
8273
8275
|
const body = canonicalArchitectureYaml({
|
|
8276
|
+
...declared,
|
|
8274
8277
|
schemaVersion: "archcontext.constraint/v1",
|
|
8275
8278
|
id: constraint.constraintId,
|
|
8276
|
-
|
|
8277
|
-
subject: constraint.subjectId,
|
|
8278
|
-
status: constraint.status,
|
|
8279
|
-
...constraint.severity ? { severity: constraint.severity } : {},
|
|
8280
|
-
...constraint.summary ? { summary: constraint.summary } : {},
|
|
8281
|
-
...constraint.metadata ? { metadata: constraint.metadata } : {}
|
|
8279
|
+
...constraint.severity ? { severity: constraint.severity } : {}
|
|
8282
8280
|
});
|
|
8283
8281
|
return projectionFile(`.archcontext/model/constraints/${pathSegment(constraint.constraintId)}.yaml`, body, "constraint", constraint.constraintId);
|
|
8284
8282
|
})
|
|
@@ -8453,12 +8451,11 @@ function yamlRecordToLedgerOperation(value, path, schemaVersion) {
|
|
|
8453
8451
|
const entityId = requireStringField(value, "id", path);
|
|
8454
8452
|
const entity = {
|
|
8455
8453
|
entityId,
|
|
8456
|
-
kind:
|
|
8457
|
-
canonicalName:
|
|
8458
|
-
status:
|
|
8459
|
-
|
|
8460
|
-
|
|
8461
|
-
...metadataField(value, ["schemaVersion", "id", "kind", "name", "canonicalName", "status", "path", "summary"])
|
|
8454
|
+
kind: requireStringField(value, "kind", path),
|
|
8455
|
+
canonicalName: requireStringField(value, "name", path),
|
|
8456
|
+
status: declaredStatus(value.status, path),
|
|
8457
|
+
summary: requireStringField(value, "summary", path),
|
|
8458
|
+
metadata: { declared: value }
|
|
8462
8459
|
};
|
|
8463
8460
|
return { operation: { op: "upsert_entity", entity }, targetKind: "entity", targetId: entityId };
|
|
8464
8461
|
}
|
|
@@ -8466,12 +8463,12 @@ function yamlRecordToLedgerOperation(value, path, schemaVersion) {
|
|
|
8466
8463
|
const relationId = requireStringField(value, "id", path);
|
|
8467
8464
|
const relation = {
|
|
8468
8465
|
relationId,
|
|
8469
|
-
kind:
|
|
8466
|
+
kind: requireStringField(value, "kind", path),
|
|
8470
8467
|
sourceEntityId: requireStringField(value, "source", path),
|
|
8471
8468
|
targetEntityId: requireStringField(value, "target", path),
|
|
8472
|
-
status:
|
|
8473
|
-
|
|
8474
|
-
|
|
8469
|
+
status: "active",
|
|
8470
|
+
summary: requireStringField(value, "intent", path),
|
|
8471
|
+
metadata: { declared: value }
|
|
8475
8472
|
};
|
|
8476
8473
|
return { operation: { op: "upsert_relation", relation }, targetKind: "relation", targetId: relationId };
|
|
8477
8474
|
}
|
|
@@ -8481,25 +8478,28 @@ function yamlRecordToLedgerOperation(value, path, schemaVersion) {
|
|
|
8481
8478
|
const target = repoScopedTarget(value.target, path, "target");
|
|
8482
8479
|
const relation = {
|
|
8483
8480
|
relationId,
|
|
8484
|
-
kind:
|
|
8481
|
+
kind: requireStringField(value, "kind", path),
|
|
8485
8482
|
sourceEntityId: source,
|
|
8486
8483
|
targetEntityId: target,
|
|
8487
|
-
status:
|
|
8488
|
-
|
|
8489
|
-
|
|
8484
|
+
status: "active",
|
|
8485
|
+
summary: requireStringField(value, "intent", path),
|
|
8486
|
+
metadata: { declared: value }
|
|
8490
8487
|
};
|
|
8491
8488
|
return { operation: { op: "upsert_relation", relation }, targetKind: "relation", targetId: relationId };
|
|
8492
8489
|
}
|
|
8493
8490
|
if (schemaVersion === "archcontext.constraint/v1") {
|
|
8494
8491
|
const constraintId = requireStringField(value, "id", path);
|
|
8492
|
+
const scope = requiredRecordField(value, "scope", path);
|
|
8493
|
+
const rule = requiredRecordField(value, "rule", path);
|
|
8494
|
+
const subjectId = firstStringArrayValue(scope.nodes) ?? firstStringArrayValue(scope.relations) ?? "repository";
|
|
8495
8495
|
const constraint = {
|
|
8496
8496
|
constraintId,
|
|
8497
|
-
kind:
|
|
8498
|
-
subjectId
|
|
8499
|
-
status:
|
|
8500
|
-
|
|
8501
|
-
|
|
8502
|
-
|
|
8497
|
+
kind: requireStringField(rule, "type", path),
|
|
8498
|
+
subjectId,
|
|
8499
|
+
status: "active",
|
|
8500
|
+
severity: requiredSeverity(value.severity, path),
|
|
8501
|
+
summary: requireStringField(value, "rationale", path),
|
|
8502
|
+
metadata: { declared: value }
|
|
8503
8503
|
};
|
|
8504
8504
|
return { operation: { op: "upsert_constraint", constraint }, targetKind: "constraint", targetId: constraintId };
|
|
8505
8505
|
}
|
|
@@ -8636,19 +8636,30 @@ function isEvidenceOnlySchema(schemaVersion) {
|
|
|
8636
8636
|
function isGeneratedProjectionFile(file) {
|
|
8637
8637
|
return file.schemaVersion === "archcontext.generated/v1" || file.path === ".archcontext/generated" || file.path.startsWith(".archcontext/generated/") || file.body.includes("Generated by ArchContext");
|
|
8638
8638
|
}
|
|
8639
|
-
function
|
|
8640
|
-
const
|
|
8641
|
-
|
|
8642
|
-
|
|
8643
|
-
|
|
8644
|
-
|
|
8639
|
+
function declaredProjectionRecord(metadata, allowedSchemaVersions, label) {
|
|
8640
|
+
const declared = metadata?.declared;
|
|
8641
|
+
if (!isRecord(declared))
|
|
8642
|
+
throw new Error(`architecture-ledger-projection-invalid: ${label} has no declared YAML record`);
|
|
8643
|
+
const schemaVersion = requireStringField(declared, "schemaVersion", label);
|
|
8644
|
+
if (!allowedSchemaVersions.includes(schemaVersion)) {
|
|
8645
|
+
throw new Error(`architecture-ledger-projection-invalid: ${label} has unsupported declared schema ${schemaVersion}`);
|
|
8645
8646
|
}
|
|
8646
|
-
|
|
8647
|
-
|
|
8648
|
-
|
|
8649
|
-
|
|
8650
|
-
|
|
8651
|
-
|
|
8647
|
+
return declared;
|
|
8648
|
+
}
|
|
8649
|
+
function requiredRecordField(value, key, path) {
|
|
8650
|
+
const field = value[key];
|
|
8651
|
+
if (!isRecord(field))
|
|
8652
|
+
throw new Error(`${path}: ${key} is required`);
|
|
8653
|
+
return field;
|
|
8654
|
+
}
|
|
8655
|
+
function firstStringArrayValue(value) {
|
|
8656
|
+
return Array.isArray(value) ? value.find((item) => typeof item === "string" && item.length > 0) : undefined;
|
|
8657
|
+
}
|
|
8658
|
+
function requiredSeverity(value, path) {
|
|
8659
|
+
const severity = severityField(value);
|
|
8660
|
+
if (!severity)
|
|
8661
|
+
throw new Error(`${path}: severity is required`);
|
|
8662
|
+
return severity;
|
|
8652
8663
|
}
|
|
8653
8664
|
function declaredYamlSubject(value, path) {
|
|
8654
8665
|
const id = stringField(value, "id");
|
|
@@ -8681,8 +8692,10 @@ function requireStringField(value, key, path) {
|
|
|
8681
8692
|
function stringField(value, key) {
|
|
8682
8693
|
return typeof value[key] === "string" ? value[key] : undefined;
|
|
8683
8694
|
}
|
|
8684
|
-
function
|
|
8685
|
-
|
|
8695
|
+
function declaredStatus(value, path) {
|
|
8696
|
+
if (value === "active" || value === "planned" || value === "deprecated" || value === "removed")
|
|
8697
|
+
return value;
|
|
8698
|
+
throw new Error(`${path}: status is required`);
|
|
8686
8699
|
}
|
|
8687
8700
|
function severityField(value) {
|
|
8688
8701
|
return value === "notice" || value === "warning" || value === "error" || value === "critical" ? value : undefined;
|
|
@@ -8768,10 +8781,85 @@ function validateArchitectureLedgerEvent(event) {
|
|
|
8768
8781
|
if (event.worktree.workspaceId.length === 0 || event.worktree.storageWorkspaceId.length === 0 || event.worktree.headSha.length === 0) {
|
|
8769
8782
|
throw new Error(`architecture-ledger-invalid-event: worktree identity required for ${event.eventId}`);
|
|
8770
8783
|
}
|
|
8784
|
+
assertArchitectureLedgerPersistenceSafe(event.payload, `event.payload for ${event.eventId}`);
|
|
8785
|
+
if (event.extensions)
|
|
8786
|
+
assertArchitectureLedgerPersistenceSafe(event.extensions, `event.extensions for ${event.eventId}`);
|
|
8787
|
+
assertArchitectureLedgerPersistenceSafe(event.provenance, `event.provenance for ${event.eventId}`);
|
|
8771
8788
|
const payload = architectureLedgerPayload(event);
|
|
8772
8789
|
for (const operation of payload.operations ?? [])
|
|
8773
8790
|
validateArchitectureLedgerOperation(operation, event.eventId);
|
|
8774
8791
|
}
|
|
8792
|
+
var ARCHITECTURE_LEDGER_MAX_PERSISTED_JSON_BYTES = 262144;
|
|
8793
|
+
var ARCHITECTURE_LEDGER_MAX_PERSISTED_STRING_BYTES = 8192;
|
|
8794
|
+
var ARCHITECTURE_LEDGER_MAX_PERSISTED_DEPTH = 32;
|
|
8795
|
+
var ARCHITECTURE_LEDGER_FORBIDDEN_RAW_KEYS = new Set([
|
|
8796
|
+
"rawsource",
|
|
8797
|
+
"sourcebody",
|
|
8798
|
+
"sourcecode",
|
|
8799
|
+
"rawdiff",
|
|
8800
|
+
"diffbody",
|
|
8801
|
+
"rawpatch",
|
|
8802
|
+
"patchbody",
|
|
8803
|
+
"prompt",
|
|
8804
|
+
"promptbody",
|
|
8805
|
+
"completion",
|
|
8806
|
+
"completionbody",
|
|
8807
|
+
"codegraphoutput",
|
|
8808
|
+
"fullcodegraphoutput",
|
|
8809
|
+
"webhookbody",
|
|
8810
|
+
"rawwebhook",
|
|
8811
|
+
"secret",
|
|
8812
|
+
"secrets",
|
|
8813
|
+
"credential",
|
|
8814
|
+
"credentials",
|
|
8815
|
+
"privatekey",
|
|
8816
|
+
"accesstoken",
|
|
8817
|
+
"refreshtoken"
|
|
8818
|
+
]);
|
|
8819
|
+
var ARCHITECTURE_LEDGER_SAFE_SENSITIVE_KEY_SUFFIX = /(?:digest|id|ids|count|counts|ref|refs|path|paths|persisted)$/;
|
|
8820
|
+
var ARCHITECTURE_LEDGER_FORBIDDEN_STRING_PATTERNS = [
|
|
8821
|
+
/diff --git /,
|
|
8822
|
+
/-----BEGIN [A-Z ]*PRIVATE KEY-----/,
|
|
8823
|
+
/\bgh[pousr]_[A-Za-z0-9]{20,}\b/,
|
|
8824
|
+
/\bsk-[A-Za-z0-9_-]{20,}\b/,
|
|
8825
|
+
/\bAKIA[A-Z0-9]{16}\b/,
|
|
8826
|
+
/\bBearer\s+[A-Za-z0-9._~+/=-]{12,}\b/i,
|
|
8827
|
+
/\b(?:api[_-]?key|secret|token|password|private[_-]?key)\s*[:=]\s*["']?[^\s"',;]{8,}/i
|
|
8828
|
+
];
|
|
8829
|
+
function assertArchitectureLedgerPersistenceSafe(value, label = "architecture-ledger-value") {
|
|
8830
|
+
const encoded = JSON.stringify(value);
|
|
8831
|
+
if (Buffer2.byteLength(encoded, "utf8") > ARCHITECTURE_LEDGER_MAX_PERSISTED_JSON_BYTES) {
|
|
8832
|
+
throw new Error(`architecture-ledger-privacy-denied: persisted JSON exceeds size limit at ${label}`);
|
|
8833
|
+
}
|
|
8834
|
+
visit(value, label, 0);
|
|
8835
|
+
function visit(current, path, depth) {
|
|
8836
|
+
if (depth > ARCHITECTURE_LEDGER_MAX_PERSISTED_DEPTH) {
|
|
8837
|
+
throw new Error(`architecture-ledger-privacy-denied: persisted JSON exceeds depth limit at ${path}`);
|
|
8838
|
+
}
|
|
8839
|
+
if (typeof current === "string") {
|
|
8840
|
+
if (Buffer2.byteLength(current, "utf8") > ARCHITECTURE_LEDGER_MAX_PERSISTED_STRING_BYTES) {
|
|
8841
|
+
throw new Error(`architecture-ledger-privacy-denied: persisted string exceeds size limit at ${path}`);
|
|
8842
|
+
}
|
|
8843
|
+
if (ARCHITECTURE_LEDGER_FORBIDDEN_STRING_PATTERNS.some((pattern) => pattern.test(current))) {
|
|
8844
|
+
throw new Error(`architecture-ledger-privacy-denied: forbidden raw or secret-shaped content at ${path}`);
|
|
8845
|
+
}
|
|
8846
|
+
return;
|
|
8847
|
+
}
|
|
8848
|
+
if (current === null || typeof current !== "object")
|
|
8849
|
+
return;
|
|
8850
|
+
if (Array.isArray(current)) {
|
|
8851
|
+
current.forEach((item, index) => visit(item, `${path}[${index}]`, depth + 1));
|
|
8852
|
+
return;
|
|
8853
|
+
}
|
|
8854
|
+
for (const [key, child] of Object.entries(current)) {
|
|
8855
|
+
const normalizedKey = key.replace(/[-_]/g, "").toLowerCase();
|
|
8856
|
+
if (ARCHITECTURE_LEDGER_FORBIDDEN_RAW_KEYS.has(normalizedKey) && !ARCHITECTURE_LEDGER_SAFE_SENSITIVE_KEY_SUFFIX.test(normalizedKey)) {
|
|
8857
|
+
throw new Error(`architecture-ledger-privacy-denied: forbidden persisted field at ${path}.${key}`);
|
|
8858
|
+
}
|
|
8859
|
+
visit(child, `${path}.${key}`, depth + 1);
|
|
8860
|
+
}
|
|
8861
|
+
}
|
|
8862
|
+
}
|
|
8775
8863
|
function validateArchitectureLedgerOperation(operation, eventId) {
|
|
8776
8864
|
if (!operation || typeof operation !== "object" || !("op" in operation)) {
|
|
8777
8865
|
throw new Error(`architecture-ledger-invalid-operation: ${eventId}`);
|
|
@@ -8894,7 +8982,7 @@ function requireNonEmpty(value, label, eventId) {
|
|
|
8894
8982
|
}
|
|
8895
8983
|
}
|
|
8896
8984
|
function requireActiveStatus(value, eventId) {
|
|
8897
|
-
if (!["active", "deprecated", "removed"].includes(value)) {
|
|
8985
|
+
if (!["active", "planned", "deprecated", "removed"].includes(value)) {
|
|
8898
8986
|
throw new Error(`architecture-ledger-invalid-operation: invalid status for ${eventId}`);
|
|
8899
8987
|
}
|
|
8900
8988
|
}
|
|
@@ -8943,6 +9031,7 @@ var REQUIRED_LOCAL_STORE_TABLES = [
|
|
|
8943
9031
|
];
|
|
8944
9032
|
var SQLITE_PRAGMAS = [
|
|
8945
9033
|
"PRAGMA journal_mode = WAL",
|
|
9034
|
+
"PRAGMA synchronous = FULL",
|
|
8946
9035
|
"PRAGMA foreign_keys = ON",
|
|
8947
9036
|
"PRAGMA busy_timeout = 5000"
|
|
8948
9037
|
];
|
|
@@ -9464,6 +9553,13 @@ var LOCAL_SQLITE_MIGRATIONS = [
|
|
|
9464
9553
|
)`,
|
|
9465
9554
|
"CREATE INDEX IF NOT EXISTS idx_audit_runs_status ON audit_runs(storage_repository_id, storage_workspace_id, status)"
|
|
9466
9555
|
]
|
|
9556
|
+
},
|
|
9557
|
+
{
|
|
9558
|
+
id: "0011_changeset_cleanup_cursor",
|
|
9559
|
+
statements: [
|
|
9560
|
+
"ALTER TABLE changeset_journal ADD COLUMN cleanup_completed_at TEXT",
|
|
9561
|
+
"CREATE INDEX IF NOT EXISTS idx_changeset_journal_cleanup_pending ON changeset_journal(status, cleanup_completed_at, updated_at)"
|
|
9562
|
+
]
|
|
9467
9563
|
}
|
|
9468
9564
|
];
|
|
9469
9565
|
var ARCHCONTEXT_STATE_DIR_ENV = "ARCHCONTEXT_STATE_DIR";
|
|
@@ -10041,17 +10137,18 @@ function readHeadSha(root) {
|
|
|
10041
10137
|
// packages/local-runtime/runtime-daemon/src/index.ts
|
|
10042
10138
|
import { randomBytes } from "node:crypto";
|
|
10043
10139
|
import { execFileSync as execFileSync6 } from "node:child_process";
|
|
10044
|
-
import { chmodSync as chmodSync3, closeSync as closeSync5, existsSync as
|
|
10140
|
+
import { chmodSync as chmodSync3, closeSync as closeSync5, existsSync as existsSync13, mkdirSync as mkdirSync7, mkdtempSync as mkdtempSync4, openSync as openSync5, readdirSync as readdirSync8, readFileSync as readFileSync11, rmSync as rmSync8, statSync as statSync6, writeFileSync as writeFileSync6 } from "node:fs";
|
|
10045
10141
|
import { createServer } from "node:http";
|
|
10046
10142
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
10047
10143
|
import { dirname as dirname8, join as join8, resolve as resolve15 } from "node:path";
|
|
10048
10144
|
|
|
10049
10145
|
// packages/core/changeset-engine/src/index.ts
|
|
10050
10146
|
init_src();
|
|
10051
|
-
import { closeSync as closeSync3, existsSync as
|
|
10147
|
+
import { closeSync as closeSync3, existsSync as existsSync5, fsyncSync as fsyncSync2, lstatSync as lstatSync2, mkdirSync as mkdirSync3, openSync as openSync3, readFileSync as readFileSync5, renameSync as renameSync2, rmSync as rmSync3, writeFileSync as writeFileSync2 } from "node:fs";
|
|
10052
10148
|
import { dirname as dirname3, resolve as resolve7 } from "node:path";
|
|
10053
10149
|
|
|
10054
10150
|
// packages/core/policy-engine/src/index.ts
|
|
10151
|
+
import { existsSync as existsSync4, realpathSync as realpathSync5 } from "node:fs";
|
|
10055
10152
|
import { isAbsolute as isAbsolute3, relative as relative4, resolve as resolve6, sep as sep3 } from "node:path";
|
|
10056
10153
|
init_src();
|
|
10057
10154
|
var INVALID_COMPAT_REASONS = new Set(["just in case", "safer to keep", "many internal callers", "large diff", "old code already exists"]);
|
|
@@ -10062,6 +10159,7 @@ var ALLOWLIST = [
|
|
|
10062
10159
|
".archcontext/practices/",
|
|
10063
10160
|
".archcontext/waivers/",
|
|
10064
10161
|
".archcontext/decisions/",
|
|
10162
|
+
".archcontext/backups/",
|
|
10065
10163
|
".archcontext/generated/",
|
|
10066
10164
|
"docs/architecture/"
|
|
10067
10165
|
];
|
|
@@ -10098,6 +10196,21 @@ function assertAllowedArchContextPath(root, relativePath) {
|
|
|
10098
10196
|
if (targetFromRoot === "" || targetFromRoot === ".." || targetFromRoot.startsWith(`..${sep3}`) || isAbsolute3(targetFromRoot)) {
|
|
10099
10197
|
throw new Error(`Path escapes repository: ${relativePath}`);
|
|
10100
10198
|
}
|
|
10199
|
+
if (!existsSync4(absoluteRoot))
|
|
10200
|
+
return;
|
|
10201
|
+
const canonicalRoot = realpathSync5.native(absoluteRoot);
|
|
10202
|
+
let existingAncestor = absoluteTarget;
|
|
10203
|
+
while (!existsSync4(existingAncestor)) {
|
|
10204
|
+
const parent = resolve6(existingAncestor, "..");
|
|
10205
|
+
if (parent === existingAncestor)
|
|
10206
|
+
throw new Error(`Path has no existing repository ancestor: ${relativePath}`);
|
|
10207
|
+
existingAncestor = parent;
|
|
10208
|
+
}
|
|
10209
|
+
const canonicalAncestor = realpathSync5.native(existingAncestor);
|
|
10210
|
+
const ancestorFromRoot = relative4(canonicalRoot, canonicalAncestor);
|
|
10211
|
+
if (ancestorFromRoot === ".." || ancestorFromRoot.startsWith(`..${sep3}`) || isAbsolute3(ancestorFromRoot)) {
|
|
10212
|
+
throw new Error(`Path escapes repository through symlink: ${relativePath}`);
|
|
10213
|
+
}
|
|
10101
10214
|
}
|
|
10102
10215
|
function evaluateChangeSetPaths(root, paths) {
|
|
10103
10216
|
const findings = [];
|
|
@@ -10157,6 +10270,7 @@ class ChangeSetEngine {
|
|
|
10157
10270
|
if (!approved)
|
|
10158
10271
|
throw new Error("ChangeSet must be approved before apply");
|
|
10159
10272
|
const deps = this.requireDeps();
|
|
10273
|
+
await this.validateModel(root, draft, deps, "before");
|
|
10160
10274
|
const backups = [];
|
|
10161
10275
|
const journalId = await deps.journal?.beginChangeSet(root, draft);
|
|
10162
10276
|
let journalCommitted = false;
|
|
@@ -10171,10 +10285,7 @@ class ChangeSetEngine {
|
|
|
10171
10285
|
if (options.faultAfterOperations && applied >= options.faultAfterOperations)
|
|
10172
10286
|
throw new Error("fault-injection");
|
|
10173
10287
|
}
|
|
10174
|
-
} else {
|
|
10175
|
-
this.rebuildGeneratedProjection(root, deps);
|
|
10176
|
-
applied += 1;
|
|
10177
|
-
}
|
|
10288
|
+
} else {}
|
|
10178
10289
|
if (options.faultAfterOperations && applied >= options.faultAfterOperations)
|
|
10179
10290
|
throw new Error("fault-injection");
|
|
10180
10291
|
continue;
|
|
@@ -10186,15 +10297,24 @@ class ChangeSetEngine {
|
|
|
10186
10297
|
if (options.faultAfterOperations && applied >= options.faultAfterOperations)
|
|
10187
10298
|
throw new Error("fault-injection");
|
|
10188
10299
|
}
|
|
10189
|
-
|
|
10190
|
-
|
|
10191
|
-
|
|
10192
|
-
|
|
10300
|
+
for (const projection of deps.projection.planGeneratedProjection(root)) {
|
|
10301
|
+
await this.applyFileOperation(root, projection.path, projection.expectedHash, projection.body, projection.operation, backups, journalId, applied + 1);
|
|
10302
|
+
applied += 1;
|
|
10303
|
+
if (options.faultAfterOperations && applied >= options.faultAfterOperations)
|
|
10304
|
+
throw new Error("fault-injection");
|
|
10305
|
+
}
|
|
10306
|
+
await this.validateModel(root, draft, deps, "after");
|
|
10307
|
+
const commit = await options.afterModelValidatedBeforeCommit?.({ root, draft, journalId });
|
|
10308
|
+
if (commit?.journalCommitted)
|
|
10309
|
+
journalCommitted = true;
|
|
10310
|
+
if (journalId && !journalCommitted) {
|
|
10193
10311
|
await deps.journal?.commitChangeSet(journalId);
|
|
10194
10312
|
journalCommitted = true;
|
|
10195
10313
|
}
|
|
10196
10314
|
try {
|
|
10197
10315
|
cleanupBackups(backups);
|
|
10316
|
+
if (journalId)
|
|
10317
|
+
await deps.journal?.completeChangeSetCleanup(journalId);
|
|
10198
10318
|
} catch {}
|
|
10199
10319
|
const appliedDraft = { ...draft, status: "applied" };
|
|
10200
10320
|
this.states.set(draft.id, appliedDraft);
|
|
@@ -10210,11 +10330,11 @@ class ChangeSetEngine {
|
|
|
10210
10330
|
throw error;
|
|
10211
10331
|
}
|
|
10212
10332
|
}
|
|
10213
|
-
|
|
10214
|
-
deps.
|
|
10215
|
-
|
|
10216
|
-
|
|
10217
|
-
|
|
10333
|
+
async validateModel(root, draft, deps, phase) {
|
|
10334
|
+
const result = await deps.modelStore.validateModel({ root, repositoryId: draft.reason.taskSessionId, headSha: draft.base.headSha });
|
|
10335
|
+
if (!result.valid) {
|
|
10336
|
+
throw new Error(`ChangeSet model validation failed ${phase} apply: ${result.errors.join("; ") || "unknown validation error"}`);
|
|
10337
|
+
}
|
|
10218
10338
|
}
|
|
10219
10339
|
requireDeps() {
|
|
10220
10340
|
if (!this.deps)
|
|
@@ -10225,19 +10345,16 @@ class ChangeSetEngine {
|
|
|
10225
10345
|
assertSafeTarget(root, path);
|
|
10226
10346
|
const deps = this.requireDeps();
|
|
10227
10347
|
const absolute = resolve7(root, path);
|
|
10228
|
-
const existed =
|
|
10348
|
+
const existed = existsSync5(absolute);
|
|
10229
10349
|
const backupPath = `${absolute}.archctx-backup`;
|
|
10230
10350
|
const tempPath = operation === "delete_entity" ? undefined : `${absolute}.archctx-tmp-${process.pid}-${sequence}`;
|
|
10231
|
-
if (
|
|
10351
|
+
if (existsSync5(backupPath))
|
|
10232
10352
|
throw new Error(`Backup path already exists: ${path}`);
|
|
10233
|
-
if (existed)
|
|
10353
|
+
if (existed)
|
|
10234
10354
|
assertExpectedHash(absolute, expectedHash);
|
|
10235
|
-
|
|
10236
|
-
fsyncDirectory2(dirname3(absolute));
|
|
10237
|
-
} else if (expectedHash !== "missing") {
|
|
10355
|
+
else if (expectedHash !== "missing")
|
|
10238
10356
|
throw new Error(`Expected missing file hash for new path: ${path}`);
|
|
10239
|
-
}
|
|
10240
|
-
backups.push({ path: absolute, backupPath, tempPath, existed });
|
|
10357
|
+
const backup = { path: absolute, backupPath, tempPath, existed };
|
|
10241
10358
|
if (journalId) {
|
|
10242
10359
|
await deps.journal?.recordChangeSetFile(journalId, {
|
|
10243
10360
|
path,
|
|
@@ -10247,6 +10364,11 @@ class ChangeSetEngine {
|
|
|
10247
10364
|
operation
|
|
10248
10365
|
});
|
|
10249
10366
|
}
|
|
10367
|
+
backups.push(backup);
|
|
10368
|
+
if (existed) {
|
|
10369
|
+
renameSync2(absolute, backupPath);
|
|
10370
|
+
fsyncDirectory2(dirname3(absolute));
|
|
10371
|
+
}
|
|
10250
10372
|
if (operation === "delete_entity") {
|
|
10251
10373
|
rmSync3(absolute, { force: true });
|
|
10252
10374
|
} else {
|
|
@@ -10257,7 +10379,7 @@ class ChangeSetEngine {
|
|
|
10257
10379
|
function assertSafeTarget(root, path) {
|
|
10258
10380
|
assertAllowedArchContextPath(root, path);
|
|
10259
10381
|
const absolute = resolve7(root, path);
|
|
10260
|
-
if (
|
|
10382
|
+
if (existsSync5(absolute) && lstatSync2(absolute).isSymbolicLink()) {
|
|
10261
10383
|
throw new Error(`Refusing to write symlink target: ${path}`);
|
|
10262
10384
|
}
|
|
10263
10385
|
}
|
|
@@ -10332,9 +10454,14 @@ function rollback(backups) {
|
|
|
10332
10454
|
for (const backup of backups.reverse()) {
|
|
10333
10455
|
if (backup.tempPath)
|
|
10334
10456
|
rmSync3(backup.tempPath, { recursive: true, force: true });
|
|
10335
|
-
|
|
10336
|
-
|
|
10337
|
-
|
|
10457
|
+
if (backup.existed) {
|
|
10458
|
+
if (existsSync5(backup.backupPath)) {
|
|
10459
|
+
rmSync3(backup.path, { recursive: true, force: true });
|
|
10460
|
+
renameSync2(backup.backupPath, backup.path);
|
|
10461
|
+
}
|
|
10462
|
+
} else {
|
|
10463
|
+
rmSync3(backup.path, { recursive: true, force: true });
|
|
10464
|
+
}
|
|
10338
10465
|
fsyncDirectory2(dirname3(backup.path));
|
|
10339
10466
|
}
|
|
10340
10467
|
}
|
|
@@ -10540,7 +10667,7 @@ function digestSuffix2(digest) {
|
|
|
10540
10667
|
}
|
|
10541
10668
|
|
|
10542
10669
|
// packages/core/application/src/index.ts
|
|
10543
|
-
import { existsSync as
|
|
10670
|
+
import { existsSync as existsSync8, statSync as statSync5 } from "node:fs";
|
|
10544
10671
|
import { extname, resolve as resolve10 } from "node:path";
|
|
10545
10672
|
|
|
10546
10673
|
// packages/core/context-compiler/src/index.ts
|
|
@@ -10548,7 +10675,7 @@ init_src();
|
|
|
10548
10675
|
|
|
10549
10676
|
// packages/core/practice-catalog/src/index.ts
|
|
10550
10677
|
init_src();
|
|
10551
|
-
import { existsSync as
|
|
10678
|
+
import { existsSync as existsSync6, lstatSync as lstatSync3, readdirSync as readdirSync4, readFileSync as readFileSync6, realpathSync as realpathSync6 } from "node:fs";
|
|
10552
10679
|
import { dirname as dirname4, relative as relative5, resolve as resolve8, sep as sep4 } from "node:path";
|
|
10553
10680
|
import { fileURLToPath } from "node:url";
|
|
10554
10681
|
var PRACTICE_CATALOG_VERSION = "2026.06.0";
|
|
@@ -10797,7 +10924,7 @@ function loadProfileFiles(dir, errors) {
|
|
|
10797
10924
|
}
|
|
10798
10925
|
function loadRepoOverlayAssets(root, errors) {
|
|
10799
10926
|
const overlayRoot = resolve8(root, ".archcontext/practices");
|
|
10800
|
-
if (!
|
|
10927
|
+
if (!existsSync6(overlayRoot))
|
|
10801
10928
|
return [];
|
|
10802
10929
|
const out = [];
|
|
10803
10930
|
for (const path of listDataFiles(overlayRoot, overlayRoot, errors)) {
|
|
@@ -11140,7 +11267,7 @@ function validateProfile(value, path, errors) {
|
|
|
11140
11267
|
return invalid ? undefined : profile;
|
|
11141
11268
|
}
|
|
11142
11269
|
function listDataFiles(dir, allowedRoot, errors) {
|
|
11143
|
-
if (!
|
|
11270
|
+
if (!existsSync6(dir))
|
|
11144
11271
|
return [];
|
|
11145
11272
|
const out = [];
|
|
11146
11273
|
const rootReal = safeRealpath(allowedRoot);
|
|
@@ -11202,7 +11329,7 @@ function assertRealChild(root, path) {
|
|
|
11202
11329
|
}
|
|
11203
11330
|
function safeRealpath(path) {
|
|
11204
11331
|
try {
|
|
11205
|
-
return
|
|
11332
|
+
return realpathSync6.native(path);
|
|
11206
11333
|
} catch {
|
|
11207
11334
|
return resolve8(path);
|
|
11208
11335
|
}
|
|
@@ -11773,7 +11900,7 @@ function checkResult(input, result) {
|
|
|
11773
11900
|
}
|
|
11774
11901
|
// packages/core/practice-engine/src/enforcement.ts
|
|
11775
11902
|
init_src();
|
|
11776
|
-
import { existsSync as
|
|
11903
|
+
import { existsSync as existsSync7, lstatSync as lstatSync4, readdirSync as readdirSync5, readFileSync as readFileSync7 } from "node:fs";
|
|
11777
11904
|
import { basename as basename3, join as join4, relative as relative6, resolve as resolve9, sep as sep5 } from "node:path";
|
|
11778
11905
|
var ENFORCEMENT_RANK = { advisory: 0, checkpoint: 1, complete: 2 };
|
|
11779
11906
|
var POLICY_MODES = new Set(["advisory", "active", "fail-open", "fail-closed"]);
|
|
@@ -11798,7 +11925,7 @@ function shouldEvaluatePracticeEnforcement(policy) {
|
|
|
11798
11925
|
}
|
|
11799
11926
|
function loadPracticeEnforcementPolicy(root) {
|
|
11800
11927
|
const path = resolve9(root, ".archcontext/policies/practices.yaml");
|
|
11801
|
-
if (!
|
|
11928
|
+
if (!existsSync7(path))
|
|
11802
11929
|
return defaultPracticeEnforcementPolicy();
|
|
11803
11930
|
assertRepoPolicyFile(root, path, ".archcontext/policies/practices.yaml");
|
|
11804
11931
|
const parsed = parseJsonYamlFile(path);
|
|
@@ -11806,7 +11933,7 @@ function loadPracticeEnforcementPolicy(root) {
|
|
|
11806
11933
|
}
|
|
11807
11934
|
function loadPracticeWaivers(root) {
|
|
11808
11935
|
const dir = resolve9(root, ".archcontext/waivers");
|
|
11809
|
-
if (!
|
|
11936
|
+
if (!existsSync7(dir))
|
|
11810
11937
|
return [];
|
|
11811
11938
|
const stat = lstatSync4(dir);
|
|
11812
11939
|
if (stat.isSymbolicLink())
|
|
@@ -11829,7 +11956,7 @@ function loadPracticeWaiverOwnerRegistry(root) {
|
|
|
11829
11956
|
const modelDir = resolve9(root, ".archcontext/model/nodes");
|
|
11830
11957
|
const sources = [];
|
|
11831
11958
|
const subjects = [];
|
|
11832
|
-
if (
|
|
11959
|
+
if (existsSync7(modelDir)) {
|
|
11833
11960
|
if (lstatSync4(modelDir).isSymbolicLink())
|
|
11834
11961
|
throw new Error("practice-owner-registry-symlink-denied");
|
|
11835
11962
|
for (const path of collectFiles(root, ".archcontext/model/nodes")) {
|
|
@@ -13642,7 +13769,7 @@ function classifyCheckpointPath(root, path) {
|
|
|
13642
13769
|
if (isGeneratedCheckpointPath(path))
|
|
13643
13770
|
return "generated";
|
|
13644
13771
|
const absolute = resolve10(root, path);
|
|
13645
|
-
if (!
|
|
13772
|
+
if (!existsSync8(absolute))
|
|
13646
13773
|
return "deleted";
|
|
13647
13774
|
try {
|
|
13648
13775
|
if (!statSync5(absolute).isFile())
|
|
@@ -14848,8 +14975,14 @@ function uniqueSorted4(values) {
|
|
|
14848
14975
|
|
|
14849
14976
|
// packages/core/projection-engine/src/index.ts
|
|
14850
14977
|
init_src();
|
|
14851
|
-
import { existsSync as
|
|
14978
|
+
import { existsSync as existsSync9, readdirSync as readdirSync6, readFileSync as readFileSync8 } from "node:fs";
|
|
14852
14979
|
import { basename as basename4, resolve as resolve11 } from "node:path";
|
|
14980
|
+
function nativeNodeSource(node) {
|
|
14981
|
+
const value = node.source;
|
|
14982
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
14983
|
+
return;
|
|
14984
|
+
return value;
|
|
14985
|
+
}
|
|
14853
14986
|
var ARCHITECTURE_DOCS_RENDERER_VERSION = "archcontext.docs-renderer/v1";
|
|
14854
14987
|
var ARCHITECTURE_DOCS_GENERATED_BEGIN_PREFIX = "<!-- BEGIN ARCHCONTEXT:generated";
|
|
14855
14988
|
var ARCHITECTURE_DOCS_GENERATED_END_PREFIX = "<!-- END ARCHCONTEXT:generated";
|
|
@@ -14885,12 +15018,6 @@ function renderArchitectureDocumentationProjection(input) {
|
|
|
14885
15018
|
});
|
|
14886
15019
|
const targets = rendered.map((file) => file.target);
|
|
14887
15020
|
const expectedByPath = new Map(rendered.map((file) => [file.path, file]));
|
|
14888
|
-
const drift = architectureDocumentationProjectionDrift({
|
|
14889
|
-
targets,
|
|
14890
|
-
expectedFiles: rendered,
|
|
14891
|
-
existingFiles: input.existingFiles ?? []
|
|
14892
|
-
});
|
|
14893
|
-
const rejected = drift.diffs.filter((diff) => diff.reasonCode === "projection-ambiguous-ownership");
|
|
14894
15021
|
const projectionDigest = digestJson({
|
|
14895
15022
|
rendererVersion,
|
|
14896
15023
|
sourceDigest: input.sourceDigest,
|
|
@@ -14901,6 +15028,38 @@ function renderArchitectureDocumentationProjection(input) {
|
|
|
14901
15028
|
generatedBodyDigest: file.generatedBodyDigest
|
|
14902
15029
|
})).sort((left, right) => left.path.localeCompare(right.path))
|
|
14903
15030
|
});
|
|
15031
|
+
const manifestValue = {
|
|
15032
|
+
schemaVersion: "archcontext.architecture-docs-projection-manifest/v1",
|
|
15033
|
+
rendererVersion,
|
|
15034
|
+
sourceDigest: input.sourceDigest,
|
|
15035
|
+
projectionDigest,
|
|
15036
|
+
targetCount: targets.length,
|
|
15037
|
+
fileCount: rendered.length,
|
|
15038
|
+
targets: targets.map((target) => ({
|
|
15039
|
+
targetId: target.targetId,
|
|
15040
|
+
type: target.type,
|
|
15041
|
+
scope: target.scope,
|
|
15042
|
+
path: target.path,
|
|
15043
|
+
ownership: target.ownership,
|
|
15044
|
+
rendererVersion: target.rendererVersion,
|
|
15045
|
+
format: target.format,
|
|
15046
|
+
sourceDigest: target.sourceDigest,
|
|
15047
|
+
outputDigest: target.outputDigest
|
|
15048
|
+
}))
|
|
15049
|
+
};
|
|
15050
|
+
const manifest = {
|
|
15051
|
+
path: "docs/architecture/.projection-manifest.json",
|
|
15052
|
+
body: `${JSON.stringify(manifestValue, null, 2)}
|
|
15053
|
+
`,
|
|
15054
|
+
digest: digestJson(manifestValue)
|
|
15055
|
+
};
|
|
15056
|
+
const drift = architectureDocumentationProjectionDrift({
|
|
15057
|
+
targets,
|
|
15058
|
+
expectedFiles: rendered,
|
|
15059
|
+
expectedManifest: manifest,
|
|
15060
|
+
existingFiles: input.existingFiles ?? []
|
|
15061
|
+
});
|
|
15062
|
+
const rejected = drift.diffs.filter((diff) => diff.reasonCode === "projection-ambiguous-ownership");
|
|
14904
15063
|
return {
|
|
14905
15064
|
schemaVersion: "archcontext.architecture-docs-projection-plan/v1",
|
|
14906
15065
|
rendererVersion,
|
|
@@ -14908,6 +15067,7 @@ function renderArchitectureDocumentationProjection(input) {
|
|
|
14908
15067
|
projectionDigest,
|
|
14909
15068
|
targets,
|
|
14910
15069
|
files: rendered.filter((file) => !rejected.some((diff) => diff.path === file.path && diff.targetId === file.target.targetId)),
|
|
15070
|
+
manifest,
|
|
14911
15071
|
drift: {
|
|
14912
15072
|
...drift,
|
|
14913
15073
|
diffs: drift.diffs.map((diff) => ({
|
|
@@ -14938,7 +15098,7 @@ function architectureDocumentationSourceDigest(input) {
|
|
|
14938
15098
|
}
|
|
14939
15099
|
function loadArchitectureDecisionRecords(root) {
|
|
14940
15100
|
const dir = resolve11(root, "docs/adr");
|
|
14941
|
-
if (!
|
|
15101
|
+
if (!existsSync9(dir))
|
|
14942
15102
|
return [];
|
|
14943
15103
|
return readdirSync6(dir).filter((file) => /^ADR-\d{4}-.+\.md$/.test(file)).sort().map((file) => {
|
|
14944
15104
|
const path = `docs/adr/${file}`;
|
|
@@ -14965,12 +15125,12 @@ function loadArchitectureDocumentationFiles(root) {
|
|
|
14965
15125
|
"docs/architecture/.projection-manifest.json"
|
|
14966
15126
|
]) {
|
|
14967
15127
|
const absolute = resolve11(root, entry);
|
|
14968
|
-
if (
|
|
15128
|
+
if (existsSync9(absolute))
|
|
14969
15129
|
files.push({ path: entry, body: readFileSync8(absolute, "utf8") });
|
|
14970
15130
|
}
|
|
14971
15131
|
for (const dir of ["docs/architecture/modules", "docs/architecture/relations"]) {
|
|
14972
15132
|
const absoluteDir = resolve11(root, dir);
|
|
14973
|
-
if (!
|
|
15133
|
+
if (!existsSync9(absoluteDir))
|
|
14974
15134
|
continue;
|
|
14975
15135
|
for (const file of readdirSync6(absoluteDir).filter((name) => name.endsWith(".md")).sort()) {
|
|
14976
15136
|
const path = `${dir}/${file}`;
|
|
@@ -15076,25 +15236,12 @@ function mermaidId(id) {
|
|
|
15076
15236
|
return stableId(id).replace(/-/g, "_").replace(/\./g, "_");
|
|
15077
15237
|
}
|
|
15078
15238
|
function readYamlObjects(dir) {
|
|
15079
|
-
if (!
|
|
15239
|
+
if (!existsSync9(dir))
|
|
15080
15240
|
return [];
|
|
15081
|
-
return readdirSync6(dir).filter((file) => /\.ya?ml$/.test(file)).sort().map((file) =>
|
|
15082
|
-
|
|
15083
|
-
|
|
15084
|
-
|
|
15085
|
-
for (const line of body.split(/\r?\n/)) {
|
|
15086
|
-
const match = line.match(/^([A-Za-z0-9_-]+):\s*(.*)$/);
|
|
15087
|
-
if (!match)
|
|
15088
|
-
continue;
|
|
15089
|
-
const [, key, raw] = match;
|
|
15090
|
-
if (raw.startsWith('"') && raw.endsWith('"'))
|
|
15091
|
-
out[key] = JSON.parse(raw);
|
|
15092
|
-
else if (raw === "true" || raw === "false")
|
|
15093
|
-
out[key] = raw === "true";
|
|
15094
|
-
else
|
|
15095
|
-
out[key] = raw;
|
|
15096
|
-
}
|
|
15097
|
-
return out;
|
|
15241
|
+
return readdirSync6(dir).filter((file) => /\.ya?ml$/.test(file)).sort().map((file) => {
|
|
15242
|
+
const path = resolve11(dir, file);
|
|
15243
|
+
return parseJsonOrStableYaml(readFileSync8(path, "utf8"), path);
|
|
15244
|
+
});
|
|
15098
15245
|
}
|
|
15099
15246
|
function escapeMermaid(value) {
|
|
15100
15247
|
return value.replace(/"/g, "'");
|
|
@@ -15324,6 +15471,34 @@ function architectureDocumentationProjectionDrift(input) {
|
|
|
15324
15471
|
const expectedByPath = new Map(input.expectedFiles.map((file) => [file.path, file]));
|
|
15325
15472
|
const targetIds = new Set(input.targets.map((target) => target.targetId));
|
|
15326
15473
|
const diffs = [];
|
|
15474
|
+
const existingManifest = existingByPath.get(input.expectedManifest.path);
|
|
15475
|
+
if (!existingManifest) {
|
|
15476
|
+
diffs.push({
|
|
15477
|
+
path: input.expectedManifest.path,
|
|
15478
|
+
reasonCode: "projection-manifest-missing",
|
|
15479
|
+
expectedDigest: input.expectedManifest.digest
|
|
15480
|
+
});
|
|
15481
|
+
} else {
|
|
15482
|
+
try {
|
|
15483
|
+
const parsed = JSON.parse(existingManifest.body);
|
|
15484
|
+
const actualDigest = digestJson(parsed);
|
|
15485
|
+
if (actualDigest !== input.expectedManifest.digest) {
|
|
15486
|
+
diffs.push({
|
|
15487
|
+
path: input.expectedManifest.path,
|
|
15488
|
+
reasonCode: "projection-manifest-stale",
|
|
15489
|
+
expectedDigest: input.expectedManifest.digest,
|
|
15490
|
+
actualDigest
|
|
15491
|
+
});
|
|
15492
|
+
}
|
|
15493
|
+
} catch {
|
|
15494
|
+
diffs.push({
|
|
15495
|
+
path: input.expectedManifest.path,
|
|
15496
|
+
reasonCode: "projection-manifest-invalid",
|
|
15497
|
+
expectedDigest: input.expectedManifest.digest,
|
|
15498
|
+
actualDigest: digestJson({ path: existingManifest.path, body: existingManifest.body })
|
|
15499
|
+
});
|
|
15500
|
+
}
|
|
15501
|
+
}
|
|
15327
15502
|
for (const expected of input.expectedFiles) {
|
|
15328
15503
|
const existing = existingByPath.get(expected.path);
|
|
15329
15504
|
if (!existing) {
|
|
@@ -15362,6 +15537,8 @@ function architectureDocumentationProjectionDrift(input) {
|
|
|
15362
15537
|
}
|
|
15363
15538
|
}
|
|
15364
15539
|
for (const existing of input.existingFiles) {
|
|
15540
|
+
if (existing.path === input.expectedManifest.path)
|
|
15541
|
+
continue;
|
|
15365
15542
|
if (!isManagedArchitectureDocumentationPath(existing.path) || expectedByPath.has(existing.path))
|
|
15366
15543
|
continue;
|
|
15367
15544
|
const region = findAnyGeneratedRegion(existing.body);
|
|
@@ -15410,7 +15587,7 @@ function generatedEndMarker(targetId) {
|
|
|
15410
15587
|
return `${ARCHITECTURE_DOCS_GENERATED_END_PREFIX} target="${targetId}" -->`;
|
|
15411
15588
|
}
|
|
15412
15589
|
function isManagedArchitectureDocumentationPath(path) {
|
|
15413
|
-
return /^docs\/architecture\/(modules|relations)\/.+\.md$/.test(path) || /^docs\/architecture\/diagrams\/architecture\.(mmd|likec4|structurizr\.json)$/.test(path) || path === "docs/architecture/index.md" || path === "docs/architecture/changelog.md" || path === "docs/architecture/decisions/index.md";
|
|
15590
|
+
return /^docs\/architecture\/(modules|relations)\/.+\.md$/.test(path) || /^docs\/architecture\/diagrams\/architecture\.(mmd|likec4|structurizr\.json)$/.test(path) || path === "docs/architecture/index.md" || path === "docs/architecture/changelog.md" || path === "docs/architecture/decisions/index.md" || path === "docs/architecture/.projection-manifest.json";
|
|
15414
15591
|
}
|
|
15415
15592
|
function pathSegment2(id) {
|
|
15416
15593
|
return stableId(id).replace(/\./g, "-");
|
|
@@ -15441,6 +15618,61 @@ function escapeDsl(value) {
|
|
|
15441
15618
|
function escapeRegExp(value) {
|
|
15442
15619
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
15443
15620
|
}
|
|
15621
|
+
function resolveArchitectureOwnerForPath(nodes, path) {
|
|
15622
|
+
const candidates = [];
|
|
15623
|
+
for (const node of nodes) {
|
|
15624
|
+
const source = nativeNodeSource(node);
|
|
15625
|
+
const include = source?.include ?? [];
|
|
15626
|
+
if (include.length === 0)
|
|
15627
|
+
continue;
|
|
15628
|
+
const excluded = (source?.exclude ?? []).some((pattern) => matchesGlob(path, pattern));
|
|
15629
|
+
if (excluded)
|
|
15630
|
+
continue;
|
|
15631
|
+
const specificity = include.filter((pattern) => matchesGlob(path, pattern)).reduce((max, pattern) => Math.max(max, globLiteralPrefixLength(pattern)), -1);
|
|
15632
|
+
if (specificity >= 0)
|
|
15633
|
+
candidates.push({ node, specificity });
|
|
15634
|
+
}
|
|
15635
|
+
if (candidates.length === 0)
|
|
15636
|
+
return { status: "no-match" };
|
|
15637
|
+
const maxSpecificity = candidates.reduce((max, candidate) => Math.max(max, candidate.specificity), -1);
|
|
15638
|
+
const winners = candidates.filter((candidate) => candidate.specificity === maxSpecificity).map((candidate) => candidate.node);
|
|
15639
|
+
if (winners.length > 1)
|
|
15640
|
+
return { status: "ambiguous", candidates: winners };
|
|
15641
|
+
return { status: "matched", node: winners[0] };
|
|
15642
|
+
}
|
|
15643
|
+
function matchesGlob(path, pattern) {
|
|
15644
|
+
return globToRegExp(pattern).test(path);
|
|
15645
|
+
}
|
|
15646
|
+
function globLiteralPrefixLength(pattern) {
|
|
15647
|
+
const index = pattern.search(/[*?]/);
|
|
15648
|
+
return index === -1 ? pattern.length : index;
|
|
15649
|
+
}
|
|
15650
|
+
function globToRegExp(pattern) {
|
|
15651
|
+
let out = "";
|
|
15652
|
+
let index = 0;
|
|
15653
|
+
while (index < pattern.length) {
|
|
15654
|
+
if (pattern.startsWith("**/", index)) {
|
|
15655
|
+
out += "(?:.*/)?";
|
|
15656
|
+
index += 3;
|
|
15657
|
+
continue;
|
|
15658
|
+
}
|
|
15659
|
+
if (pattern.startsWith("**", index)) {
|
|
15660
|
+
out += ".*";
|
|
15661
|
+
index += 2;
|
|
15662
|
+
continue;
|
|
15663
|
+
}
|
|
15664
|
+
const char = pattern[index];
|
|
15665
|
+
if (char === "*") {
|
|
15666
|
+
out += "[^/]*";
|
|
15667
|
+
} else if (char === "?") {
|
|
15668
|
+
out += "[^/]";
|
|
15669
|
+
} else {
|
|
15670
|
+
out += escapeRegExp(char);
|
|
15671
|
+
}
|
|
15672
|
+
index += 1;
|
|
15673
|
+
}
|
|
15674
|
+
return new RegExp(`^${out}$`);
|
|
15675
|
+
}
|
|
15444
15676
|
|
|
15445
15677
|
// packages/local-runtime/explorer-html/src/index.ts
|
|
15446
15678
|
var STATUS_STYLE = {
|
|
@@ -16565,7 +16797,7 @@ init_src();
|
|
|
16565
16797
|
|
|
16566
16798
|
// packages/local-runtime/git-adapter/src/index.ts
|
|
16567
16799
|
import { execFileSync as execFileSync4, spawnSync as spawnSync2 } from "node:child_process";
|
|
16568
|
-
import { existsSync as
|
|
16800
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync4, mkdtempSync as mkdtempSync2, rmSync as rmSync4 } from "node:fs";
|
|
16569
16801
|
import { dirname as dirname5, join as join5, resolve as resolve12 } from "node:path";
|
|
16570
16802
|
import { tmpdir } from "node:os";
|
|
16571
16803
|
init_src();
|
|
@@ -16579,7 +16811,7 @@ function findRepositoryRoot2(start) {
|
|
|
16579
16811
|
} catch {
|
|
16580
16812
|
let cursor = resolve12(start);
|
|
16581
16813
|
while (true) {
|
|
16582
|
-
if (
|
|
16814
|
+
if (existsSync10(resolve12(cursor, ".git")))
|
|
16583
16815
|
return cursor;
|
|
16584
16816
|
const parent = dirname5(cursor);
|
|
16585
16817
|
if (parent === cursor)
|
|
@@ -16850,7 +17082,7 @@ function isGitWorktreeError(error) {
|
|
|
16850
17082
|
// packages/local-runtime/local-store-sqlite/src/index.ts
|
|
16851
17083
|
import { execFileSync as execFileSync5 } from "node:child_process";
|
|
16852
17084
|
import { createHash as createHash6, randomUUID as randomUUID2 } from "node:crypto";
|
|
16853
|
-
import { chmodSync as chmodSync2, closeSync as closeSync4, existsSync as
|
|
17085
|
+
import { chmodSync as chmodSync2, closeSync as closeSync4, existsSync as existsSync11, fsyncSync as fsyncSync3, lstatSync as lstatSync5, mkdirSync as mkdirSync5, openSync as openSync4, readFileSync as readFileSync9, realpathSync as realpathSync7, renameSync as renameSync3, rmSync as rmSync5, writeFileSync as writeFileSync3 } from "node:fs";
|
|
16854
17086
|
import { readdir, readFile } from "node:fs/promises";
|
|
16855
17087
|
import { createRequire as createRequire3 } from "node:module";
|
|
16856
17088
|
import { homedir as homedir2 } from "node:os";
|
|
@@ -16893,6 +17125,7 @@ var REQUIRED_LOCAL_STORE_TABLES2 = [
|
|
|
16893
17125
|
];
|
|
16894
17126
|
var SQLITE_PRAGMAS2 = [
|
|
16895
17127
|
"PRAGMA journal_mode = WAL",
|
|
17128
|
+
"PRAGMA synchronous = FULL",
|
|
16896
17129
|
"PRAGMA foreign_keys = ON",
|
|
16897
17130
|
"PRAGMA busy_timeout = 5000"
|
|
16898
17131
|
];
|
|
@@ -17414,8 +17647,16 @@ var LOCAL_SQLITE_MIGRATIONS2 = [
|
|
|
17414
17647
|
)`,
|
|
17415
17648
|
"CREATE INDEX IF NOT EXISTS idx_audit_runs_status ON audit_runs(storage_repository_id, storage_workspace_id, status)"
|
|
17416
17649
|
]
|
|
17650
|
+
},
|
|
17651
|
+
{
|
|
17652
|
+
id: "0011_changeset_cleanup_cursor",
|
|
17653
|
+
statements: [
|
|
17654
|
+
"ALTER TABLE changeset_journal ADD COLUMN cleanup_completed_at TEXT",
|
|
17655
|
+
"CREATE INDEX IF NOT EXISTS idx_changeset_journal_cleanup_pending ON changeset_journal(status, cleanup_completed_at, updated_at)"
|
|
17656
|
+
]
|
|
17417
17657
|
}
|
|
17418
17658
|
];
|
|
17659
|
+
var CHANGESET_STARTUP_CLEANUP_LIMIT = 100;
|
|
17419
17660
|
var ARCHCONTEXT_STATE_DIR_ENV2 = "ARCHCONTEXT_STATE_DIR";
|
|
17420
17661
|
var ARCHCONTEXT_LOCAL_STORE_PATH_ENV2 = "ARCHCONTEXT_LOCAL_STORE_PATH";
|
|
17421
17662
|
function defaultArchContextStateRoot2(env = process.env, platform = process.platform, home = homedir2()) {
|
|
@@ -17474,10 +17715,10 @@ function migrateLegacyLocalStoreIfNeeded2(root = process.cwd(), env = process.en
|
|
|
17474
17715
|
});
|
|
17475
17716
|
}
|
|
17476
17717
|
ensurePrivateDir2(dirname6(paths.localStorePath));
|
|
17477
|
-
const legacyExists =
|
|
17718
|
+
const legacyExists = existsSync11(paths.legacyLocalStorePath);
|
|
17478
17719
|
const integrityCheck = {};
|
|
17479
17720
|
const quarantinedFiles = [];
|
|
17480
|
-
const targetExists =
|
|
17721
|
+
const targetExists = existsSync11(paths.localStorePath);
|
|
17481
17722
|
if (targetExists) {
|
|
17482
17723
|
try {
|
|
17483
17724
|
integrityCheck.target = assertCurrentLocalStore2(paths.localStorePath);
|
|
@@ -17941,6 +18182,11 @@ class SqliteLocalStore {
|
|
|
17941
18182
|
throw new Error(`ChangeSet journal not found: ${journalId}`);
|
|
17942
18183
|
db.prepare("UPDATE changeset_journal SET status = ?, updated_at = ?, completed_at = ? WHERE journal_id = ?").run("committed", nowIso2(), nowIso2(), journalId);
|
|
17943
18184
|
}
|
|
18185
|
+
async completeChangeSetCleanup(journalId) {
|
|
18186
|
+
const db = await this.database();
|
|
18187
|
+
const completedAt = nowIso2();
|
|
18188
|
+
db.prepare("UPDATE changeset_journal SET cleanup_completed_at = ?, updated_at = ? WHERE journal_id = ? AND status = ?").run(completedAt, completedAt, journalId, "committed");
|
|
18189
|
+
}
|
|
17944
18190
|
async abortChangeSet(journalId, reason) {
|
|
17945
18191
|
const db = await this.database();
|
|
17946
18192
|
const row = db.prepare("SELECT metadata_json FROM changeset_journal WHERE journal_id = ?").get(journalId);
|
|
@@ -17951,37 +18197,61 @@ class SqliteLocalStore {
|
|
|
17951
18197
|
}
|
|
17952
18198
|
recoverPendingChangeSets() {
|
|
17953
18199
|
const db = this.requireOpenDatabase();
|
|
17954
|
-
const committed = db.prepare(
|
|
18200
|
+
const committed = db.prepare(`SELECT journal_id, files_json FROM changeset_journal
|
|
18201
|
+
WHERE status = ? AND cleanup_completed_at IS NULL
|
|
18202
|
+
ORDER BY updated_at, journal_id
|
|
18203
|
+
LIMIT ?`).all("committed", CHANGESET_STARTUP_CLEANUP_LIMIT);
|
|
17955
18204
|
for (const row of committed) {
|
|
17956
|
-
|
|
18205
|
+
try {
|
|
18206
|
+
cleanupCommittedJournalFiles(JSON.parse(String(row.files_json)));
|
|
18207
|
+
const completedAt = nowIso2();
|
|
18208
|
+
db.prepare("UPDATE changeset_journal SET cleanup_completed_at = ?, updated_at = ? WHERE journal_id = ?").run(completedAt, completedAt, String(row.journal_id));
|
|
18209
|
+
} catch {}
|
|
17957
18210
|
}
|
|
17958
18211
|
const rows = db.prepare("SELECT journal_id, root, files_json, metadata_json FROM changeset_journal WHERE status = ?").all("pending");
|
|
18212
|
+
let recovered = 0;
|
|
17959
18213
|
for (const row of rows) {
|
|
17960
|
-
|
|
17961
|
-
|
|
17962
|
-
|
|
17963
|
-
|
|
17964
|
-
|
|
17965
|
-
const
|
|
17966
|
-
if (
|
|
17967
|
-
|
|
18214
|
+
let metadata = {};
|
|
18215
|
+
try {
|
|
18216
|
+
const files = JSON.parse(String(row.files_json));
|
|
18217
|
+
metadata = JSON.parse(String(row.metadata_json));
|
|
18218
|
+
const plannedLedgerEvent = changeSetJournalPlannedLedgerEvent(metadata);
|
|
18219
|
+
const existingLedgerEvent = plannedLedgerEvent ? architectureEventByIdempotency(db, plannedLedgerEvent) : undefined;
|
|
18220
|
+
if (plannedLedgerEvent && existingLedgerEvent) {
|
|
18221
|
+
const expectedEventHash = normalizeArchitectureLedgerEvent(plannedLedgerEvent, existingLedgerEvent.previousEventHash).eventHash;
|
|
18222
|
+
if (expectedEventHash !== existingLedgerEvent.event.eventHash) {
|
|
18223
|
+
throw new Error(`changeset-ledger-recovery-idempotency-conflict: ${plannedLedgerEvent.idempotencyKey}`);
|
|
18224
|
+
}
|
|
18225
|
+
cleanupCommittedJournalFiles(files);
|
|
18226
|
+
const completedAt2 = nowIso2();
|
|
18227
|
+
db.prepare("UPDATE changeset_journal SET status = ?, metadata_json = ?, updated_at = ?, completed_at = ?, cleanup_completed_at = ? WHERE journal_id = ?").run("committed", stableJson(withChangeSetJournalArchitectureLedger(metadata, {
|
|
18228
|
+
recovery: {
|
|
18229
|
+
schemaVersion: "archcontext.changeset-ledger-recovery/v1",
|
|
18230
|
+
status: "ledger-append-detected",
|
|
18231
|
+
eventId: existingLedgerEvent.event.eventId,
|
|
18232
|
+
eventHash: existingLedgerEvent.event.eventHash ?? "",
|
|
18233
|
+
recoveredAt: completedAt2
|
|
18234
|
+
}
|
|
18235
|
+
})), completedAt2, completedAt2, completedAt2, String(row.journal_id));
|
|
18236
|
+
recovered += 1;
|
|
18237
|
+
continue;
|
|
17968
18238
|
}
|
|
17969
|
-
|
|
17970
|
-
|
|
17971
|
-
|
|
17972
|
-
|
|
17973
|
-
|
|
17974
|
-
|
|
17975
|
-
|
|
17976
|
-
|
|
18239
|
+
recoverJournalFiles(String(row.root), files);
|
|
18240
|
+
const completedAt = nowIso2();
|
|
18241
|
+
db.prepare("UPDATE changeset_journal SET status = ?, updated_at = ?, completed_at = ?, cleanup_completed_at = ? WHERE journal_id = ?").run("recovered", completedAt, completedAt, completedAt, String(row.journal_id));
|
|
18242
|
+
recovered += 1;
|
|
18243
|
+
} catch (error) {
|
|
18244
|
+
db.prepare("UPDATE changeset_journal SET metadata_json = ?, updated_at = ? WHERE journal_id = ?").run(stableJson({
|
|
18245
|
+
...metadata,
|
|
18246
|
+
recoveryError: {
|
|
18247
|
+
schemaVersion: "archcontext.changeset-recovery-error/v1",
|
|
18248
|
+
message: error instanceof Error ? error.message : String(error),
|
|
18249
|
+
failedAt: nowIso2()
|
|
17977
18250
|
}
|
|
17978
|
-
})
|
|
17979
|
-
continue;
|
|
18251
|
+
}), nowIso2(), String(row.journal_id));
|
|
17980
18252
|
}
|
|
17981
|
-
recoverJournalFiles(String(row.root), files);
|
|
17982
|
-
db.prepare("UPDATE changeset_journal SET status = ?, updated_at = ?, completed_at = ? WHERE journal_id = ?").run("recovered", nowIso2(), nowIso2(), String(row.journal_id));
|
|
17983
18253
|
}
|
|
17984
|
-
return
|
|
18254
|
+
return recovered;
|
|
17985
18255
|
}
|
|
17986
18256
|
async saveTaskState(taskSessionId, state) {
|
|
17987
18257
|
const db = await this.database();
|
|
@@ -18079,52 +18349,38 @@ class SqliteLocalStore {
|
|
|
18079
18349
|
for (const event of input.events)
|
|
18080
18350
|
validateArchitectureLedgerEvent(event);
|
|
18081
18351
|
const db = await this.database();
|
|
18082
|
-
const startedAt = Date.now();
|
|
18083
|
-
const appendedEvents = [];
|
|
18084
|
-
const duplicateEvents = [];
|
|
18085
18352
|
db.exec("BEGIN IMMEDIATE");
|
|
18086
18353
|
try {
|
|
18087
|
-
|
|
18088
|
-
for (const event of input.events) {
|
|
18089
|
-
const duplicate = architectureEventByIdempotency(db, event);
|
|
18090
|
-
if (duplicate) {
|
|
18091
|
-
const expectedDuplicateHash = normalizeArchitectureLedgerEvent(event, duplicate.previousEventHash).eventHash;
|
|
18092
|
-
if (expectedDuplicateHash !== duplicate.event.eventHash) {
|
|
18093
|
-
throw new Error(`architecture-ledger-idempotency-conflict: ${event.idempotencyKey}`);
|
|
18094
|
-
}
|
|
18095
|
-
duplicateEvents.push(duplicate.event);
|
|
18096
|
-
continue;
|
|
18097
|
-
}
|
|
18098
|
-
const previousEventHash = latestArchitectureEventHash(db, event.repository.storageRepositoryId, event.worktree.storageWorkspaceId);
|
|
18099
|
-
const normalized = normalizeArchitectureLedgerEvent(event, previousEventHash);
|
|
18100
|
-
insertArchitectureEvent(db, normalized);
|
|
18101
|
-
persistArchitectureLedgerArtifacts(db, normalized);
|
|
18102
|
-
materializeArchitectureLedgerEvent(db, normalized);
|
|
18103
|
-
appendedEvents.push(normalized);
|
|
18104
|
-
processed += 1;
|
|
18105
|
-
if (input.faultAfterEvents !== undefined && processed >= input.faultAfterEvents)
|
|
18106
|
-
throw new Error("architecture-ledger-fault-injection");
|
|
18107
|
-
}
|
|
18108
|
-
const scope = architectureScopeFromEvent(input.events[0] ?? duplicateEvents[0]);
|
|
18109
|
-
const state = scope ? readArchitectureLedgerStateFromDb(db, scope) : emptyArchitectureLedgerState();
|
|
18110
|
-
if (scope) {
|
|
18111
|
-
recordArchitectureLedgerOperation(db, {
|
|
18112
|
-
scope,
|
|
18113
|
-
operationKind: "append_events",
|
|
18114
|
-
durationMs: Date.now() - startedAt,
|
|
18115
|
-
rowCount: appendedEvents.length,
|
|
18116
|
-
rebuildReason: null
|
|
18117
|
-
});
|
|
18118
|
-
}
|
|
18354
|
+
const result = appendArchitectureEventsInOpenTransaction(db, input);
|
|
18119
18355
|
db.exec("COMMIT");
|
|
18120
|
-
return
|
|
18121
|
-
|
|
18122
|
-
|
|
18123
|
-
|
|
18124
|
-
|
|
18125
|
-
|
|
18126
|
-
|
|
18127
|
-
|
|
18356
|
+
return result;
|
|
18357
|
+
} catch (error) {
|
|
18358
|
+
db.exec("ROLLBACK");
|
|
18359
|
+
throw error;
|
|
18360
|
+
}
|
|
18361
|
+
}
|
|
18362
|
+
async appendArchitectureEventsAndCommitChangeSet(journalId, input) {
|
|
18363
|
+
if (input.writer !== "runtime-daemon")
|
|
18364
|
+
throw new Error("architecture-ledger-writer-must-be-runtime-daemon");
|
|
18365
|
+
for (const event of input.events)
|
|
18366
|
+
validateArchitectureLedgerEvent(event);
|
|
18367
|
+
const db = await this.database();
|
|
18368
|
+
db.exec("BEGIN IMMEDIATE");
|
|
18369
|
+
try {
|
|
18370
|
+
const row = db.prepare("SELECT status, metadata_json FROM changeset_journal WHERE journal_id = ?").get(journalId);
|
|
18371
|
+
if (!row)
|
|
18372
|
+
throw new Error(`ChangeSet journal not found: ${journalId}`);
|
|
18373
|
+
if (String(row.status) !== "pending")
|
|
18374
|
+
throw new Error(`ChangeSet journal is not pending: ${journalId}`);
|
|
18375
|
+
const metadata = JSON.parse(String(row.metadata_json));
|
|
18376
|
+
const result = appendArchitectureEventsInOpenTransaction(db, input);
|
|
18377
|
+
const completedAt = nowIso2();
|
|
18378
|
+
db.prepare("UPDATE changeset_journal SET status = ?, metadata_json = ?, updated_at = ?, completed_at = ? WHERE journal_id = ?").run("committed", stableJson(withChangeSetJournalArchitectureLedger(metadata, {
|
|
18379
|
+
append: changeSetLedgerAppendSummary(result),
|
|
18380
|
+
appendedAt: completedAt
|
|
18381
|
+
})), completedAt, completedAt, journalId);
|
|
18382
|
+
db.exec("COMMIT");
|
|
18383
|
+
return result;
|
|
18128
18384
|
} catch (error) {
|
|
18129
18385
|
db.exec("ROLLBACK");
|
|
18130
18386
|
throw error;
|
|
@@ -18138,24 +18394,50 @@ class SqliteLocalStore {
|
|
|
18138
18394
|
WHERE storage_repository_id = ?
|
|
18139
18395
|
AND storage_workspace_id = ?
|
|
18140
18396
|
${statusClause}
|
|
18141
|
-
ORDER BY created_at DESC, run_id ASC`).all(input.repository.storageRepositoryId, input.worktree
|
|
18397
|
+
ORDER BY created_at DESC, run_id ASC`).all(input.repository.storageRepositoryId, architectureLedgerWorkspaceKey(input.worktree), ...statuses).map((row) => JSON.parse(String(row.run_json)));
|
|
18398
|
+
}
|
|
18399
|
+
async resolveArchitectureLedgerScope(input) {
|
|
18400
|
+
const db = await this.database();
|
|
18401
|
+
const rows = db.prepare(`SELECT event_json FROM architecture_events
|
|
18402
|
+
WHERE storage_repository_id = ? AND workspace_id = ? AND branch = ?
|
|
18403
|
+
ORDER BY event_sequence DESC`).all(input.repository.storageRepositoryId, input.worktree.workspaceId, input.worktree.branch);
|
|
18404
|
+
for (const row of rows) {
|
|
18405
|
+
const event = JSON.parse(String(row.event_json));
|
|
18406
|
+
if (event.worktree.storageWorkspaceId === input.worktree.storageWorkspaceId) {
|
|
18407
|
+
return architectureScopeFromEvent(event);
|
|
18408
|
+
}
|
|
18409
|
+
}
|
|
18410
|
+
return input;
|
|
18411
|
+
}
|
|
18412
|
+
async resolveLatestArchitectureLedgerScope(input) {
|
|
18413
|
+
const db = await this.database();
|
|
18414
|
+
const rows = db.prepare(`SELECT event_json FROM architecture_events
|
|
18415
|
+
WHERE storage_repository_id = ? AND workspace_id = ?
|
|
18416
|
+
ORDER BY event_sequence DESC`).all(input.repository.storageRepositoryId, input.worktree.workspaceId);
|
|
18417
|
+
for (const row of rows) {
|
|
18418
|
+
const event = JSON.parse(String(row.event_json));
|
|
18419
|
+
if (event.worktree.storageWorkspaceId === input.worktree.storageWorkspaceId) {
|
|
18420
|
+
return architectureScopeFromEvent(event);
|
|
18421
|
+
}
|
|
18422
|
+
}
|
|
18423
|
+
return input;
|
|
18142
18424
|
}
|
|
18143
18425
|
async getAuditRun(input) {
|
|
18144
18426
|
const db = await this.database();
|
|
18145
18427
|
const row = db.prepare(`SELECT run_json FROM audit_runs
|
|
18146
|
-
WHERE storage_repository_id = ? AND storage_workspace_id = ? AND run_id = ?`).get(input.repository.storageRepositoryId, input.worktree.
|
|
18428
|
+
WHERE storage_repository_id = ? AND storage_workspace_id = ? AND run_id = ?`).get(input.repository.storageRepositoryId, architectureLedgerWorkspaceKey(input.worktree), architectureLedgerStorageId(input.worktree, input.runId));
|
|
18147
18429
|
return row ? JSON.parse(String(row.run_json)) : undefined;
|
|
18148
18430
|
}
|
|
18149
18431
|
async readArchitectureLedgerSourceCursor(input) {
|
|
18150
18432
|
const db = await this.database();
|
|
18151
18433
|
const row = db.prepare(`SELECT cursor_json FROM source_cursors
|
|
18152
|
-
WHERE storage_repository_id = ? AND storage_workspace_id = ? AND cursor_id = ?`).get(input.repository.storageRepositoryId, input.worktree.
|
|
18434
|
+
WHERE storage_repository_id = ? AND storage_workspace_id = ? AND cursor_id = ?`).get(input.repository.storageRepositoryId, architectureLedgerWorkspaceKey(input.worktree), architectureLedgerStorageId(input.worktree, input.cursorId));
|
|
18153
18435
|
return row ? JSON.parse(String(row.cursor_json)) : undefined;
|
|
18154
18436
|
}
|
|
18155
18437
|
async createArchitectureLedgerSnapshot(input) {
|
|
18156
18438
|
const db = await this.database();
|
|
18157
18439
|
const startedAt = Date.now();
|
|
18158
|
-
const latest = latestArchitectureEvent(db, input.repository.storageRepositoryId, input.worktree
|
|
18440
|
+
const latest = latestArchitectureEvent(db, input.repository.storageRepositoryId, architectureLedgerWorkspaceKey(input.worktree));
|
|
18159
18441
|
if (!latest)
|
|
18160
18442
|
throw new Error("architecture-ledger-snapshot-requires-event");
|
|
18161
18443
|
const state = readArchitectureLedgerStateFromDb(db, input);
|
|
@@ -18169,7 +18451,7 @@ class SqliteLocalStore {
|
|
|
18169
18451
|
(snapshot_id, repository_id, storage_repository_id, workspace_id, storage_workspace_id, branch, head_sha, worktree_digest,
|
|
18170
18452
|
source_mode, last_event_id, last_event_hash, graph_digest, projection_digest, entity_count, relation_count,
|
|
18171
18453
|
constraint_count, input_digests_json, snapshot_json, created_at)
|
|
18172
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(snapshot.snapshotId, snapshot.repository.repositoryId, snapshot.repository.storageRepositoryId, snapshot.worktree.workspaceId, snapshot.worktree
|
|
18454
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(snapshot.snapshotId, snapshot.repository.repositoryId, snapshot.repository.storageRepositoryId, snapshot.worktree.workspaceId, architectureLedgerWorkspaceKey(snapshot.worktree), snapshot.worktree.branch, snapshot.worktree.headSha, snapshot.worktree.worktreeDigest, snapshot.sourceMode, architectureLedgerStorageId(snapshot.worktree, snapshot.eventCursor.lastEventId), snapshot.eventCursor.lastEventHash, snapshot.graphDigest, snapshot.projectionDigest, snapshot.entityCount, snapshot.relationCount, snapshot.constraintCount, stableJson(snapshot.inputDigests), stableJson(snapshot), snapshot.createdAt);
|
|
18173
18455
|
recordArchitectureLedgerOperation(db, {
|
|
18174
18456
|
scope: input,
|
|
18175
18457
|
operationKind: "create_snapshot",
|
|
@@ -18238,16 +18520,16 @@ class SqliteLocalStore {
|
|
|
18238
18520
|
const db = await this.database();
|
|
18239
18521
|
const startedAt = Date.now();
|
|
18240
18522
|
const snapshot = db.prepare(`SELECT last_event_id FROM architecture_snapshots
|
|
18241
|
-
WHERE snapshot_id = ? AND storage_repository_id = ? AND storage_workspace_id = ?`).get(input.beforeSnapshotId, input.repository.storageRepositoryId, input.worktree
|
|
18523
|
+
WHERE snapshot_id = ? AND storage_repository_id = ? AND storage_workspace_id = ?`).get(input.beforeSnapshotId, input.repository.storageRepositoryId, architectureLedgerWorkspaceKey(input.worktree));
|
|
18242
18524
|
if (!snapshot)
|
|
18243
18525
|
throw new Error(`architecture-ledger-snapshot-not-found: ${input.beforeSnapshotId}`);
|
|
18244
18526
|
const cursor = db.prepare("SELECT event_sequence FROM architecture_events WHERE event_id = ?").get(String(snapshot.last_event_id));
|
|
18245
18527
|
if (!cursor)
|
|
18246
18528
|
throw new Error(`architecture-ledger-snapshot-cursor-not-found: ${String(snapshot.last_event_id)}`);
|
|
18247
18529
|
const before = Number(db.prepare(`SELECT COUNT(*) AS count FROM architecture_events
|
|
18248
|
-
WHERE storage_repository_id = ? AND storage_workspace_id = ? AND event_sequence <= ? AND compacted_by_snapshot_id IS NULL`).get(input.repository.storageRepositoryId, input.worktree
|
|
18530
|
+
WHERE storage_repository_id = ? AND storage_workspace_id = ? AND event_sequence <= ? AND compacted_by_snapshot_id IS NULL`).get(input.repository.storageRepositoryId, architectureLedgerWorkspaceKey(input.worktree), Number(cursor.event_sequence))?.count ?? 0);
|
|
18249
18531
|
db.prepare(`UPDATE architecture_events SET compacted_by_snapshot_id = ?
|
|
18250
|
-
WHERE storage_repository_id = ? AND storage_workspace_id = ? AND event_sequence <= ? AND compacted_by_snapshot_id IS NULL`).run(input.beforeSnapshotId, input.repository.storageRepositoryId, input.worktree
|
|
18532
|
+
WHERE storage_repository_id = ? AND storage_workspace_id = ? AND event_sequence <= ? AND compacted_by_snapshot_id IS NULL`).run(input.beforeSnapshotId, input.repository.storageRepositoryId, architectureLedgerWorkspaceKey(input.worktree), Number(cursor.event_sequence));
|
|
18251
18533
|
recordArchitectureLedgerOperation(db, {
|
|
18252
18534
|
scope: input,
|
|
18253
18535
|
operationKind: "compact_events",
|
|
@@ -18267,7 +18549,7 @@ class SqliteLocalStore {
|
|
|
18267
18549
|
const replay = await this.verifyArchitectureLedgerReplay(input);
|
|
18268
18550
|
if (!replay.ok)
|
|
18269
18551
|
failures.push(...replay.mismatches);
|
|
18270
|
-
const snapshotCount = Number(db.prepare("SELECT COUNT(*) AS count FROM architecture_snapshots WHERE storage_repository_id = ? AND storage_workspace_id = ?").get(input.repository.storageRepositoryId, input.worktree
|
|
18552
|
+
const snapshotCount = Number(db.prepare("SELECT COUNT(*) AS count FROM architecture_snapshots WHERE storage_repository_id = ? AND storage_workspace_id = ?").get(input.repository.storageRepositoryId, architectureLedgerWorkspaceKey(input.worktree))?.count ?? 0);
|
|
18271
18553
|
const result = {
|
|
18272
18554
|
ok: failures.length === 0,
|
|
18273
18555
|
graphDigest: replay.materializedDigest,
|
|
@@ -18287,7 +18569,7 @@ class SqliteLocalStore {
|
|
|
18287
18569
|
async backupArchitectureLedger(input) {
|
|
18288
18570
|
const db = await this.database();
|
|
18289
18571
|
ensurePrivateDir2(dirname6(input.backupPath));
|
|
18290
|
-
if (
|
|
18572
|
+
if (existsSync11(input.backupPath))
|
|
18291
18573
|
rmSync5(input.backupPath, { force: true });
|
|
18292
18574
|
db.exec(`VACUUM INTO ${sqliteStringLiteral2(input.backupPath)}`);
|
|
18293
18575
|
return { backupPath: input.backupPath, integrity: assertSqliteIntegrity2(input.backupPath) };
|
|
@@ -18318,9 +18600,80 @@ class SqliteLocalStore {
|
|
|
18318
18600
|
function architectureScopeFromEvent(event) {
|
|
18319
18601
|
return event ? { repository: event.repository, worktree: event.worktree } : undefined;
|
|
18320
18602
|
}
|
|
18603
|
+
function architectureLedgerWorkspaceKey(worktree) {
|
|
18604
|
+
return `ledger-scope:${digestJson({
|
|
18605
|
+
storageWorkspaceId: worktree.storageWorkspaceId,
|
|
18606
|
+
branch: worktree.branch,
|
|
18607
|
+
headSha: worktree.headSha,
|
|
18608
|
+
worktreeDigest: worktree.worktreeDigest
|
|
18609
|
+
}).slice("sha256:".length)}`;
|
|
18610
|
+
}
|
|
18611
|
+
function architectureLedgerStorageId(worktree, logicalId) {
|
|
18612
|
+
return `${architectureLedgerWorkspaceKey(worktree)}:${logicalId}`;
|
|
18613
|
+
}
|
|
18614
|
+
function appendArchitectureEventsInOpenTransaction(db, input) {
|
|
18615
|
+
const startedAt = Date.now();
|
|
18616
|
+
const appendedEvents = [];
|
|
18617
|
+
const duplicateEvents = [];
|
|
18618
|
+
let processed = 0;
|
|
18619
|
+
for (const event of input.events) {
|
|
18620
|
+
const duplicate = architectureEventByIdempotency(db, event);
|
|
18621
|
+
if (duplicate) {
|
|
18622
|
+
const expectedDuplicateHash = normalizeArchitectureLedgerEvent(event, duplicate.previousEventHash).eventHash;
|
|
18623
|
+
if (expectedDuplicateHash !== duplicate.event.eventHash) {
|
|
18624
|
+
throw new Error(`architecture-ledger-idempotency-conflict: ${event.idempotencyKey}`);
|
|
18625
|
+
}
|
|
18626
|
+
duplicateEvents.push(duplicate.event);
|
|
18627
|
+
continue;
|
|
18628
|
+
}
|
|
18629
|
+
const previousEventHash = latestArchitectureEventHash(db, event.repository.storageRepositoryId, architectureLedgerWorkspaceKey(event.worktree));
|
|
18630
|
+
const normalized = normalizeArchitectureLedgerEvent(event, previousEventHash);
|
|
18631
|
+
const operations = architectureLedgerPayload(normalized).operations ?? [];
|
|
18632
|
+
const scope2 = architectureScopeFromEvent(normalized);
|
|
18633
|
+
if (operations.length > 0) {
|
|
18634
|
+
const currentDigest = architectureLedgerStateDigest(readArchitectureLedgerStateFromDb(db, scope2));
|
|
18635
|
+
if (normalized.baseDigest !== currentDigest) {
|
|
18636
|
+
throw new Error(`architecture-ledger-base-digest-conflict: expected ${currentDigest}, received ${normalized.baseDigest}`);
|
|
18637
|
+
}
|
|
18638
|
+
}
|
|
18639
|
+
insertArchitectureEvent(db, normalized);
|
|
18640
|
+
persistArchitectureLedgerArtifacts(db, normalized);
|
|
18641
|
+
materializeArchitectureLedgerEvent(db, normalized);
|
|
18642
|
+
if (operations.length > 0) {
|
|
18643
|
+
const resultingDigest = architectureLedgerStateDigest(readArchitectureLedgerStateFromDb(db, scope2));
|
|
18644
|
+
if (normalized.resultingDigest !== resultingDigest) {
|
|
18645
|
+
throw new Error(`architecture-ledger-resulting-digest-conflict: expected ${resultingDigest}, received ${normalized.resultingDigest}`);
|
|
18646
|
+
}
|
|
18647
|
+
}
|
|
18648
|
+
appendedEvents.push(normalized);
|
|
18649
|
+
processed += 1;
|
|
18650
|
+
if (input.faultAfterEvents !== undefined && processed >= input.faultAfterEvents) {
|
|
18651
|
+
throw new Error("architecture-ledger-fault-injection");
|
|
18652
|
+
}
|
|
18653
|
+
}
|
|
18654
|
+
const scope = architectureScopeFromEvent(input.events[0] ?? duplicateEvents[0]);
|
|
18655
|
+
const state = scope ? readArchitectureLedgerStateFromDb(db, scope) : emptyArchitectureLedgerState();
|
|
18656
|
+
if (scope) {
|
|
18657
|
+
recordArchitectureLedgerOperation(db, {
|
|
18658
|
+
scope,
|
|
18659
|
+
operationKind: "append_events",
|
|
18660
|
+
durationMs: Date.now() - startedAt,
|
|
18661
|
+
rowCount: appendedEvents.length,
|
|
18662
|
+
rebuildReason: null
|
|
18663
|
+
});
|
|
18664
|
+
}
|
|
18665
|
+
return {
|
|
18666
|
+
appendedEvents,
|
|
18667
|
+
duplicateEvents,
|
|
18668
|
+
graphDigest: architectureLedgerStateDigest(state),
|
|
18669
|
+
entityCount: state.entities.length,
|
|
18670
|
+
relationCount: state.relations.length,
|
|
18671
|
+
constraintCount: state.constraints.length
|
|
18672
|
+
};
|
|
18673
|
+
}
|
|
18321
18674
|
function architectureEventByIdempotency(db, event) {
|
|
18322
18675
|
const row = db.prepare(`SELECT event_json, previous_event_hash FROM architecture_events
|
|
18323
|
-
WHERE storage_repository_id = ? AND storage_workspace_id = ? AND idempotency_key = ?`).get(event.repository.storageRepositoryId, event.worktree
|
|
18676
|
+
WHERE storage_repository_id = ? AND storage_workspace_id = ? AND idempotency_key = ?`).get(event.repository.storageRepositoryId, architectureLedgerWorkspaceKey(event.worktree), event.idempotencyKey);
|
|
18324
18677
|
return row ? {
|
|
18325
18678
|
event: JSON.parse(String(row.event_json)),
|
|
18326
18679
|
previousEventHash: row.previous_event_hash === null || row.previous_event_hash === undefined ? null : String(row.previous_event_hash)
|
|
@@ -18336,11 +18689,12 @@ function latestArchitectureEvent(db, storageRepositoryId, storageWorkspaceId) {
|
|
|
18336
18689
|
return row ? { event: JSON.parse(String(row.event_json)), eventHash: String(row.event_hash) } : undefined;
|
|
18337
18690
|
}
|
|
18338
18691
|
function insertArchitectureEvent(db, event) {
|
|
18692
|
+
const workspaceKey = architectureLedgerWorkspaceKey(event.worktree);
|
|
18339
18693
|
db.prepare(`INSERT INTO architecture_events
|
|
18340
18694
|
(event_id, repository_id, storage_repository_id, workspace_id, storage_workspace_id, branch, head_sha, worktree_digest,
|
|
18341
18695
|
event_type, payload_version, source, actor_kind, actor_id, base_digest, resulting_digest, previous_event_hash,
|
|
18342
18696
|
event_hash, idempotency_key, payload_json, provenance_json, event_json, created_at)
|
|
18343
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(event.eventId, event.repository.repositoryId, event.repository.storageRepositoryId, event.worktree.workspaceId,
|
|
18697
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(architectureLedgerStorageId(event.worktree, event.eventId), event.repository.repositoryId, event.repository.storageRepositoryId, event.worktree.workspaceId, workspaceKey, event.worktree.branch, event.worktree.headSha, event.worktree.worktreeDigest, event.eventType, event.payloadVersion, event.source, event.actor.kind, event.actor.id, event.baseDigest, event.resultingDigest, event.previousEventHash ?? null, event.eventHash, event.idempotencyKey, stableJson(event.payload), stableJson(event.provenance), stableJson(event), event.timestamp);
|
|
18344
18698
|
insertArchitectureLedgerFts(db, "event", architectureLedgerPayload(event).summary ?? "", architectureLedgerPayload(event).rationale ?? "", architectureLedgerPayload(event).title ?? "", "");
|
|
18345
18699
|
const payload = architectureLedgerPayload(event);
|
|
18346
18700
|
insertArchitectureLedgerSearchDoc(db, event, {
|
|
@@ -18354,11 +18708,13 @@ function insertArchitectureEvent(db, event) {
|
|
|
18354
18708
|
}
|
|
18355
18709
|
function persistArchitectureLedgerArtifacts(db, event) {
|
|
18356
18710
|
const payload = architectureLedgerPayload(event);
|
|
18711
|
+
const workspaceKey = architectureLedgerWorkspaceKey(event.worktree);
|
|
18712
|
+
const storageEventId = architectureLedgerStorageId(event.worktree, event.eventId);
|
|
18357
18713
|
for (const evidence of payload.evidenceItems ?? []) {
|
|
18358
18714
|
db.prepare(`INSERT OR REPLACE INTO evidence_items
|
|
18359
18715
|
(evidence_id, repository_id, storage_repository_id, workspace_id, storage_workspace_id, event_id, kind, strength,
|
|
18360
18716
|
polarity, origin, subject, selector_json, summary, coverage_json, supports_json, provenance_json, evidence_json, digest, created_at)
|
|
18361
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(evidence.evidenceId, event.repository.repositoryId, event.repository.storageRepositoryId, event.worktree.workspaceId,
|
|
18717
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(architectureLedgerStorageId(event.worktree, evidence.evidenceId), event.repository.repositoryId, event.repository.storageRepositoryId, event.worktree.workspaceId, workspaceKey, storageEventId, evidence.kind, evidence.strength, evidence.polarity, evidence.origin, evidence.subject, stableJson(evidence.selector), evidence.summary, stableJson(evidence.coverage), stableJson(evidence.supports), stableJson(evidence.provenance), stableJson(evidence), evidence.digest, evidence.createdAt);
|
|
18362
18718
|
insertArchitectureLedgerFts(db, "evidence", evidence.summary, "", "", evidence.summary);
|
|
18363
18719
|
insertArchitectureLedgerSearchDoc(db, event, {
|
|
18364
18720
|
docId: `evidence:${evidence.evidenceId}`,
|
|
@@ -18373,7 +18729,7 @@ function persistArchitectureLedgerArtifacts(db, event) {
|
|
|
18373
18729
|
db.prepare(`INSERT OR REPLACE INTO evidence_bindings
|
|
18374
18730
|
(binding_id, evidence_id, repository_id, storage_repository_id, workspace_id, storage_workspace_id, event_id,
|
|
18375
18731
|
target_kind, target_id, binding_reason, authority_effect, provenance_json, binding_json, created_at)
|
|
18376
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(binding.bindingId, binding.evidenceId, event.repository.repositoryId, event.repository.storageRepositoryId, event.worktree.workspaceId,
|
|
18732
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(architectureLedgerStorageId(event.worktree, binding.bindingId), architectureLedgerStorageId(event.worktree, binding.evidenceId), event.repository.repositoryId, event.repository.storageRepositoryId, event.worktree.workspaceId, workspaceKey, storageEventId, binding.target.kind, binding.target.id, binding.bindingReason, binding.authorityEffect, stableJson(binding.provenance), stableJson(binding), binding.createdAt);
|
|
18377
18733
|
}
|
|
18378
18734
|
for (const run of payload.recommendationRuns ?? [])
|
|
18379
18735
|
persistRecommendationRun(db, event, run);
|
|
@@ -18393,17 +18749,19 @@ function persistArchitectureLedgerArtifacts(db, event) {
|
|
|
18393
18749
|
persistProjectionState(db, event, payload.projectionState);
|
|
18394
18750
|
}
|
|
18395
18751
|
function persistRecommendationRun(db, event, run) {
|
|
18752
|
+
const workspaceKey = architectureLedgerWorkspaceKey(event.worktree);
|
|
18396
18753
|
db.prepare(`INSERT OR REPLACE INTO recommendation_runs
|
|
18397
18754
|
(run_id, repository_id, storage_repository_id, workspace_id, storage_workspace_id, event_id, status, catalog_digest,
|
|
18398
18755
|
input_digest, output_digest, metrics_json, run_json, started_at, completed_at)
|
|
18399
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(run.runId, event.repository.repositoryId, event.repository.storageRepositoryId, event.worktree.workspaceId, event.worktree
|
|
18756
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(architectureLedgerStorageId(event.worktree, run.runId), event.repository.repositoryId, event.repository.storageRepositoryId, event.worktree.workspaceId, workspaceKey, architectureLedgerStorageId(event.worktree, event.eventId), run.status, run.catalogDigest, run.inputDigest, run.outputDigest, stableJson(run.metrics), stableJson(run), run.startedAt, run.completedAt ?? null);
|
|
18400
18757
|
}
|
|
18401
18758
|
function persistRecommendation(db, event, recommendation) {
|
|
18759
|
+
const workspaceKey = architectureLedgerWorkspaceKey(event.worktree);
|
|
18402
18760
|
db.prepare(`INSERT OR REPLACE INTO recommendations
|
|
18403
18761
|
(recommendation_id, run_id, repository_id, storage_repository_id, workspace_id, storage_workspace_id, event_id, fingerprint,
|
|
18404
18762
|
subject, practice_id, status, confidence, enforcement, risk, uncertainty, evidence_binding_ids_json, explanation_json,
|
|
18405
18763
|
recommendation_json, created_at, updated_at)
|
|
18406
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(recommendation.recommendationId, recommendation.runId, event.repository.repositoryId, event.repository.storageRepositoryId, event.worktree.workspaceId, event.worktree
|
|
18764
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(architectureLedgerStorageId(event.worktree, recommendation.recommendationId), architectureLedgerStorageId(event.worktree, recommendation.runId), event.repository.repositoryId, event.repository.storageRepositoryId, event.worktree.workspaceId, workspaceKey, architectureLedgerStorageId(event.worktree, event.eventId), recommendation.fingerprint, recommendation.subject, recommendation.practiceId ?? null, recommendation.status, recommendation.confidence, recommendation.enforcement, recommendation.risk, recommendation.uncertainty, stableJson(recommendation.evidenceBindingIds), stableJson(recommendation.explanation), stableJson(recommendation), recommendation.createdAt, recommendation.updatedAt);
|
|
18407
18765
|
insertArchitectureLedgerFts(db, "recommendation", recommendation.explanation.join(`
|
|
18408
18766
|
`), "", recommendation.subject, recommendation.explanation.join(`
|
|
18409
18767
|
`));
|
|
@@ -18420,44 +18778,51 @@ function persistRecommendation(db, event, recommendation) {
|
|
|
18420
18778
|
});
|
|
18421
18779
|
}
|
|
18422
18780
|
function persistAgentJob(db, event, job) {
|
|
18781
|
+
const workspaceKey = architectureLedgerWorkspaceKey(event.worktree);
|
|
18423
18782
|
db.prepare(`INSERT OR REPLACE INTO agent_jobs
|
|
18424
18783
|
(job_id, repository_id, storage_repository_id, workspace_id, storage_workspace_id, event_id, status, runner_port,
|
|
18425
18784
|
fingerprint, input_digest, output_digest, stale_policy, job_json, queued_at, updated_at)
|
|
18426
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(job.jobId, event.repository.repositoryId, event.repository.storageRepositoryId, event.worktree.workspaceId, event.worktree
|
|
18785
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(architectureLedgerStorageId(event.worktree, job.jobId), event.repository.repositoryId, event.repository.storageRepositoryId, event.worktree.workspaceId, workspaceKey, architectureLedgerStorageId(event.worktree, event.eventId), job.status, job.runnerPort, job.fingerprint, job.inputDigest, job.outputDigest ?? null, job.stalePolicy, stableJson(job), job.queuedAt, job.updatedAt);
|
|
18427
18786
|
}
|
|
18428
18787
|
function persistAuditRun(db, event, run) {
|
|
18788
|
+
const workspaceKey = architectureLedgerWorkspaceKey(event.worktree);
|
|
18429
18789
|
db.prepare(`INSERT OR REPLACE INTO audit_runs
|
|
18430
18790
|
(run_id, repository_id, storage_repository_id, workspace_id, storage_workspace_id, event_id, job_id, report_id, status,
|
|
18431
18791
|
repo_name_with_owner, repo_visibility, base_sha, input_digest, output_digest, run_json, created_at)
|
|
18432
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(run.runId, event.repository.repositoryId, event.repository.storageRepositoryId, event.worktree.workspaceId, event.worktree
|
|
18792
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(architectureLedgerStorageId(event.worktree, run.runId), event.repository.repositoryId, event.repository.storageRepositoryId, event.worktree.workspaceId, workspaceKey, architectureLedgerStorageId(event.worktree, event.eventId), run.jobId, run.reportId, run.status, run.repoNameWithOwner, run.repoVisibility, run.baseSha, run.inputDigest, run.outputDigest, stableJson(run), run.createdAt);
|
|
18433
18793
|
}
|
|
18434
18794
|
function persistProjectionState(db, event, state) {
|
|
18435
18795
|
const path = String(state.path ?? "projection");
|
|
18436
18796
|
const projectionDigest = typeof state.projectionDigest === "string" ? state.projectionDigest : digestJson(state);
|
|
18797
|
+
const workspaceKey = architectureLedgerWorkspaceKey(event.worktree);
|
|
18437
18798
|
db.prepare(`INSERT OR REPLACE INTO projection_state
|
|
18438
18799
|
(projection_id, repository_id, storage_repository_id, workspace_id, storage_workspace_id, event_id, path, projection_digest, state_json, updated_at)
|
|
18439
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(String(state.projectionId ?? stableLedgerId("projection", event.eventId, path)), event.repository.repositoryId, event.repository.storageRepositoryId, event.worktree.workspaceId, event.worktree
|
|
18800
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(architectureLedgerStorageId(event.worktree, String(state.projectionId ?? stableLedgerId("projection", event.eventId, path))), event.repository.repositoryId, event.repository.storageRepositoryId, event.worktree.workspaceId, workspaceKey, architectureLedgerStorageId(event.worktree, event.eventId), path, projectionDigest, stableJson(state), event.timestamp);
|
|
18440
18801
|
}
|
|
18441
18802
|
function persistSourceCursor(db, event, cursor) {
|
|
18442
18803
|
const source = String(cursor.source ?? event.source);
|
|
18804
|
+
const workspaceKey = architectureLedgerWorkspaceKey(event.worktree);
|
|
18443
18805
|
db.prepare(`INSERT OR REPLACE INTO source_cursors
|
|
18444
18806
|
(cursor_id, repository_id, storage_repository_id, workspace_id, storage_workspace_id, source, cursor_json, updated_at)
|
|
18445
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`).run(String(cursor.cursorId ?? stableLedgerId("cursor", event.eventId, source)), event.repository.repositoryId, event.repository.storageRepositoryId, event.worktree.workspaceId,
|
|
18807
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`).run(architectureLedgerStorageId(event.worktree, String(cursor.cursorId ?? stableLedgerId("cursor", event.eventId, source))), event.repository.repositoryId, event.repository.storageRepositoryId, event.worktree.workspaceId, workspaceKey, source, stableJson(cursor), event.timestamp);
|
|
18446
18808
|
}
|
|
18447
18809
|
function persistGenericLedgerJson(db, event, table, idColumn, jsonColumn, value, prefix) {
|
|
18448
18810
|
const id = String(value[idColumn] ?? value.id ?? stableLedgerId(prefix, event.eventId, stableJson(value)));
|
|
18811
|
+
const storageId = architectureLedgerStorageId(event.worktree, id);
|
|
18812
|
+
const workspaceKey = architectureLedgerWorkspaceKey(event.worktree);
|
|
18449
18813
|
if (table === "recommendation_feedback") {
|
|
18450
18814
|
db.prepare(`INSERT OR REPLACE INTO recommendation_feedback
|
|
18451
18815
|
(feedback_id, recommendation_id, repository_id, storage_repository_id, workspace_id, storage_workspace_id, event_id, feedback_json, created_at)
|
|
18452
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(
|
|
18816
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(storageId, architectureLedgerStorageId(event.worktree, String(value.recommendationId ?? value.recommendation_id ?? "unknown")), event.repository.repositoryId, event.repository.storageRepositoryId, event.worktree.workspaceId, workspaceKey, architectureLedgerStorageId(event.worktree, event.eventId), stableJson(value), String(value.createdAt ?? event.timestamp));
|
|
18453
18817
|
return;
|
|
18454
18818
|
}
|
|
18455
18819
|
db.prepare(`INSERT OR REPLACE INTO waivers
|
|
18456
18820
|
(waiver_id, repository_id, storage_repository_id, workspace_id, storage_workspace_id, event_id, target_kind, target_id, waiver_json, created_at, expires_at)
|
|
18457
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(
|
|
18821
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(storageId, event.repository.repositoryId, event.repository.storageRepositoryId, event.worktree.workspaceId, workspaceKey, architectureLedgerStorageId(event.worktree, event.eventId), String(value.targetKind ?? value.target_kind ?? "unknown"), String(value.targetId ?? value.target_id ?? "unknown"), stableJson(value), String(value.createdAt ?? event.timestamp), value.expiresAt ? String(value.expiresAt) : null);
|
|
18458
18822
|
}
|
|
18459
18823
|
function materializeArchitectureLedgerEvent(db, event) {
|
|
18460
18824
|
const payload = architectureLedgerPayload(event);
|
|
18825
|
+
const workspaceKey = architectureLedgerWorkspaceKey(event.worktree);
|
|
18461
18826
|
for (const operation of payload.operations ?? []) {
|
|
18462
18827
|
switch (operation.op) {
|
|
18463
18828
|
case "upsert_entity":
|
|
@@ -18465,31 +18830,32 @@ function materializeArchitectureLedgerEvent(db, event) {
|
|
|
18465
18830
|
break;
|
|
18466
18831
|
case "delete_entity":
|
|
18467
18832
|
deleteArchitectureLedgerSearchDocs(db, { repository: event.repository, worktree: event.worktree }, [operation.entityId]);
|
|
18468
|
-
db.prepare("DELETE FROM architecture_entities_current WHERE storage_repository_id = ? AND storage_workspace_id = ? AND entity_id = ?").run(event.repository.storageRepositoryId,
|
|
18469
|
-
db.prepare("DELETE FROM architecture_relations_current WHERE storage_repository_id = ? AND storage_workspace_id = ? AND (source_entity_id = ? OR target_entity_id = ?)").run(event.repository.storageRepositoryId,
|
|
18833
|
+
db.prepare("DELETE FROM architecture_entities_current WHERE storage_repository_id = ? AND storage_workspace_id = ? AND entity_id = ?").run(event.repository.storageRepositoryId, workspaceKey, operation.entityId);
|
|
18834
|
+
db.prepare("DELETE FROM architecture_relations_current WHERE storage_repository_id = ? AND storage_workspace_id = ? AND (source_entity_id = ? OR target_entity_id = ?)").run(event.repository.storageRepositoryId, workspaceKey, operation.entityId, operation.entityId);
|
|
18470
18835
|
break;
|
|
18471
18836
|
case "upsert_relation":
|
|
18472
18837
|
upsertArchitectureRelation(db, event, operation.relation);
|
|
18473
18838
|
break;
|
|
18474
18839
|
case "delete_relation":
|
|
18475
18840
|
deleteArchitectureLedgerSearchDocs(db, { repository: event.repository, worktree: event.worktree }, [operation.relationId]);
|
|
18476
|
-
db.prepare("DELETE FROM architecture_relations_current WHERE storage_repository_id = ? AND storage_workspace_id = ? AND relation_id = ?").run(event.repository.storageRepositoryId,
|
|
18841
|
+
db.prepare("DELETE FROM architecture_relations_current WHERE storage_repository_id = ? AND storage_workspace_id = ? AND relation_id = ?").run(event.repository.storageRepositoryId, workspaceKey, operation.relationId);
|
|
18477
18842
|
break;
|
|
18478
18843
|
case "upsert_constraint":
|
|
18479
18844
|
upsertArchitectureConstraint(db, event, operation.constraint);
|
|
18480
18845
|
break;
|
|
18481
18846
|
case "delete_constraint":
|
|
18482
18847
|
deleteArchitectureLedgerSearchDocs(db, { repository: event.repository, worktree: event.worktree }, [operation.constraintId]);
|
|
18483
|
-
db.prepare("DELETE FROM architecture_constraints_current WHERE storage_repository_id = ? AND storage_workspace_id = ? AND constraint_id = ?").run(event.repository.storageRepositoryId,
|
|
18848
|
+
db.prepare("DELETE FROM architecture_constraints_current WHERE storage_repository_id = ? AND storage_workspace_id = ? AND constraint_id = ?").run(event.repository.storageRepositoryId, workspaceKey, operation.constraintId);
|
|
18484
18849
|
break;
|
|
18485
18850
|
}
|
|
18486
18851
|
}
|
|
18487
18852
|
}
|
|
18488
18853
|
function upsertArchitectureEntity(db, event, entity) {
|
|
18854
|
+
const workspaceKey = architectureLedgerWorkspaceKey(event.worktree);
|
|
18489
18855
|
db.prepare(`INSERT OR REPLACE INTO architecture_entities_current
|
|
18490
18856
|
(storage_repository_id, storage_workspace_id, entity_id, repository_id, workspace_id, branch, head_sha, worktree_digest,
|
|
18491
18857
|
kind, canonical_name, status, path, summary, metadata_json, last_event_id, updated_at)
|
|
18492
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(event.repository.storageRepositoryId,
|
|
18858
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(event.repository.storageRepositoryId, workspaceKey, entity.entityId, event.repository.repositoryId, event.worktree.workspaceId, event.worktree.branch, event.worktree.headSha, event.worktree.worktreeDigest, entity.kind, entity.canonicalName, entity.status, entity.path ?? null, entity.summary ?? null, stableJson(entity.metadata ?? {}), architectureLedgerStorageId(event.worktree, event.eventId), event.timestamp);
|
|
18493
18859
|
insertArchitectureLedgerFts(db, "entity", entity.summary ?? "", "", entity.canonicalName, "");
|
|
18494
18860
|
insertArchitectureLedgerSearchDoc(db, event, {
|
|
18495
18861
|
docId: `entity:${entity.entityId}`,
|
|
@@ -18502,10 +18868,11 @@ function upsertArchitectureEntity(db, event, entity) {
|
|
|
18502
18868
|
});
|
|
18503
18869
|
}
|
|
18504
18870
|
function upsertArchitectureRelation(db, event, relation) {
|
|
18871
|
+
const workspaceKey = architectureLedgerWorkspaceKey(event.worktree);
|
|
18505
18872
|
db.prepare(`INSERT OR REPLACE INTO architecture_relations_current
|
|
18506
18873
|
(storage_repository_id, storage_workspace_id, relation_id, repository_id, workspace_id, branch, head_sha, worktree_digest,
|
|
18507
18874
|
kind, source_entity_id, target_entity_id, status, summary, metadata_json, last_event_id, updated_at)
|
|
18508
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(event.repository.storageRepositoryId,
|
|
18875
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(event.repository.storageRepositoryId, workspaceKey, relation.relationId, event.repository.repositoryId, event.worktree.workspaceId, event.worktree.branch, event.worktree.headSha, event.worktree.worktreeDigest, relation.kind, relation.sourceEntityId, relation.targetEntityId, relation.status, relation.summary ?? null, stableJson(relation.metadata ?? {}), architectureLedgerStorageId(event.worktree, event.eventId), event.timestamp);
|
|
18509
18876
|
insertArchitectureLedgerFts(db, "relation", relation.summary ?? "", "", relation.relationId, "");
|
|
18510
18877
|
insertArchitectureLedgerSearchDoc(db, event, {
|
|
18511
18878
|
docId: `relation:${relation.relationId}`,
|
|
@@ -18518,10 +18885,11 @@ function upsertArchitectureRelation(db, event, relation) {
|
|
|
18518
18885
|
});
|
|
18519
18886
|
}
|
|
18520
18887
|
function upsertArchitectureConstraint(db, event, constraint) {
|
|
18888
|
+
const workspaceKey = architectureLedgerWorkspaceKey(event.worktree);
|
|
18521
18889
|
db.prepare(`INSERT OR REPLACE INTO architecture_constraints_current
|
|
18522
18890
|
(storage_repository_id, storage_workspace_id, constraint_id, repository_id, workspace_id, branch, head_sha, worktree_digest,
|
|
18523
18891
|
kind, subject_id, status, severity, summary, metadata_json, last_event_id, updated_at)
|
|
18524
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(event.repository.storageRepositoryId,
|
|
18892
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(event.repository.storageRepositoryId, workspaceKey, constraint.constraintId, event.repository.repositoryId, event.worktree.workspaceId, event.worktree.branch, event.worktree.headSha, event.worktree.worktreeDigest, constraint.kind, constraint.subjectId, constraint.status, constraint.severity ?? null, constraint.summary ?? null, stableJson(constraint.metadata ?? {}), architectureLedgerStorageId(event.worktree, event.eventId), event.timestamp);
|
|
18525
18893
|
insertArchitectureLedgerFts(db, "constraint", constraint.summary ?? "", "", constraint.constraintId, "");
|
|
18526
18894
|
insertArchitectureLedgerSearchDoc(db, event, {
|
|
18527
18895
|
docId: `constraint:${constraint.constraintId}`,
|
|
@@ -18534,7 +18902,7 @@ function upsertArchitectureConstraint(db, event, constraint) {
|
|
|
18534
18902
|
});
|
|
18535
18903
|
}
|
|
18536
18904
|
function readArchitectureLedgerStateFromDb(db, scope) {
|
|
18537
|
-
const scopeParams = [scope.repository.storageRepositoryId, scope.worktree
|
|
18905
|
+
const scopeParams = [scope.repository.storageRepositoryId, architectureLedgerWorkspaceKey(scope.worktree)];
|
|
18538
18906
|
const entities = db.prepare(`SELECT entity_id, kind, canonical_name, status, path, summary, metadata_json
|
|
18539
18907
|
FROM architecture_entities_current
|
|
18540
18908
|
WHERE storage_repository_id = ? AND storage_workspace_id = ?
|
|
@@ -18575,7 +18943,7 @@ function readArchitectureLedgerStateFromDb(db, scope) {
|
|
|
18575
18943
|
}
|
|
18576
18944
|
function readArchitectureLedgerNeighborhoodFromDb(db, input) {
|
|
18577
18945
|
const depth = Math.max(0, Math.floor(input.depth));
|
|
18578
|
-
const scopeParams = [input.repository.storageRepositoryId, input.worktree
|
|
18946
|
+
const scopeParams = [input.repository.storageRepositoryId, architectureLedgerWorkspaceKey(input.worktree)];
|
|
18579
18947
|
const entityIds = [...new Set(db.prepare(`WITH RECURSIVE seed(entity_id) AS (
|
|
18580
18948
|
SELECT entity_id FROM architecture_entities_current
|
|
18581
18949
|
WHERE storage_repository_id = ? AND storage_workspace_id = ? AND entity_id = ?
|
|
@@ -18656,42 +19024,43 @@ function architectureEventsForReplay(db, input) {
|
|
|
18656
19024
|
let untilEventId = input.untilEventId;
|
|
18657
19025
|
if (input.snapshotId) {
|
|
18658
19026
|
const snapshot = db.prepare(`SELECT last_event_id FROM architecture_snapshots
|
|
18659
|
-
WHERE snapshot_id = ? AND storage_repository_id = ? AND storage_workspace_id = ?`).get(input.snapshotId, input.repository.storageRepositoryId, input.worktree
|
|
19027
|
+
WHERE snapshot_id = ? AND storage_repository_id = ? AND storage_workspace_id = ?`).get(input.snapshotId, input.repository.storageRepositoryId, architectureLedgerWorkspaceKey(input.worktree));
|
|
18660
19028
|
if (!snapshot)
|
|
18661
19029
|
throw new Error(`architecture-ledger-snapshot-not-found: ${input.snapshotId}`);
|
|
18662
19030
|
untilEventId = String(snapshot.last_event_id);
|
|
18663
19031
|
}
|
|
18664
19032
|
const rows = db.prepare(`SELECT event_id, event_json FROM architecture_events
|
|
18665
19033
|
WHERE storage_repository_id = ? AND storage_workspace_id = ?
|
|
18666
|
-
ORDER BY event_sequence`).all(input.repository.storageRepositoryId, input.worktree
|
|
19034
|
+
ORDER BY event_sequence`).all(input.repository.storageRepositoryId, architectureLedgerWorkspaceKey(input.worktree));
|
|
18667
19035
|
const events = [];
|
|
18668
19036
|
for (const row of rows) {
|
|
18669
19037
|
const event = JSON.parse(String(row.event_json));
|
|
18670
19038
|
events.push(event);
|
|
18671
|
-
if (untilEventId && String(row.event_id) === untilEventId)
|
|
19039
|
+
if (untilEventId && (String(row.event_id) === untilEventId || event.eventId === untilEventId))
|
|
18672
19040
|
break;
|
|
18673
19041
|
}
|
|
18674
19042
|
return events;
|
|
18675
19043
|
}
|
|
18676
19044
|
function deleteArchitectureCurrentState(db, scope) {
|
|
18677
19045
|
for (const table of ["architecture_entities_current", "architecture_relations_current", "architecture_constraints_current"]) {
|
|
18678
|
-
db.prepare(`DELETE FROM ${table} WHERE storage_repository_id = ? AND storage_workspace_id = ?`).run(scope.repository.storageRepositoryId, scope.worktree
|
|
19046
|
+
db.prepare(`DELETE FROM ${table} WHERE storage_repository_id = ? AND storage_workspace_id = ?`).run(scope.repository.storageRepositoryId, architectureLedgerWorkspaceKey(scope.worktree));
|
|
18679
19047
|
}
|
|
18680
19048
|
}
|
|
18681
19049
|
function insertArchitectureLedgerFts(db, kind, summary, rationale, title, evidenceSummary) {
|
|
18682
19050
|
db.prepare("INSERT INTO architecture_ledger_fts(kind, summary, rationale, title, evidence_summary) VALUES (?, ?, ?, ?, ?)").run(kind, summary, rationale, title, evidenceSummary);
|
|
18683
19051
|
}
|
|
18684
19052
|
function insertArchitectureLedgerSearchDoc(db, event, doc) {
|
|
19053
|
+
const workspaceKey = architectureLedgerWorkspaceKey(event.worktree);
|
|
18685
19054
|
db.prepare(`DELETE FROM architecture_ledger_search_fts
|
|
18686
|
-
WHERE storage_repository_id = ? AND storage_workspace_id = ? AND doc_id = ?`).run(event.repository.storageRepositoryId,
|
|
19055
|
+
WHERE storage_repository_id = ? AND storage_workspace_id = ? AND doc_id = ?`).run(event.repository.storageRepositoryId, workspaceKey, doc.docId);
|
|
18687
19056
|
db.prepare(`INSERT INTO architecture_ledger_search_fts
|
|
18688
19057
|
(doc_id, storage_repository_id, storage_workspace_id, target_kind, target_id, subject_id, title, summary, rationale, evidence_summary)
|
|
18689
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(doc.docId, event.repository.storageRepositoryId,
|
|
19058
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(doc.docId, event.repository.storageRepositoryId, workspaceKey, doc.targetKind, doc.targetId, doc.subjectId ?? null, doc.title ?? "", doc.summary ?? "", doc.rationale ?? "", doc.evidenceSummary ?? "");
|
|
18690
19059
|
}
|
|
18691
19060
|
function deleteArchitectureLedgerSearchDocs(db, scope, ids) {
|
|
18692
19061
|
for (const id of ids) {
|
|
18693
19062
|
db.prepare(`DELETE FROM architecture_ledger_search_fts
|
|
18694
|
-
WHERE storage_repository_id = ? AND storage_workspace_id = ? AND (target_id = ? OR subject_id = ?)`).run(scope.repository.storageRepositoryId, scope.worktree
|
|
19063
|
+
WHERE storage_repository_id = ? AND storage_workspace_id = ? AND (target_id = ? OR subject_id = ?)`).run(scope.repository.storageRepositoryId, architectureLedgerWorkspaceKey(scope.worktree), id, id);
|
|
18695
19064
|
}
|
|
18696
19065
|
}
|
|
18697
19066
|
function queryArchitectureLedgerSearchFts(db, input) {
|
|
@@ -18705,7 +19074,7 @@ function queryArchitectureLedgerSearchFts(db, input) {
|
|
|
18705
19074
|
WHERE storage_repository_id = ? AND storage_workspace_id = ?
|
|
18706
19075
|
AND architecture_ledger_search_fts MATCH ?
|
|
18707
19076
|
ORDER BY rank, target_kind, target_id
|
|
18708
|
-
LIMIT ?`).all(input.repository.storageRepositoryId, input.worktree
|
|
19077
|
+
LIMIT ?`).all(input.repository.storageRepositoryId, architectureLedgerWorkspaceKey(input.worktree), matchQuery, limit);
|
|
18709
19078
|
return rows.map((row) => {
|
|
18710
19079
|
const title = String(row.title ?? "");
|
|
18711
19080
|
const summary = String(row.summary ?? "");
|
|
@@ -18749,7 +19118,7 @@ function ftsMatchKind(query, doc) {
|
|
|
18749
19118
|
function recordArchitectureLedgerOperation(db, input) {
|
|
18750
19119
|
db.prepare(`INSERT INTO architecture_ledger_operations
|
|
18751
19120
|
(operation_id, storage_repository_id, storage_workspace_id, operation_kind, duration_ms, row_count, rebuild_reason, created_at)
|
|
18752
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`).run(`ledger_operation_${randomUUID2()}`, input.scope.repository.storageRepositoryId, input.scope.worktree
|
|
19121
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`).run(`ledger_operation_${randomUUID2()}`, input.scope.repository.storageRepositoryId, architectureLedgerWorkspaceKey(input.scope.worktree), input.operationKind, Math.max(0, Math.trunc(input.durationMs)), input.rowCount, input.rebuildReason, nowIso2());
|
|
18753
19122
|
}
|
|
18754
19123
|
function stableLedgerId(prefix, ...parts) {
|
|
18755
19124
|
return `${prefix}.${createHash6("sha256").update(parts.join("\x00")).digest("hex").slice(0, 24)}`;
|
|
@@ -18932,7 +19301,7 @@ function assertTrustedLegacyLocalStoreSource2(paths) {
|
|
|
18932
19301
|
if (!stat.isFile()) {
|
|
18933
19302
|
throw new Error(`Legacy SQLite source must be a regular file: ${paths.legacyLocalStorePath}`);
|
|
18934
19303
|
}
|
|
18935
|
-
const sourceRealPath =
|
|
19304
|
+
const sourceRealPath = realpathSync7.native(paths.legacyLocalStorePath);
|
|
18936
19305
|
if (!isPathInsideOrSame2(sourceRealPath, paths.repositoryRoot)) {
|
|
18937
19306
|
throw new Error(`Legacy SQLite source must stay inside the repository root: ${paths.legacyLocalStorePath}`);
|
|
18938
19307
|
}
|
|
@@ -18986,7 +19355,7 @@ function sqliteStringLiteral2(value) {
|
|
|
18986
19355
|
function publishStagedLocalStore2(stagingPath, targetPath) {
|
|
18987
19356
|
for (const suffix of SQLITE_SIDECAR_SUFFIXES2) {
|
|
18988
19357
|
const path = `${targetPath}${suffix}`;
|
|
18989
|
-
if (
|
|
19358
|
+
if (existsSync11(path))
|
|
18990
19359
|
throw new Error(`Cannot publish migrated SQLite over existing target file: ${path}`);
|
|
18991
19360
|
}
|
|
18992
19361
|
renameSync3(stagingPath, targetPath);
|
|
@@ -19058,14 +19427,14 @@ function quarantineExistingLocalStore2(paths) {
|
|
|
19058
19427
|
const quarantinedFiles = [];
|
|
19059
19428
|
for (const suffix of SQLITE_SIDECAR_SUFFIXES2) {
|
|
19060
19429
|
const source = `${paths.localStorePath}${suffix}`;
|
|
19061
|
-
if (!
|
|
19430
|
+
if (!existsSync11(source))
|
|
19062
19431
|
continue;
|
|
19063
19432
|
const target = join6(quarantineDir, `runtime.sqlite${suffix}`);
|
|
19064
19433
|
renameSync3(source, target);
|
|
19065
19434
|
quarantinedFiles.push(target);
|
|
19066
19435
|
}
|
|
19067
19436
|
const markerPath = legacyMigrationMarkerPath2(paths);
|
|
19068
|
-
if (
|
|
19437
|
+
if (existsSync11(markerPath)) {
|
|
19069
19438
|
const target = join6(quarantineDir, LEGACY_MIGRATION_MARKER_FILE2);
|
|
19070
19439
|
renameSync3(markerPath, target);
|
|
19071
19440
|
quarantinedFiles.push(target);
|
|
@@ -19125,7 +19494,7 @@ function resolveMaybeRelative2(base, path) {
|
|
|
19125
19494
|
function canonicalPath2(path) {
|
|
19126
19495
|
const resolved = resolve13(path);
|
|
19127
19496
|
try {
|
|
19128
|
-
return
|
|
19497
|
+
return realpathSync7.native(resolved);
|
|
19129
19498
|
} catch {
|
|
19130
19499
|
return resolved;
|
|
19131
19500
|
}
|
|
@@ -19315,18 +19684,22 @@ function changeSetLedgerAppendSummary(result) {
|
|
|
19315
19684
|
}
|
|
19316
19685
|
function changeSetJournalPlannedLedgerEvent(metadata) {
|
|
19317
19686
|
const architectureLedger = metadata.architectureLedger;
|
|
19318
|
-
if (
|
|
19687
|
+
if (architectureLedger === undefined)
|
|
19319
19688
|
return;
|
|
19689
|
+
if (!isJsonRecord(architectureLedger))
|
|
19690
|
+
throw new Error("changeset-ledger-recovery-metadata-malformed");
|
|
19320
19691
|
const plannedEvent = architectureLedger.plannedEvent;
|
|
19321
|
-
if (
|
|
19692
|
+
if (plannedEvent === undefined)
|
|
19322
19693
|
return;
|
|
19694
|
+
if (!isJsonRecord(plannedEvent))
|
|
19695
|
+
throw new Error("changeset-ledger-recovery-planned-event-malformed");
|
|
19696
|
+
const event = plannedEvent;
|
|
19323
19697
|
try {
|
|
19324
|
-
const event = plannedEvent;
|
|
19325
19698
|
validateArchitectureLedgerEvent(event);
|
|
19326
|
-
|
|
19327
|
-
|
|
19328
|
-
return;
|
|
19699
|
+
} catch (error) {
|
|
19700
|
+
throw new Error(`changeset-ledger-recovery-planned-event-invalid: ${error instanceof Error ? error.message : String(error)}`);
|
|
19329
19701
|
}
|
|
19702
|
+
return event;
|
|
19330
19703
|
}
|
|
19331
19704
|
function isJsonRecord(value) {
|
|
19332
19705
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -19336,9 +19709,13 @@ function recoverJournalFiles(root, files) {
|
|
|
19336
19709
|
const absolute = resolve13(root, file.path);
|
|
19337
19710
|
if (file.tempPath)
|
|
19338
19711
|
rmSync5(file.tempPath, { recursive: true, force: true });
|
|
19339
|
-
|
|
19340
|
-
|
|
19341
|
-
|
|
19712
|
+
if (file.existed) {
|
|
19713
|
+
if (file.backupPath && existsSync11(file.backupPath)) {
|
|
19714
|
+
rmSync5(absolute, { recursive: true, force: true });
|
|
19715
|
+
renameSync3(file.backupPath, absolute);
|
|
19716
|
+
}
|
|
19717
|
+
} else {
|
|
19718
|
+
rmSync5(absolute, { recursive: true, force: true });
|
|
19342
19719
|
}
|
|
19343
19720
|
fsyncDirectory3(dirname6(absolute));
|
|
19344
19721
|
}
|
|
@@ -19395,8 +19772,18 @@ async function readRelationFiles(root, relationsDir) {
|
|
|
19395
19772
|
|
|
19396
19773
|
// packages/local-runtime/model-store-yaml/src/index.ts
|
|
19397
19774
|
init_src();
|
|
19398
|
-
import { existsSync as
|
|
19775
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync6, readdirSync as readdirSync7, readFileSync as readFileSync10, rmSync as rmSync6, writeFileSync as writeFileSync4 } from "node:fs";
|
|
19399
19776
|
import { dirname as dirname7, resolve as resolve14 } from "node:path";
|
|
19777
|
+
var GENERATED_ARCHITECTURE_PATH = ".archcontext/generated/ARCHITECTURE.md";
|
|
19778
|
+
var GENERATED_ARCHITECTURE_BODY = [
|
|
19779
|
+
"<!-- Generated by ArchContext. Do not edit by hand. -->",
|
|
19780
|
+
"",
|
|
19781
|
+
"# Architecture",
|
|
19782
|
+
"",
|
|
19783
|
+
"Model projection is generated from `.archcontext/model`.",
|
|
19784
|
+
""
|
|
19785
|
+
].join(`
|
|
19786
|
+
`);
|
|
19400
19787
|
function createDefaultManifest(productId, productName) {
|
|
19401
19788
|
return {
|
|
19402
19789
|
schemaVersion: "archcontext.manifest/v1",
|
|
@@ -19494,6 +19881,22 @@ function createDefaultProjectionTargetManifest() {
|
|
|
19494
19881
|
pathTemplate: "docs/architecture/diagrams/architecture.likec4",
|
|
19495
19882
|
ownership: "generated",
|
|
19496
19883
|
format: "likec4"
|
|
19884
|
+
},
|
|
19885
|
+
{
|
|
19886
|
+
id: "projection_rule.agent-context.claude",
|
|
19887
|
+
targetType: "agent-context",
|
|
19888
|
+
scope: { kind: "entity", entityKind: "capability" },
|
|
19889
|
+
pathTemplate: "{primarySourceDir}/CLAUDE.md",
|
|
19890
|
+
ownership: "mixed",
|
|
19891
|
+
format: "markdown"
|
|
19892
|
+
},
|
|
19893
|
+
{
|
|
19894
|
+
id: "projection_rule.agent-context.agents",
|
|
19895
|
+
targetType: "agent-context",
|
|
19896
|
+
scope: { kind: "entity", entityKind: "capability" },
|
|
19897
|
+
pathTemplate: "{primarySourceDir}/AGENTS.md",
|
|
19898
|
+
ownership: "mixed",
|
|
19899
|
+
format: "markdown"
|
|
19497
19900
|
}
|
|
19498
19901
|
]
|
|
19499
19902
|
};
|
|
@@ -19530,16 +19933,50 @@ function initializeArchContextModel(root, productName = "ArchContext Project") {
|
|
|
19530
19933
|
rebuildGeneratedProjection(root);
|
|
19531
19934
|
}
|
|
19532
19935
|
function rebuildGeneratedProjection(root) {
|
|
19533
|
-
|
|
19534
|
-
|
|
19535
|
-
|
|
19536
|
-
|
|
19537
|
-
|
|
19538
|
-
|
|
19539
|
-
|
|
19540
|
-
|
|
19541
|
-
|
|
19542
|
-
|
|
19936
|
+
for (const operation of planGeneratedProjection(root)) {
|
|
19937
|
+
if (operation.operation === "delete_entity") {
|
|
19938
|
+
rmSync6(resolve14(root, operation.path), { force: true });
|
|
19939
|
+
} else {
|
|
19940
|
+
writeFile(root, operation.path, operation.body);
|
|
19941
|
+
}
|
|
19942
|
+
}
|
|
19943
|
+
}
|
|
19944
|
+
function planGeneratedProjection(root) {
|
|
19945
|
+
const desiredBody = GENERATED_ARCHITECTURE_BODY;
|
|
19946
|
+
const desiredAbsolute = resolve14(root, GENERATED_ARCHITECTURE_PATH);
|
|
19947
|
+
const operations = [];
|
|
19948
|
+
if (!existsSync12(desiredAbsolute) || readFileSync10(desiredAbsolute, "utf8") !== desiredBody) {
|
|
19949
|
+
operations.push({
|
|
19950
|
+
path: GENERATED_ARCHITECTURE_PATH,
|
|
19951
|
+
expectedHash: existsSync12(desiredAbsolute) ? digestJson({ body: readFileSync10(desiredAbsolute, "utf8") }) : "missing",
|
|
19952
|
+
body: desiredBody,
|
|
19953
|
+
operation: "render_projection"
|
|
19954
|
+
});
|
|
19955
|
+
}
|
|
19956
|
+
for (const path of collectGeneratedProjectionFiles(root)) {
|
|
19957
|
+
if (path === GENERATED_ARCHITECTURE_PATH)
|
|
19958
|
+
continue;
|
|
19959
|
+
const body = readFileSync10(resolve14(root, path), "utf8");
|
|
19960
|
+
operations.push({
|
|
19961
|
+
path,
|
|
19962
|
+
expectedHash: digestJson({ body }),
|
|
19963
|
+
body: "",
|
|
19964
|
+
operation: "delete_entity"
|
|
19965
|
+
});
|
|
19966
|
+
}
|
|
19967
|
+
return operations.sort((left, right) => left.path.localeCompare(right.path));
|
|
19968
|
+
}
|
|
19969
|
+
function collectGeneratedProjectionFiles(root) {
|
|
19970
|
+
const generatedRoot = ".archcontext/generated";
|
|
19971
|
+
if (!existsSync12(resolve14(root, generatedRoot)))
|
|
19972
|
+
return [];
|
|
19973
|
+
const walk = (relativeDir) => readdirSync7(resolve14(root, relativeDir), { withFileTypes: true }).flatMap((entry) => {
|
|
19974
|
+
const child = `${relativeDir}/${entry.name}`;
|
|
19975
|
+
if (entry.isDirectory())
|
|
19976
|
+
return walk(child);
|
|
19977
|
+
return entry.isFile() ? [child] : [];
|
|
19978
|
+
});
|
|
19979
|
+
return walk(generatedRoot).sort();
|
|
19543
19980
|
}
|
|
19544
19981
|
|
|
19545
19982
|
class YamlModelStore {
|
|
@@ -19596,7 +20033,7 @@ function collectArchContextFiles(root, entries) {
|
|
|
19596
20033
|
const files = [];
|
|
19597
20034
|
for (const entry of entries) {
|
|
19598
20035
|
const absolute = resolve14(root, entry);
|
|
19599
|
-
if (!
|
|
20036
|
+
if (!existsSync12(absolute))
|
|
19600
20037
|
continue;
|
|
19601
20038
|
const stat = readdirOrFile(absolute);
|
|
19602
20039
|
if (stat === "file") {
|
|
@@ -20011,7 +20448,8 @@ class ArchitectureLedgerReadModelStore {
|
|
|
20011
20448
|
return this.fallback.writeChangeSetPreview(changeSet);
|
|
20012
20449
|
}
|
|
20013
20450
|
async loadLedgerModel(workspace) {
|
|
20014
|
-
const
|
|
20451
|
+
const scope = await this.localStore.resolveArchitectureLedgerScope(architectureLedgerScopeForWorkspace(workspace));
|
|
20452
|
+
const state = await this.localStore.readArchitectureLedgerState(scope);
|
|
20015
20453
|
const projectedFiles = projectArchitectureLedgerStateToYamlFiles(state).map((file) => ({
|
|
20016
20454
|
path: file.path,
|
|
20017
20455
|
body: file.body,
|
|
@@ -20095,7 +20533,7 @@ class ArchctxDaemon {
|
|
|
20095
20533
|
this.readModelStore = new ArchitectureLedgerReadModelStore(this.modelStore, this.localStore, this.architectureLedger);
|
|
20096
20534
|
this.changeSetEngine = deps.changeSetEngine ?? new ChangeSetEngine({
|
|
20097
20535
|
modelStore: this.modelStore,
|
|
20098
|
-
projection: {
|
|
20536
|
+
projection: { planGeneratedProjection },
|
|
20099
20537
|
journal: this.localStore
|
|
20100
20538
|
});
|
|
20101
20539
|
this.devicePrivateKeySigner = deps.devicePrivateKeySigner;
|
|
@@ -21047,7 +21485,7 @@ class ArchctxDaemon {
|
|
|
21047
21485
|
const absolute = resolve15(session.workspace.root, path);
|
|
21048
21486
|
const body = `${JSON.stringify(waiver, null, 2)}
|
|
21049
21487
|
`;
|
|
21050
|
-
const expectedHash =
|
|
21488
|
+
const expectedHash = existsSync13(absolute) ? digestJson({ body: readFileSync11(absolute, "utf8") }) : "missing";
|
|
21051
21489
|
const draft = this.changeSetEngine.plan({
|
|
21052
21490
|
id: input.id ?? `changeset.practice-waiver-${waiverId.replace(/[^A-Za-z0-9_-]/g, "-")}`,
|
|
21053
21491
|
base: {
|
|
@@ -21386,6 +21824,16 @@ class ArchctxDaemon {
|
|
|
21386
21824
|
const draft = this.changesets.get(input.id);
|
|
21387
21825
|
if (!draft)
|
|
21388
21826
|
throw new Error(`Unknown ChangeSet: ${input.id}`);
|
|
21827
|
+
if (draft.base.headSha !== session.workspace.headSha)
|
|
21828
|
+
throw new Error("ChangeSet HEAD changed before apply");
|
|
21829
|
+
if (draft.base.worktreeDigest !== current)
|
|
21830
|
+
throw new Error("ChangeSet worktree digest changed before apply");
|
|
21831
|
+
const currentModel = await this.readModelStore.validateModel(session.workspace);
|
|
21832
|
+
if (!currentModel.valid) {
|
|
21833
|
+
throw new Error(`ChangeSet base model is invalid: ${currentModel.errors.join("; ") || "unknown validation error"}`);
|
|
21834
|
+
}
|
|
21835
|
+
if (draft.base.modelDigest !== currentModel.modelDigest)
|
|
21836
|
+
throw new Error("ChangeSet model digest changed before apply");
|
|
21389
21837
|
const approved = input.approved ? this.changeSetEngine.approve(draft) : draft;
|
|
21390
21838
|
let ledgerAppend;
|
|
21391
21839
|
const writesLedger = architectureLedgerWriteAppendsEvents(this.architectureLedger.writeMode);
|
|
@@ -21402,6 +21850,7 @@ class ArchctxDaemon {
|
|
|
21402
21850
|
relationCount: appended.relationCount,
|
|
21403
21851
|
constraintCount: appended.constraintCount
|
|
21404
21852
|
};
|
|
21853
|
+
return { journalCommitted: Boolean(journalId) };
|
|
21405
21854
|
} : undefined
|
|
21406
21855
|
});
|
|
21407
21856
|
return okEnvelope("apply_update", {
|
|
@@ -21435,14 +21884,72 @@ class ArchctxDaemon {
|
|
|
21435
21884
|
});
|
|
21436
21885
|
if (journalId)
|
|
21437
21886
|
await this.localStore.recordChangeSetLedgerPlan(journalId, { event: plan.event });
|
|
21438
|
-
const
|
|
21439
|
-
|
|
21440
|
-
events: [plan.event]
|
|
21441
|
-
});
|
|
21442
|
-
if (journalId)
|
|
21443
|
-
await this.localStore.recordChangeSetLedgerAppend(journalId, { result });
|
|
21887
|
+
const appendInput = { writer: "runtime-daemon", events: [plan.event] };
|
|
21888
|
+
const result = journalId ? await this.localStore.appendArchitectureEventsAndCommitChangeSet(journalId, appendInput) : await this.localStore.appendArchitectureEvents(appendInput);
|
|
21444
21889
|
return result;
|
|
21445
21890
|
}
|
|
21891
|
+
async applyArchitectureProjectionChangeSet(root, input) {
|
|
21892
|
+
const session = await this.openSession(root);
|
|
21893
|
+
const model = await this.modelStore.validateModel(session.workspace);
|
|
21894
|
+
if (!model.valid)
|
|
21895
|
+
throw new Error(`Architecture projection ChangeSet base model is invalid: ${model.errors.join("; ")}`);
|
|
21896
|
+
const projectionFiles = input.files.map((file) => ({
|
|
21897
|
+
path: file.path,
|
|
21898
|
+
body: file.body.endsWith(`
|
|
21899
|
+
`) ? file.body : `${file.body}
|
|
21900
|
+
`,
|
|
21901
|
+
expectedHash: expectedFileHash(root, file.path)
|
|
21902
|
+
}));
|
|
21903
|
+
const operations = [
|
|
21904
|
+
...projectionFiles.length > 0 ? [{ op: "render_projection", expectedHash: "missing", projectionFiles }] : [],
|
|
21905
|
+
...input.removedPaths.map((path) => ({
|
|
21906
|
+
op: "delete_entity",
|
|
21907
|
+
path,
|
|
21908
|
+
expectedHash: expectedFileHash(root, path)
|
|
21909
|
+
}))
|
|
21910
|
+
];
|
|
21911
|
+
const draft = this.changeSetEngine.approve(this.changeSetEngine.plan({
|
|
21912
|
+
id: input.id,
|
|
21913
|
+
base: {
|
|
21914
|
+
headSha: session.workspace.headSha,
|
|
21915
|
+
worktreeDigest: session.snapshot.worktreeDigest,
|
|
21916
|
+
modelDigest: model.modelDigest
|
|
21917
|
+
},
|
|
21918
|
+
reason: { taskSessionId: input.id },
|
|
21919
|
+
operations
|
|
21920
|
+
}));
|
|
21921
|
+
await this.changeSetEngine.apply(root, draft, { approved: true });
|
|
21922
|
+
}
|
|
21923
|
+
async applyArchitectureProjectionRollbackChangeSet(root, projectedFiles, currentFiles, createdAt) {
|
|
21924
|
+
const targetPaths = new Set(projectedFiles.map((file) => file.path));
|
|
21925
|
+
const backupBase = `.archcontext/backups/ledger-rollback/${safePathSegment(createdAt)}`;
|
|
21926
|
+
const backupRelativePath = uniqueBackupPath(root, backupBase);
|
|
21927
|
+
const manifestPath = `${backupRelativePath}/manifest.json`;
|
|
21928
|
+
const { backup, manifest } = architectureProjectionRollbackBackup(currentFiles, {
|
|
21929
|
+
createdAt,
|
|
21930
|
+
path: backupRelativePath,
|
|
21931
|
+
manifestPath
|
|
21932
|
+
});
|
|
21933
|
+
const removedPaths = currentFiles.filter((file) => !targetPaths.has(file.path)).map((file) => file.path);
|
|
21934
|
+
await this.applyArchitectureProjectionChangeSet(root, {
|
|
21935
|
+
id: `changeset.ledger-rollback-${shortDigest3(digestJson({ createdAt, projectionDigest: architectureLedgerProjectionDigest(projectedFiles) }))}`,
|
|
21936
|
+
files: [
|
|
21937
|
+
...currentFiles.map((file) => ({
|
|
21938
|
+
path: join8(backupRelativePath, archContextRelativePath(file.path)),
|
|
21939
|
+
body: file.body
|
|
21940
|
+
})),
|
|
21941
|
+
{ path: manifestPath, body: `${JSON.stringify(manifest, null, 2)}
|
|
21942
|
+
` },
|
|
21943
|
+
...projectedFiles.map(({ path, body }) => ({ path, body }))
|
|
21944
|
+
],
|
|
21945
|
+
removedPaths
|
|
21946
|
+
});
|
|
21947
|
+
return {
|
|
21948
|
+
backup,
|
|
21949
|
+
writtenPaths: projectedFiles.map((file) => file.path),
|
|
21950
|
+
removedPaths
|
|
21951
|
+
};
|
|
21952
|
+
}
|
|
21446
21953
|
async ledgerState(root) {
|
|
21447
21954
|
this.assertRunning();
|
|
21448
21955
|
return okEnvelope("ledger.state", await this.architectureLedgerReadback(root));
|
|
@@ -21796,8 +22303,13 @@ class ArchctxDaemon {
|
|
|
21796
22303
|
const scope = await this.architectureLedgerScope(root);
|
|
21797
22304
|
const state = await this.localStore.readArchitectureLedgerState(scope);
|
|
21798
22305
|
const projectedFiles = projectArchitectureLedgerStateToYamlFiles(state);
|
|
21799
|
-
if (writes)
|
|
21800
|
-
|
|
22306
|
+
if (writes) {
|
|
22307
|
+
await this.applyArchitectureProjectionChangeSet(root, {
|
|
22308
|
+
id: `changeset.ledger-project-${shortDigest3(architectureLedgerProjectionDigest(projectedFiles))}`,
|
|
22309
|
+
files: projectedFiles.map(({ path, body }) => ({ path, body })),
|
|
22310
|
+
removedPaths: []
|
|
22311
|
+
});
|
|
22312
|
+
}
|
|
21801
22313
|
const drift = compareArchitectureLedgerStateToYaml({
|
|
21802
22314
|
state,
|
|
21803
22315
|
files: listModelFiles(root),
|
|
@@ -21947,7 +22459,7 @@ class ArchctxDaemon {
|
|
|
21947
22459
|
const projectedFiles = projectArchitectureLedgerStateToYamlFiles(state);
|
|
21948
22460
|
const currentManagedFiles = listModelFiles(root).filter((file) => isArchitectureLedgerManagedModelPath(file.path));
|
|
21949
22461
|
const backupPlan = architectureProjectionRollbackBackup(currentManagedFiles);
|
|
21950
|
-
const writeResult = writes ?
|
|
22462
|
+
const writeResult = writes ? await this.applyArchitectureProjectionRollbackChangeSet(root, projectedFiles, currentManagedFiles, this.clock()) : { backup: backupPlan.backup, writtenPaths: [], removedPaths: [] };
|
|
21951
22463
|
const drift = compareArchitectureLedgerStateToYaml({
|
|
21952
22464
|
state,
|
|
21953
22465
|
files: listModelFiles(root),
|
|
@@ -21986,9 +22498,12 @@ class ArchctxDaemon {
|
|
|
21986
22498
|
return errorEnvelope("ledger.rebuild", "AC_SCHEMA_INVALID", "ledger rebuild currently requires --from-git");
|
|
21987
22499
|
return this.withWriter(async () => {
|
|
21988
22500
|
this.assertFreshWorktree(root, input.expectedWorktreeDigest, "ledger rebuild --from-git");
|
|
21989
|
-
const scope = await this.
|
|
22501
|
+
const scope = await this.architectureLedgerGitScope(root);
|
|
21990
22502
|
const files = listModelFiles(root);
|
|
21991
|
-
const
|
|
22503
|
+
const exactReplay = await this.localStore.replayArchitectureLedger(scope);
|
|
22504
|
+
const exactScopeHasEvents = exactReplay.events.length > 0;
|
|
22505
|
+
const authorityScope = exactScopeHasEvents ? scope : await this.localStore.resolveLatestArchitectureLedgerScope(scope);
|
|
22506
|
+
const previousState = exactScopeHasEvents ? exactReplay.state : await this.localStore.readArchitectureLedgerState(authorityScope);
|
|
21992
22507
|
const previousGraphDigest = architectureLedgerStateDigest(previousState);
|
|
21993
22508
|
const rebuildCommand = input.acceptExternalProjection ? "archctx ledger rebuild --from-git --accept-external-projection" : "archctx ledger rebuild --from-git";
|
|
21994
22509
|
const plan = planYamlToArchitectureLedgerRebuild({
|
|
@@ -21998,12 +22513,18 @@ class ArchctxDaemon {
|
|
|
21998
22513
|
command: rebuildCommand,
|
|
21999
22514
|
previousState
|
|
22000
22515
|
});
|
|
22516
|
+
const importPlan = planYamlToArchitectureLedgerImport({
|
|
22517
|
+
...scope,
|
|
22518
|
+
files,
|
|
22519
|
+
createdAt: this.clock(),
|
|
22520
|
+
command: rebuildCommand
|
|
22521
|
+
});
|
|
22001
22522
|
if (plan.unsupportedFiles.length > 0) {
|
|
22002
22523
|
return errorEnvelope("ledger.rebuild", "AC_SCHEMA_INVALID", "ledger rebuild requires supported YAML model files");
|
|
22003
22524
|
}
|
|
22004
22525
|
const cursor = architectureLedgerGitCursorFromPlan({ ...scope, plan });
|
|
22005
22526
|
const previousCursor = await this.localStore.readArchitectureLedgerSourceCursor({
|
|
22006
|
-
...
|
|
22527
|
+
...authorityScope,
|
|
22007
22528
|
cursorId: ARCHITECTURE_LEDGER_GIT_CURSOR_ID
|
|
22008
22529
|
});
|
|
22009
22530
|
const cursorChanged = previousCursor?.cursorDigest !== cursor.cursorDigest;
|
|
@@ -22019,7 +22540,13 @@ class ArchctxDaemon {
|
|
|
22019
22540
|
let rebuildStatus = "unchanged";
|
|
22020
22541
|
let proposedExternalProjectionChange;
|
|
22021
22542
|
if (previousGraphDigest === plan.graphDigest) {
|
|
22022
|
-
if (
|
|
22543
|
+
if (!exactScopeHasEvents) {
|
|
22544
|
+
append = await this.localStore.appendArchitectureEvents({
|
|
22545
|
+
writer: "runtime-daemon",
|
|
22546
|
+
events: [importPlan.event]
|
|
22547
|
+
});
|
|
22548
|
+
rebuildStatus = isEmptyArchitectureLedgerState(previousState) ? "rebuilt" : "cursor-refreshed";
|
|
22549
|
+
} else if (cursorChanged) {
|
|
22023
22550
|
const cursorPlan = planGitCursorRefreshToArchitectureLedgerEvent({
|
|
22024
22551
|
...scope,
|
|
22025
22552
|
cursor,
|
|
@@ -22035,7 +22562,7 @@ class ArchctxDaemon {
|
|
|
22035
22562
|
}
|
|
22036
22563
|
} else if (!previousStateEmpty && !input.acceptExternalProjection) {
|
|
22037
22564
|
const proposal = planExternalProjectionChangeToArchitectureLedgerEvent({
|
|
22038
|
-
...
|
|
22565
|
+
...authorityScope,
|
|
22039
22566
|
files,
|
|
22040
22567
|
createdAt: this.clock(),
|
|
22041
22568
|
command: rebuildCommand,
|
|
@@ -22058,11 +22585,12 @@ class ArchctxDaemon {
|
|
|
22058
22585
|
} else {
|
|
22059
22586
|
append = await this.localStore.appendArchitectureEvents({
|
|
22060
22587
|
writer: "runtime-daemon",
|
|
22061
|
-
events: [plan.event]
|
|
22588
|
+
events: [exactScopeHasEvents ? plan.event : importPlan.event]
|
|
22062
22589
|
});
|
|
22063
22590
|
rebuildStatus = previousStateEmpty ? "rebuilt" : "external-projection-accepted";
|
|
22064
22591
|
}
|
|
22065
|
-
const
|
|
22592
|
+
const replayScope = rebuildStatus === "external-projection-proposed" ? authorityScope : scope;
|
|
22593
|
+
const replay = await this.localStore.rebuildArchitectureLedgerCurrentState(replayScope);
|
|
22066
22594
|
const drift = compareArchitectureLedgerStateToYaml({
|
|
22067
22595
|
state: replay.state,
|
|
22068
22596
|
files: listModelFiles(root),
|
|
@@ -22208,6 +22736,9 @@ class ArchctxDaemon {
|
|
|
22208
22736
|
return this.running ? this.architectureLedgerContextPort(root) : undefined;
|
|
22209
22737
|
}
|
|
22210
22738
|
async architectureLedgerScope(root) {
|
|
22739
|
+
return this.localStore.resolveArchitectureLedgerScope(await this.architectureLedgerGitScope(root));
|
|
22740
|
+
}
|
|
22741
|
+
async architectureLedgerGitScope(root) {
|
|
22211
22742
|
const session = await this.openSession(root);
|
|
22212
22743
|
const paths = runtimeStatePaths2(root);
|
|
22213
22744
|
return {
|
|
@@ -22270,7 +22801,7 @@ class ArchctxDaemon {
|
|
|
22270
22801
|
cleanup: "remove-run-root"
|
|
22271
22802
|
}
|
|
22272
22803
|
};
|
|
22273
|
-
if (
|
|
22804
|
+
if (existsSync13(paths.lockPath) || existsSync13(paths.manifestPath)) {
|
|
22274
22805
|
removePathWithRetry(paths.runRoot);
|
|
22275
22806
|
throw new Error(`developer-review-run-already-active: ${input.challenge.challengeId}`);
|
|
22276
22807
|
}
|
|
@@ -22327,7 +22858,7 @@ class ArchctxDaemon {
|
|
|
22327
22858
|
const errors = [];
|
|
22328
22859
|
if (run.worktree) {
|
|
22329
22860
|
try {
|
|
22330
|
-
const hadWorktree =
|
|
22861
|
+
const hadWorktree = existsSync13(run.worktree.worktreeRoot);
|
|
22331
22862
|
removeDetachedReviewWorktree(run.worktree);
|
|
22332
22863
|
if (hadWorktree)
|
|
22333
22864
|
removed.push("worktree");
|
|
@@ -22341,7 +22872,7 @@ class ArchctxDaemon {
|
|
|
22341
22872
|
["lock", run.lockPath]
|
|
22342
22873
|
]) {
|
|
22343
22874
|
try {
|
|
22344
|
-
const existed =
|
|
22875
|
+
const existed = existsSync13(path);
|
|
22345
22876
|
removePathWithRetry(path);
|
|
22346
22877
|
if (existed)
|
|
22347
22878
|
removed.push(kind);
|
|
@@ -22370,7 +22901,7 @@ class ArchctxDaemon {
|
|
|
22370
22901
|
removedLocks: [],
|
|
22371
22902
|
skippedActive: []
|
|
22372
22903
|
};
|
|
22373
|
-
if (!
|
|
22904
|
+
if (!existsSync13(stateDir))
|
|
22374
22905
|
return recovery;
|
|
22375
22906
|
for (const entry of readdirSync8(stateDir).sort()) {
|
|
22376
22907
|
if (!entry.endsWith(".json"))
|
|
@@ -22738,7 +23269,7 @@ class ArchctxDaemon {
|
|
|
22738
23269
|
}
|
|
22739
23270
|
async restoreRepositorySessions() {
|
|
22740
23271
|
for (const record of await this.localStore.listRepositorySessions()) {
|
|
22741
|
-
if (!record.root || !
|
|
23272
|
+
if (!record.root || !existsSync13(record.root))
|
|
22742
23273
|
continue;
|
|
22743
23274
|
if (repositoryFingerprint2(record.root) !== record.repositoryId)
|
|
22744
23275
|
continue;
|
|
@@ -23531,7 +24062,7 @@ function recoverStaleDaemonControlFiles(root = process.cwd(), options = {}) {
|
|
|
23531
24062
|
rmSync8(connectionPath, { force: true });
|
|
23532
24063
|
removed.push(connectionReason);
|
|
23533
24064
|
}
|
|
23534
|
-
if (
|
|
24065
|
+
if (existsSync13(lockPath) && isStaleLock(lockPath)) {
|
|
23535
24066
|
rmSync8(lockPath, { force: true });
|
|
23536
24067
|
removed.push("stale-lock-file");
|
|
23537
24068
|
}
|
|
@@ -23979,14 +24510,14 @@ function expandWorkspacePackageJson(root, pattern) {
|
|
|
23979
24510
|
return [];
|
|
23980
24511
|
if (!pattern.includes("*")) {
|
|
23981
24512
|
const path = resolve15(root, pattern, "package.json");
|
|
23982
|
-
return
|
|
24513
|
+
return existsSync13(path) ? [path] : [];
|
|
23983
24514
|
}
|
|
23984
24515
|
if (!pattern.endsWith("/*"))
|
|
23985
24516
|
return [];
|
|
23986
24517
|
const base = resolve15(root, pattern.slice(0, -2));
|
|
23987
|
-
if (!
|
|
24518
|
+
if (!existsSync13(base) || !statSync6(base).isDirectory())
|
|
23988
24519
|
return [];
|
|
23989
|
-
return readdirSync8(base, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => resolve15(base, entry.name, "package.json")).filter((path) =>
|
|
24520
|
+
return readdirSync8(base, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => resolve15(base, entry.name, "package.json")).filter((path) => existsSync13(path));
|
|
23990
24521
|
}
|
|
23991
24522
|
function exactVersionFromManifest(manifest, packageName) {
|
|
23992
24523
|
if (!manifest || typeof manifest !== "object" || Array.isArray(manifest))
|
|
@@ -24002,7 +24533,7 @@ function exactVersionFromManifest(manifest, packageName) {
|
|
|
24002
24533
|
return;
|
|
24003
24534
|
}
|
|
24004
24535
|
function readJsonFile(path) {
|
|
24005
|
-
if (!
|
|
24536
|
+
if (!existsSync13(path))
|
|
24006
24537
|
return;
|
|
24007
24538
|
try {
|
|
24008
24539
|
return JSON.parse(readFileSync11(path, "utf8"));
|
|
@@ -24015,7 +24546,7 @@ function isExactPackageVersion(value) {
|
|
|
24015
24546
|
}
|
|
24016
24547
|
function readContext7Lockfile(root) {
|
|
24017
24548
|
const path = resolve15(root, CONTEXT7_LOCKFILE);
|
|
24018
|
-
if (!
|
|
24549
|
+
if (!existsSync13(path)) {
|
|
24019
24550
|
return {
|
|
24020
24551
|
schemaVersion: CONTEXT7_LOCKFILE_SCHEMA_VERSION,
|
|
24021
24552
|
provider: "context7",
|
|
@@ -24264,48 +24795,9 @@ function auditApproveResultPayload(runId, status, totalCount, issuedIssues) {
|
|
|
24264
24795
|
issuedIssues
|
|
24265
24796
|
};
|
|
24266
24797
|
}
|
|
24267
|
-
function
|
|
24268
|
-
|
|
24269
|
-
|
|
24270
|
-
mkdirSync7(dirname8(absolute), { recursive: true });
|
|
24271
|
-
writeFileSync6(absolute, file.body.endsWith(`
|
|
24272
|
-
`) ? file.body : `${file.body}
|
|
24273
|
-
`, "utf8");
|
|
24274
|
-
}
|
|
24275
|
-
}
|
|
24276
|
-
function replaceArchitectureProjectionFilesForYamlRollback(root, projectedFiles, currentFiles, createdAt) {
|
|
24277
|
-
const targetPaths = new Set(projectedFiles.map((file) => file.path));
|
|
24278
|
-
const backupBase = `.archcontext/backups/ledger-rollback/${safePathSegment(createdAt)}`;
|
|
24279
|
-
const backupRelativePath = uniqueBackupPath(root, backupBase);
|
|
24280
|
-
const manifestPath = `${backupRelativePath}/manifest.json`;
|
|
24281
|
-
const { backup, manifest } = architectureProjectionRollbackBackup(currentFiles, {
|
|
24282
|
-
createdAt,
|
|
24283
|
-
path: backupRelativePath,
|
|
24284
|
-
manifestPath
|
|
24285
|
-
});
|
|
24286
|
-
for (const file of currentFiles) {
|
|
24287
|
-
const backupPath = join8(backupRelativePath, archContextRelativePath(file.path));
|
|
24288
|
-
const absolute = resolve15(root, backupPath);
|
|
24289
|
-
mkdirSync7(dirname8(absolute), { recursive: true });
|
|
24290
|
-
writeFileSync6(absolute, file.body, "utf8");
|
|
24291
|
-
}
|
|
24292
|
-
const manifestAbsolute = resolve15(root, manifestPath);
|
|
24293
|
-
mkdirSync7(dirname8(manifestAbsolute), { recursive: true });
|
|
24294
|
-
writeFileSync6(manifestAbsolute, `${JSON.stringify(manifest, null, 2)}
|
|
24295
|
-
`, "utf8");
|
|
24296
|
-
const removedPaths = [];
|
|
24297
|
-
for (const file of currentFiles) {
|
|
24298
|
-
if (targetPaths.has(file.path))
|
|
24299
|
-
continue;
|
|
24300
|
-
rmSync8(resolve15(root, file.path), { force: true });
|
|
24301
|
-
removedPaths.push(file.path);
|
|
24302
|
-
}
|
|
24303
|
-
writeArchitectureProjectionFiles(root, projectedFiles);
|
|
24304
|
-
return {
|
|
24305
|
-
backup,
|
|
24306
|
-
writtenPaths: projectedFiles.map((file) => file.path),
|
|
24307
|
-
removedPaths
|
|
24308
|
-
};
|
|
24798
|
+
function expectedFileHash(root, path) {
|
|
24799
|
+
const absolute = resolve15(root, path);
|
|
24800
|
+
return existsSync13(absolute) ? digestJson({ body: readFileSync11(absolute, "utf8") }) : "missing";
|
|
24309
24801
|
}
|
|
24310
24802
|
function architectureProjectionRollbackBackup(files, options = {}) {
|
|
24311
24803
|
const manifest = {
|
|
@@ -24332,7 +24824,7 @@ function architectureProjectionRollbackBackup(files, options = {}) {
|
|
|
24332
24824
|
function uniqueBackupPath(root, backupBase) {
|
|
24333
24825
|
let candidate = backupBase;
|
|
24334
24826
|
let suffix = 2;
|
|
24335
|
-
while (
|
|
24827
|
+
while (existsSync13(resolve15(root, candidate))) {
|
|
24336
24828
|
candidate = `${backupBase}-${suffix}`;
|
|
24337
24829
|
suffix += 1;
|
|
24338
24830
|
}
|
|
@@ -24341,7 +24833,7 @@ function uniqueBackupPath(root, backupBase) {
|
|
|
24341
24833
|
function uniqueRuntimeBackupPath(backupBase) {
|
|
24342
24834
|
let candidate = backupBase;
|
|
24343
24835
|
let suffix = 2;
|
|
24344
|
-
while (
|
|
24836
|
+
while (existsSync13(candidate)) {
|
|
24345
24837
|
candidate = backupBase.replace(/\.sqlite$/, `-${suffix}.sqlite`);
|
|
24346
24838
|
suffix += 1;
|
|
24347
24839
|
}
|
|
@@ -24386,7 +24878,7 @@ function isValidRuntimeRpcConnection(value) {
|
|
|
24386
24878
|
return value.schemaVersion === RUNTIME_RPC_VERSION && value.protocol === "http-loopback" && value.version === 1 && typeof value.url === "string" && value.url.startsWith("http://127.0.0.1:") && typeof value.token === "string" && value.token.length > 0 && typeof value.pid === "number" && typeof value.connectionPath === "string" && typeof value.lockPath === "string";
|
|
24387
24879
|
}
|
|
24388
24880
|
function staleConnectionFileReason(path, removeUnhealthyConnection) {
|
|
24389
|
-
if (!
|
|
24881
|
+
if (!existsSync13(path))
|
|
24390
24882
|
return;
|
|
24391
24883
|
try {
|
|
24392
24884
|
if (!isPrivateControlFile(path))
|
|
@@ -24448,7 +24940,7 @@ function parsePracticeCheckpointBaselineState(state, repositoryId, taskSessionId
|
|
|
24448
24940
|
return record;
|
|
24449
24941
|
}
|
|
24450
24942
|
function completeTaskProjectionDrift(root) {
|
|
24451
|
-
if (!
|
|
24943
|
+
if (!existsSync13(resolve15(root, "docs/architecture/.projection-manifest.json")))
|
|
24452
24944
|
return;
|
|
24453
24945
|
const loaded = loadArchitectureDocumentationInputs(root);
|
|
24454
24946
|
const sourceDigest = architectureDocumentationSourceDigest({
|
|
@@ -24724,7 +25216,7 @@ init_src();
|
|
|
24724
25216
|
// packages/local-runtime/runtime-daemon/src/index.ts
|
|
24725
25217
|
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
24726
25218
|
import { execFileSync as execFileSync7 } from "node:child_process";
|
|
24727
|
-
import { chmodSync as chmodSync4, closeSync as closeSync6, existsSync as
|
|
25219
|
+
import { chmodSync as chmodSync4, closeSync as closeSync6, existsSync as existsSync14, mkdirSync as mkdirSync8, mkdtempSync as mkdtempSync5, openSync as openSync6, readdirSync as readdirSync9, readFileSync as readFileSync12, rmSync as rmSync9, statSync as statSync7, writeFileSync as writeFileSync7 } from "node:fs";
|
|
24728
25220
|
import { createServer as createServer2 } from "node:http";
|
|
24729
25221
|
import { tmpdir as tmpdir4 } from "node:os";
|
|
24730
25222
|
import { dirname as dirname9, join as join9, resolve as resolve16 } from "node:path";
|
|
@@ -24837,7 +25329,8 @@ class ArchitectureLedgerReadModelStore2 {
|
|
|
24837
25329
|
return this.fallback.writeChangeSetPreview(changeSet);
|
|
24838
25330
|
}
|
|
24839
25331
|
async loadLedgerModel(workspace) {
|
|
24840
|
-
const
|
|
25332
|
+
const scope = await this.localStore.resolveArchitectureLedgerScope(architectureLedgerScopeForWorkspace2(workspace));
|
|
25333
|
+
const state = await this.localStore.readArchitectureLedgerState(scope);
|
|
24841
25334
|
const projectedFiles = projectArchitectureLedgerStateToYamlFiles(state).map((file) => ({
|
|
24842
25335
|
path: file.path,
|
|
24843
25336
|
body: file.body,
|
|
@@ -24921,7 +25414,7 @@ class ArchctxDaemon2 {
|
|
|
24921
25414
|
this.readModelStore = new ArchitectureLedgerReadModelStore2(this.modelStore, this.localStore, this.architectureLedger);
|
|
24922
25415
|
this.changeSetEngine = deps.changeSetEngine ?? new ChangeSetEngine({
|
|
24923
25416
|
modelStore: this.modelStore,
|
|
24924
|
-
projection: {
|
|
25417
|
+
projection: { planGeneratedProjection },
|
|
24925
25418
|
journal: this.localStore
|
|
24926
25419
|
});
|
|
24927
25420
|
this.devicePrivateKeySigner = deps.devicePrivateKeySigner;
|
|
@@ -25873,7 +26366,7 @@ class ArchctxDaemon2 {
|
|
|
25873
26366
|
const absolute = resolve16(session.workspace.root, path);
|
|
25874
26367
|
const body = `${JSON.stringify(waiver, null, 2)}
|
|
25875
26368
|
`;
|
|
25876
|
-
const expectedHash =
|
|
26369
|
+
const expectedHash = existsSync14(absolute) ? digestJson({ body: readFileSync12(absolute, "utf8") }) : "missing";
|
|
25877
26370
|
const draft = this.changeSetEngine.plan({
|
|
25878
26371
|
id: input.id ?? `changeset.practice-waiver-${waiverId.replace(/[^A-Za-z0-9_-]/g, "-")}`,
|
|
25879
26372
|
base: {
|
|
@@ -26212,6 +26705,16 @@ class ArchctxDaemon2 {
|
|
|
26212
26705
|
const draft = this.changesets.get(input.id);
|
|
26213
26706
|
if (!draft)
|
|
26214
26707
|
throw new Error(`Unknown ChangeSet: ${input.id}`);
|
|
26708
|
+
if (draft.base.headSha !== session.workspace.headSha)
|
|
26709
|
+
throw new Error("ChangeSet HEAD changed before apply");
|
|
26710
|
+
if (draft.base.worktreeDigest !== current)
|
|
26711
|
+
throw new Error("ChangeSet worktree digest changed before apply");
|
|
26712
|
+
const currentModel = await this.readModelStore.validateModel(session.workspace);
|
|
26713
|
+
if (!currentModel.valid) {
|
|
26714
|
+
throw new Error(`ChangeSet base model is invalid: ${currentModel.errors.join("; ") || "unknown validation error"}`);
|
|
26715
|
+
}
|
|
26716
|
+
if (draft.base.modelDigest !== currentModel.modelDigest)
|
|
26717
|
+
throw new Error("ChangeSet model digest changed before apply");
|
|
26215
26718
|
const approved = input.approved ? this.changeSetEngine.approve(draft) : draft;
|
|
26216
26719
|
let ledgerAppend;
|
|
26217
26720
|
const writesLedger = architectureLedgerWriteAppendsEvents2(this.architectureLedger.writeMode);
|
|
@@ -26228,6 +26731,7 @@ class ArchctxDaemon2 {
|
|
|
26228
26731
|
relationCount: appended.relationCount,
|
|
26229
26732
|
constraintCount: appended.constraintCount
|
|
26230
26733
|
};
|
|
26734
|
+
return { journalCommitted: Boolean(journalId) };
|
|
26231
26735
|
} : undefined
|
|
26232
26736
|
});
|
|
26233
26737
|
return okEnvelope("apply_update", {
|
|
@@ -26261,14 +26765,72 @@ class ArchctxDaemon2 {
|
|
|
26261
26765
|
});
|
|
26262
26766
|
if (journalId)
|
|
26263
26767
|
await this.localStore.recordChangeSetLedgerPlan(journalId, { event: plan.event });
|
|
26264
|
-
const
|
|
26265
|
-
|
|
26266
|
-
events: [plan.event]
|
|
26267
|
-
});
|
|
26268
|
-
if (journalId)
|
|
26269
|
-
await this.localStore.recordChangeSetLedgerAppend(journalId, { result });
|
|
26768
|
+
const appendInput = { writer: "runtime-daemon", events: [plan.event] };
|
|
26769
|
+
const result = journalId ? await this.localStore.appendArchitectureEventsAndCommitChangeSet(journalId, appendInput) : await this.localStore.appendArchitectureEvents(appendInput);
|
|
26270
26770
|
return result;
|
|
26271
26771
|
}
|
|
26772
|
+
async applyArchitectureProjectionChangeSet(root, input) {
|
|
26773
|
+
const session = await this.openSession(root);
|
|
26774
|
+
const model = await this.modelStore.validateModel(session.workspace);
|
|
26775
|
+
if (!model.valid)
|
|
26776
|
+
throw new Error(`Architecture projection ChangeSet base model is invalid: ${model.errors.join("; ")}`);
|
|
26777
|
+
const projectionFiles = input.files.map((file) => ({
|
|
26778
|
+
path: file.path,
|
|
26779
|
+
body: file.body.endsWith(`
|
|
26780
|
+
`) ? file.body : `${file.body}
|
|
26781
|
+
`,
|
|
26782
|
+
expectedHash: expectedFileHash2(root, file.path)
|
|
26783
|
+
}));
|
|
26784
|
+
const operations = [
|
|
26785
|
+
...projectionFiles.length > 0 ? [{ op: "render_projection", expectedHash: "missing", projectionFiles }] : [],
|
|
26786
|
+
...input.removedPaths.map((path) => ({
|
|
26787
|
+
op: "delete_entity",
|
|
26788
|
+
path,
|
|
26789
|
+
expectedHash: expectedFileHash2(root, path)
|
|
26790
|
+
}))
|
|
26791
|
+
];
|
|
26792
|
+
const draft = this.changeSetEngine.approve(this.changeSetEngine.plan({
|
|
26793
|
+
id: input.id,
|
|
26794
|
+
base: {
|
|
26795
|
+
headSha: session.workspace.headSha,
|
|
26796
|
+
worktreeDigest: session.snapshot.worktreeDigest,
|
|
26797
|
+
modelDigest: model.modelDigest
|
|
26798
|
+
},
|
|
26799
|
+
reason: { taskSessionId: input.id },
|
|
26800
|
+
operations
|
|
26801
|
+
}));
|
|
26802
|
+
await this.changeSetEngine.apply(root, draft, { approved: true });
|
|
26803
|
+
}
|
|
26804
|
+
async applyArchitectureProjectionRollbackChangeSet(root, projectedFiles, currentFiles, createdAt) {
|
|
26805
|
+
const targetPaths = new Set(projectedFiles.map((file) => file.path));
|
|
26806
|
+
const backupBase = `.archcontext/backups/ledger-rollback/${safePathSegment2(createdAt)}`;
|
|
26807
|
+
const backupRelativePath = uniqueBackupPath2(root, backupBase);
|
|
26808
|
+
const manifestPath = `${backupRelativePath}/manifest.json`;
|
|
26809
|
+
const { backup, manifest } = architectureProjectionRollbackBackup2(currentFiles, {
|
|
26810
|
+
createdAt,
|
|
26811
|
+
path: backupRelativePath,
|
|
26812
|
+
manifestPath
|
|
26813
|
+
});
|
|
26814
|
+
const removedPaths = currentFiles.filter((file) => !targetPaths.has(file.path)).map((file) => file.path);
|
|
26815
|
+
await this.applyArchitectureProjectionChangeSet(root, {
|
|
26816
|
+
id: `changeset.ledger-rollback-${shortDigest4(digestJson({ createdAt, projectionDigest: architectureLedgerProjectionDigest(projectedFiles) }))}`,
|
|
26817
|
+
files: [
|
|
26818
|
+
...currentFiles.map((file) => ({
|
|
26819
|
+
path: join9(backupRelativePath, archContextRelativePath2(file.path)),
|
|
26820
|
+
body: file.body
|
|
26821
|
+
})),
|
|
26822
|
+
{ path: manifestPath, body: `${JSON.stringify(manifest, null, 2)}
|
|
26823
|
+
` },
|
|
26824
|
+
...projectedFiles.map(({ path, body }) => ({ path, body }))
|
|
26825
|
+
],
|
|
26826
|
+
removedPaths
|
|
26827
|
+
});
|
|
26828
|
+
return {
|
|
26829
|
+
backup,
|
|
26830
|
+
writtenPaths: projectedFiles.map((file) => file.path),
|
|
26831
|
+
removedPaths
|
|
26832
|
+
};
|
|
26833
|
+
}
|
|
26272
26834
|
async ledgerState(root) {
|
|
26273
26835
|
this.assertRunning();
|
|
26274
26836
|
return okEnvelope("ledger.state", await this.architectureLedgerReadback(root));
|
|
@@ -26622,8 +27184,13 @@ class ArchctxDaemon2 {
|
|
|
26622
27184
|
const scope = await this.architectureLedgerScope(root);
|
|
26623
27185
|
const state = await this.localStore.readArchitectureLedgerState(scope);
|
|
26624
27186
|
const projectedFiles = projectArchitectureLedgerStateToYamlFiles(state);
|
|
26625
|
-
if (writes)
|
|
26626
|
-
|
|
27187
|
+
if (writes) {
|
|
27188
|
+
await this.applyArchitectureProjectionChangeSet(root, {
|
|
27189
|
+
id: `changeset.ledger-project-${shortDigest4(architectureLedgerProjectionDigest(projectedFiles))}`,
|
|
27190
|
+
files: projectedFiles.map(({ path, body }) => ({ path, body })),
|
|
27191
|
+
removedPaths: []
|
|
27192
|
+
});
|
|
27193
|
+
}
|
|
26627
27194
|
const drift = compareArchitectureLedgerStateToYaml({
|
|
26628
27195
|
state,
|
|
26629
27196
|
files: listModelFiles(root),
|
|
@@ -26773,7 +27340,7 @@ class ArchctxDaemon2 {
|
|
|
26773
27340
|
const projectedFiles = projectArchitectureLedgerStateToYamlFiles(state);
|
|
26774
27341
|
const currentManagedFiles = listModelFiles(root).filter((file) => isArchitectureLedgerManagedModelPath2(file.path));
|
|
26775
27342
|
const backupPlan = architectureProjectionRollbackBackup2(currentManagedFiles);
|
|
26776
|
-
const writeResult = writes ?
|
|
27343
|
+
const writeResult = writes ? await this.applyArchitectureProjectionRollbackChangeSet(root, projectedFiles, currentManagedFiles, this.clock()) : { backup: backupPlan.backup, writtenPaths: [], removedPaths: [] };
|
|
26777
27344
|
const drift = compareArchitectureLedgerStateToYaml({
|
|
26778
27345
|
state,
|
|
26779
27346
|
files: listModelFiles(root),
|
|
@@ -26812,9 +27379,12 @@ class ArchctxDaemon2 {
|
|
|
26812
27379
|
return errorEnvelope("ledger.rebuild", "AC_SCHEMA_INVALID", "ledger rebuild currently requires --from-git");
|
|
26813
27380
|
return this.withWriter(async () => {
|
|
26814
27381
|
this.assertFreshWorktree(root, input.expectedWorktreeDigest, "ledger rebuild --from-git");
|
|
26815
|
-
const scope = await this.
|
|
27382
|
+
const scope = await this.architectureLedgerGitScope(root);
|
|
26816
27383
|
const files = listModelFiles(root);
|
|
26817
|
-
const
|
|
27384
|
+
const exactReplay = await this.localStore.replayArchitectureLedger(scope);
|
|
27385
|
+
const exactScopeHasEvents = exactReplay.events.length > 0;
|
|
27386
|
+
const authorityScope = exactScopeHasEvents ? scope : await this.localStore.resolveLatestArchitectureLedgerScope(scope);
|
|
27387
|
+
const previousState = exactScopeHasEvents ? exactReplay.state : await this.localStore.readArchitectureLedgerState(authorityScope);
|
|
26818
27388
|
const previousGraphDigest = architectureLedgerStateDigest(previousState);
|
|
26819
27389
|
const rebuildCommand = input.acceptExternalProjection ? "archctx ledger rebuild --from-git --accept-external-projection" : "archctx ledger rebuild --from-git";
|
|
26820
27390
|
const plan = planYamlToArchitectureLedgerRebuild({
|
|
@@ -26824,12 +27394,18 @@ class ArchctxDaemon2 {
|
|
|
26824
27394
|
command: rebuildCommand,
|
|
26825
27395
|
previousState
|
|
26826
27396
|
});
|
|
27397
|
+
const importPlan = planYamlToArchitectureLedgerImport({
|
|
27398
|
+
...scope,
|
|
27399
|
+
files,
|
|
27400
|
+
createdAt: this.clock(),
|
|
27401
|
+
command: rebuildCommand
|
|
27402
|
+
});
|
|
26827
27403
|
if (plan.unsupportedFiles.length > 0) {
|
|
26828
27404
|
return errorEnvelope("ledger.rebuild", "AC_SCHEMA_INVALID", "ledger rebuild requires supported YAML model files");
|
|
26829
27405
|
}
|
|
26830
27406
|
const cursor = architectureLedgerGitCursorFromPlan({ ...scope, plan });
|
|
26831
27407
|
const previousCursor = await this.localStore.readArchitectureLedgerSourceCursor({
|
|
26832
|
-
...
|
|
27408
|
+
...authorityScope,
|
|
26833
27409
|
cursorId: ARCHITECTURE_LEDGER_GIT_CURSOR_ID
|
|
26834
27410
|
});
|
|
26835
27411
|
const cursorChanged = previousCursor?.cursorDigest !== cursor.cursorDigest;
|
|
@@ -26845,7 +27421,13 @@ class ArchctxDaemon2 {
|
|
|
26845
27421
|
let rebuildStatus = "unchanged";
|
|
26846
27422
|
let proposedExternalProjectionChange;
|
|
26847
27423
|
if (previousGraphDigest === plan.graphDigest) {
|
|
26848
|
-
if (
|
|
27424
|
+
if (!exactScopeHasEvents) {
|
|
27425
|
+
append = await this.localStore.appendArchitectureEvents({
|
|
27426
|
+
writer: "runtime-daemon",
|
|
27427
|
+
events: [importPlan.event]
|
|
27428
|
+
});
|
|
27429
|
+
rebuildStatus = isEmptyArchitectureLedgerState2(previousState) ? "rebuilt" : "cursor-refreshed";
|
|
27430
|
+
} else if (cursorChanged) {
|
|
26849
27431
|
const cursorPlan = planGitCursorRefreshToArchitectureLedgerEvent({
|
|
26850
27432
|
...scope,
|
|
26851
27433
|
cursor,
|
|
@@ -26861,7 +27443,7 @@ class ArchctxDaemon2 {
|
|
|
26861
27443
|
}
|
|
26862
27444
|
} else if (!previousStateEmpty && !input.acceptExternalProjection) {
|
|
26863
27445
|
const proposal = planExternalProjectionChangeToArchitectureLedgerEvent({
|
|
26864
|
-
...
|
|
27446
|
+
...authorityScope,
|
|
26865
27447
|
files,
|
|
26866
27448
|
createdAt: this.clock(),
|
|
26867
27449
|
command: rebuildCommand,
|
|
@@ -26884,11 +27466,12 @@ class ArchctxDaemon2 {
|
|
|
26884
27466
|
} else {
|
|
26885
27467
|
append = await this.localStore.appendArchitectureEvents({
|
|
26886
27468
|
writer: "runtime-daemon",
|
|
26887
|
-
events: [plan.event]
|
|
27469
|
+
events: [exactScopeHasEvents ? plan.event : importPlan.event]
|
|
26888
27470
|
});
|
|
26889
27471
|
rebuildStatus = previousStateEmpty ? "rebuilt" : "external-projection-accepted";
|
|
26890
27472
|
}
|
|
26891
|
-
const
|
|
27473
|
+
const replayScope = rebuildStatus === "external-projection-proposed" ? authorityScope : scope;
|
|
27474
|
+
const replay = await this.localStore.rebuildArchitectureLedgerCurrentState(replayScope);
|
|
26892
27475
|
const drift = compareArchitectureLedgerStateToYaml({
|
|
26893
27476
|
state: replay.state,
|
|
26894
27477
|
files: listModelFiles(root),
|
|
@@ -27034,6 +27617,9 @@ class ArchctxDaemon2 {
|
|
|
27034
27617
|
return this.running ? this.architectureLedgerContextPort(root) : undefined;
|
|
27035
27618
|
}
|
|
27036
27619
|
async architectureLedgerScope(root) {
|
|
27620
|
+
return this.localStore.resolveArchitectureLedgerScope(await this.architectureLedgerGitScope(root));
|
|
27621
|
+
}
|
|
27622
|
+
async architectureLedgerGitScope(root) {
|
|
27037
27623
|
const session = await this.openSession(root);
|
|
27038
27624
|
const paths = runtimeStatePaths2(root);
|
|
27039
27625
|
return {
|
|
@@ -27096,7 +27682,7 @@ class ArchctxDaemon2 {
|
|
|
27096
27682
|
cleanup: "remove-run-root"
|
|
27097
27683
|
}
|
|
27098
27684
|
};
|
|
27099
|
-
if (
|
|
27685
|
+
if (existsSync14(paths.lockPath) || existsSync14(paths.manifestPath)) {
|
|
27100
27686
|
removePathWithRetry(paths.runRoot);
|
|
27101
27687
|
throw new Error(`developer-review-run-already-active: ${input.challenge.challengeId}`);
|
|
27102
27688
|
}
|
|
@@ -27153,7 +27739,7 @@ class ArchctxDaemon2 {
|
|
|
27153
27739
|
const errors = [];
|
|
27154
27740
|
if (run.worktree) {
|
|
27155
27741
|
try {
|
|
27156
|
-
const hadWorktree =
|
|
27742
|
+
const hadWorktree = existsSync14(run.worktree.worktreeRoot);
|
|
27157
27743
|
removeDetachedReviewWorktree(run.worktree);
|
|
27158
27744
|
if (hadWorktree)
|
|
27159
27745
|
removed.push("worktree");
|
|
@@ -27167,7 +27753,7 @@ class ArchctxDaemon2 {
|
|
|
27167
27753
|
["lock", run.lockPath]
|
|
27168
27754
|
]) {
|
|
27169
27755
|
try {
|
|
27170
|
-
const existed =
|
|
27756
|
+
const existed = existsSync14(path);
|
|
27171
27757
|
removePathWithRetry(path);
|
|
27172
27758
|
if (existed)
|
|
27173
27759
|
removed.push(kind);
|
|
@@ -27196,7 +27782,7 @@ class ArchctxDaemon2 {
|
|
|
27196
27782
|
removedLocks: [],
|
|
27197
27783
|
skippedActive: []
|
|
27198
27784
|
};
|
|
27199
|
-
if (!
|
|
27785
|
+
if (!existsSync14(stateDir))
|
|
27200
27786
|
return recovery;
|
|
27201
27787
|
for (const entry of readdirSync9(stateDir).sort()) {
|
|
27202
27788
|
if (!entry.endsWith(".json"))
|
|
@@ -27564,7 +28150,7 @@ class ArchctxDaemon2 {
|
|
|
27564
28150
|
}
|
|
27565
28151
|
async restoreRepositorySessions() {
|
|
27566
28152
|
for (const record of await this.localStore.listRepositorySessions()) {
|
|
27567
|
-
if (!record.root || !
|
|
28153
|
+
if (!record.root || !existsSync14(record.root))
|
|
27568
28154
|
continue;
|
|
27569
28155
|
if (repositoryFingerprint2(record.root) !== record.repositoryId)
|
|
27570
28156
|
continue;
|
|
@@ -28470,14 +29056,14 @@ function expandWorkspacePackageJson2(root, pattern) {
|
|
|
28470
29056
|
return [];
|
|
28471
29057
|
if (!pattern.includes("*")) {
|
|
28472
29058
|
const path = resolve16(root, pattern, "package.json");
|
|
28473
|
-
return
|
|
29059
|
+
return existsSync14(path) ? [path] : [];
|
|
28474
29060
|
}
|
|
28475
29061
|
if (!pattern.endsWith("/*"))
|
|
28476
29062
|
return [];
|
|
28477
29063
|
const base = resolve16(root, pattern.slice(0, -2));
|
|
28478
|
-
if (!
|
|
29064
|
+
if (!existsSync14(base) || !statSync7(base).isDirectory())
|
|
28479
29065
|
return [];
|
|
28480
|
-
return readdirSync9(base, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => resolve16(base, entry.name, "package.json")).filter((path) =>
|
|
29066
|
+
return readdirSync9(base, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => resolve16(base, entry.name, "package.json")).filter((path) => existsSync14(path));
|
|
28481
29067
|
}
|
|
28482
29068
|
function exactVersionFromManifest2(manifest, packageName) {
|
|
28483
29069
|
if (!manifest || typeof manifest !== "object" || Array.isArray(manifest))
|
|
@@ -28493,7 +29079,7 @@ function exactVersionFromManifest2(manifest, packageName) {
|
|
|
28493
29079
|
return;
|
|
28494
29080
|
}
|
|
28495
29081
|
function readJsonFile2(path) {
|
|
28496
|
-
if (!
|
|
29082
|
+
if (!existsSync14(path))
|
|
28497
29083
|
return;
|
|
28498
29084
|
try {
|
|
28499
29085
|
return JSON.parse(readFileSync12(path, "utf8"));
|
|
@@ -28506,7 +29092,7 @@ function isExactPackageVersion2(value) {
|
|
|
28506
29092
|
}
|
|
28507
29093
|
function readContext7Lockfile2(root) {
|
|
28508
29094
|
const path = resolve16(root, CONTEXT7_LOCKFILE2);
|
|
28509
|
-
if (!
|
|
29095
|
+
if (!existsSync14(path)) {
|
|
28510
29096
|
return {
|
|
28511
29097
|
schemaVersion: CONTEXT7_LOCKFILE_SCHEMA_VERSION,
|
|
28512
29098
|
provider: "context7",
|
|
@@ -28735,48 +29321,9 @@ function auditApproveResultPayload2(runId, status, totalCount, issuedIssues) {
|
|
|
28735
29321
|
issuedIssues
|
|
28736
29322
|
};
|
|
28737
29323
|
}
|
|
28738
|
-
function
|
|
28739
|
-
|
|
28740
|
-
|
|
28741
|
-
mkdirSync8(dirname9(absolute), { recursive: true });
|
|
28742
|
-
writeFileSync7(absolute, file.body.endsWith(`
|
|
28743
|
-
`) ? file.body : `${file.body}
|
|
28744
|
-
`, "utf8");
|
|
28745
|
-
}
|
|
28746
|
-
}
|
|
28747
|
-
function replaceArchitectureProjectionFilesForYamlRollback2(root, projectedFiles, currentFiles, createdAt) {
|
|
28748
|
-
const targetPaths = new Set(projectedFiles.map((file) => file.path));
|
|
28749
|
-
const backupBase = `.archcontext/backups/ledger-rollback/${safePathSegment2(createdAt)}`;
|
|
28750
|
-
const backupRelativePath = uniqueBackupPath2(root, backupBase);
|
|
28751
|
-
const manifestPath = `${backupRelativePath}/manifest.json`;
|
|
28752
|
-
const { backup, manifest } = architectureProjectionRollbackBackup2(currentFiles, {
|
|
28753
|
-
createdAt,
|
|
28754
|
-
path: backupRelativePath,
|
|
28755
|
-
manifestPath
|
|
28756
|
-
});
|
|
28757
|
-
for (const file of currentFiles) {
|
|
28758
|
-
const backupPath = join9(backupRelativePath, archContextRelativePath2(file.path));
|
|
28759
|
-
const absolute = resolve16(root, backupPath);
|
|
28760
|
-
mkdirSync8(dirname9(absolute), { recursive: true });
|
|
28761
|
-
writeFileSync7(absolute, file.body, "utf8");
|
|
28762
|
-
}
|
|
28763
|
-
const manifestAbsolute = resolve16(root, manifestPath);
|
|
28764
|
-
mkdirSync8(dirname9(manifestAbsolute), { recursive: true });
|
|
28765
|
-
writeFileSync7(manifestAbsolute, `${JSON.stringify(manifest, null, 2)}
|
|
28766
|
-
`, "utf8");
|
|
28767
|
-
const removedPaths = [];
|
|
28768
|
-
for (const file of currentFiles) {
|
|
28769
|
-
if (targetPaths.has(file.path))
|
|
28770
|
-
continue;
|
|
28771
|
-
rmSync9(resolve16(root, file.path), { force: true });
|
|
28772
|
-
removedPaths.push(file.path);
|
|
28773
|
-
}
|
|
28774
|
-
writeArchitectureProjectionFiles2(root, projectedFiles);
|
|
28775
|
-
return {
|
|
28776
|
-
backup,
|
|
28777
|
-
writtenPaths: projectedFiles.map((file) => file.path),
|
|
28778
|
-
removedPaths
|
|
28779
|
-
};
|
|
29324
|
+
function expectedFileHash2(root, path) {
|
|
29325
|
+
const absolute = resolve16(root, path);
|
|
29326
|
+
return existsSync14(absolute) ? digestJson({ body: readFileSync12(absolute, "utf8") }) : "missing";
|
|
28780
29327
|
}
|
|
28781
29328
|
function architectureProjectionRollbackBackup2(files, options = {}) {
|
|
28782
29329
|
const manifest = {
|
|
@@ -28803,7 +29350,7 @@ function architectureProjectionRollbackBackup2(files, options = {}) {
|
|
|
28803
29350
|
function uniqueBackupPath2(root, backupBase) {
|
|
28804
29351
|
let candidate = backupBase;
|
|
28805
29352
|
let suffix = 2;
|
|
28806
|
-
while (
|
|
29353
|
+
while (existsSync14(resolve16(root, candidate))) {
|
|
28807
29354
|
candidate = `${backupBase}-${suffix}`;
|
|
28808
29355
|
suffix += 1;
|
|
28809
29356
|
}
|
|
@@ -28812,7 +29359,7 @@ function uniqueBackupPath2(root, backupBase) {
|
|
|
28812
29359
|
function uniqueRuntimeBackupPath2(backupBase) {
|
|
28813
29360
|
let candidate = backupBase;
|
|
28814
29361
|
let suffix = 2;
|
|
28815
|
-
while (
|
|
29362
|
+
while (existsSync14(candidate)) {
|
|
28816
29363
|
candidate = backupBase.replace(/\.sqlite$/, `-${suffix}.sqlite`);
|
|
28817
29364
|
suffix += 1;
|
|
28818
29365
|
}
|
|
@@ -28874,7 +29421,7 @@ function parsePracticeCheckpointBaselineState2(state, repositoryId, taskSessionI
|
|
|
28874
29421
|
return record;
|
|
28875
29422
|
}
|
|
28876
29423
|
function completeTaskProjectionDrift2(root) {
|
|
28877
|
-
if (!
|
|
29424
|
+
if (!existsSync14(resolve16(root, "docs/architecture/.projection-manifest.json")))
|
|
28878
29425
|
return;
|
|
28879
29426
|
const loaded = loadArchitectureDocumentationInputs(root);
|
|
28880
29427
|
const sourceDigest = architectureDocumentationSourceDigest({
|
|
@@ -28991,6 +29538,7 @@ function writeHtml2(response, statusCode, body) {
|
|
|
28991
29538
|
}
|
|
28992
29539
|
|
|
28993
29540
|
// packages/surfaces/mcp-local/src/index.ts
|
|
29541
|
+
var MCP_PROTOCOL_VERSION = "2025-03-26";
|
|
28994
29542
|
var LOCAL_MCP_TOOLS = [
|
|
28995
29543
|
{
|
|
28996
29544
|
name: "archcontext_prepare_task",
|
|
@@ -29302,9 +29850,17 @@ async function runStdioMcpLoop(input, output, log = (line) => process.stderr.wri
|
|
|
29302
29850
|
log("[archctx-mcp] started");
|
|
29303
29851
|
for await (const line of input) {
|
|
29304
29852
|
const message = JSON.parse(line);
|
|
29853
|
+
if (message.id === undefined)
|
|
29854
|
+
continue;
|
|
29305
29855
|
let result;
|
|
29306
|
-
if (message.method === "
|
|
29856
|
+
if (message.method === "initialize") {
|
|
29857
|
+
result = mcpInitializeResult(message.params?.protocolVersion);
|
|
29858
|
+
} else if (message.method === "ping") {
|
|
29859
|
+
result = {};
|
|
29860
|
+
} else if (message.method === "tools/list") {
|
|
29307
29861
|
result = { tools: server.listTools() };
|
|
29862
|
+
} else if (message.method === "tools/call") {
|
|
29863
|
+
result = await server.callTool(message.params?.name, message.params?.arguments ?? {});
|
|
29308
29864
|
} else if (message.method === "resources/list") {
|
|
29309
29865
|
result = { resources: await server.listResources(message.params?.root) };
|
|
29310
29866
|
} else if (message.method === "resources/read") {
|
|
@@ -29314,11 +29870,28 @@ async function runStdioMcpLoop(input, output, log = (line) => process.stderr.wri
|
|
|
29314
29870
|
contents: content === undefined ? [] : [{ uri, mimeType: "application/json", text: JSON.stringify(content) }]
|
|
29315
29871
|
};
|
|
29316
29872
|
} else {
|
|
29317
|
-
result =
|
|
29873
|
+
result = {
|
|
29874
|
+
content: errorEnvelope("mcp", "AC_SCHEMA_INVALID", `Unknown MCP method: ${message.method}`),
|
|
29875
|
+
dataClassification: "local-metadata"
|
|
29876
|
+
};
|
|
29318
29877
|
}
|
|
29319
29878
|
output(JSON.stringify({ jsonrpc: "2.0", id: message.id, result }));
|
|
29320
29879
|
}
|
|
29321
29880
|
}
|
|
29881
|
+
function mcpInitializeResult(clientProtocolVersion) {
|
|
29882
|
+
const product = productVersionManifest().product;
|
|
29883
|
+
return {
|
|
29884
|
+
protocolVersion: typeof clientProtocolVersion === "string" ? clientProtocolVersion : MCP_PROTOCOL_VERSION,
|
|
29885
|
+
capabilities: {
|
|
29886
|
+
tools: { listChanged: false },
|
|
29887
|
+
resources: { subscribe: false, listChanged: false }
|
|
29888
|
+
},
|
|
29889
|
+
serverInfo: {
|
|
29890
|
+
name: product.name,
|
|
29891
|
+
version: product.version
|
|
29892
|
+
}
|
|
29893
|
+
};
|
|
29894
|
+
}
|
|
29322
29895
|
function isExternalDocumentationResourceUri(uri) {
|
|
29323
29896
|
return /^archcontext:\/\/external-docs\/context7\/sha256:[0-9a-f]{64}$/.test(uri);
|
|
29324
29897
|
}
|
|
@@ -29365,6 +29938,8 @@ if (__require.main == __require.module) {
|
|
|
29365
29938
|
`);
|
|
29366
29939
|
if (result.ok === false)
|
|
29367
29940
|
process.exitCode = 1;
|
|
29941
|
+
if (command === "resolve")
|
|
29942
|
+
process.exitCode = resolveCommandExitCode(result);
|
|
29368
29943
|
}
|
|
29369
29944
|
}
|
|
29370
29945
|
async function* stdinLines() {
|
|
@@ -29621,6 +30196,37 @@ async function runCliUnchecked(command2 = "help", args2 = [], cwd, deps = {}) {
|
|
|
29621
30196
|
revocation: "archctx tunnel --revoke"
|
|
29622
30197
|
}
|
|
29623
30198
|
};
|
|
30199
|
+
case "resolve": {
|
|
30200
|
+
const path = readFlag(args2, "--path");
|
|
30201
|
+
if (!path)
|
|
30202
|
+
return errorEnvelope("resolve", "AC_SCHEMA_INVALID", "resolve requires --path");
|
|
30203
|
+
if (!isRepoRelativePosixPath(path)) {
|
|
30204
|
+
return errorEnvelope("resolve", "AC_SCHEMA_INVALID", "resolve --path must be a repository-relative POSIX path");
|
|
30205
|
+
}
|
|
30206
|
+
const model = loadNativeModelFromArchContext(cwd);
|
|
30207
|
+
const outcome = resolveArchitectureOwnerForPath(model.nodes, path);
|
|
30208
|
+
if (outcome.status === "matched") {
|
|
30209
|
+
const node = outcome.node;
|
|
30210
|
+
return okEnvelope("resolve", {
|
|
30211
|
+
matched: true,
|
|
30212
|
+
ambiguous: false,
|
|
30213
|
+
stableId: node.id,
|
|
30214
|
+
kind: node.kind,
|
|
30215
|
+
name: node.name,
|
|
30216
|
+
...node.source ? { source: node.source } : {},
|
|
30217
|
+
...node.extensions ? { extensions: node.extensions } : {}
|
|
30218
|
+
});
|
|
30219
|
+
}
|
|
30220
|
+
if (outcome.status === "ambiguous") {
|
|
30221
|
+
return okEnvelope("resolve", {
|
|
30222
|
+
matched: false,
|
|
30223
|
+
ambiguous: true,
|
|
30224
|
+
path,
|
|
30225
|
+
candidates: outcome.candidates.map((node) => node.id)
|
|
30226
|
+
});
|
|
30227
|
+
}
|
|
30228
|
+
return okEnvelope("resolve", { matched: false, ambiguous: false, path });
|
|
30229
|
+
}
|
|
29624
30230
|
case "help":
|
|
29625
30231
|
default:
|
|
29626
30232
|
return {
|
|
@@ -29628,8 +30234,8 @@ async function runCliUnchecked(command2 = "help", args2 = [], cwd, deps = {}) {
|
|
|
29628
30234
|
ok: true,
|
|
29629
30235
|
requestId: "help",
|
|
29630
30236
|
data: {
|
|
29631
|
-
commands: ["init", "sync", "validate", "context", "status", "daemon", "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", "tunnel"],
|
|
29632
|
-
examples: ["archctx init --name MyApp", "archctx ledger migrate --from-yaml --dry-run", "archctx ledger promote --mode authoritative --preflight --rollback-plan", "archctx book recommendations --open --explain", "archctx recommendations accept --id recommendation.<id> --reason 'Accepted after local readback.'", "archctx recommendations metrics", "archctx practices validate --strict", "archctx practices list --json", "archctx practices waivers", "archctx practices waive --practice-id modularity.no-new-cycle --owner team-architecture --reason 'External migration window requires this edge until cutover.' --review-at 2026-07-10T00:00:00.000Z --expires-at 2026-07-24T00:00:00.000Z --evidence-digest sha256:<64-hex> --subject module.a->module.b", "archctx checkpoint --task-session-id task_cli", "archctx investigate --runner-port codex", "archctx agents status --status queued,running", "archctx agents budget", "archctx hook enqueue --event post-edit --path src/app.ts", "archctx jobs list --status queued", "archctx audit run --reason 'quarterly architecture audit'", "archctx audit run --no-wait", "archctx audit list --status pending", "archctx audit show audit_run.<id>", "archctx audit approve audit_run.<id>", "archctx audit approve audit_run.<id> --confirm-public-repo public:<owner/repo>:<baseSha>:<runId>", "archctx audit approve audit_run.<id> --resume", "archctx hooks install --host codex", "archctx paths", "archctx update --check", "archctx doctor --check-updates", "archctx github connect", "archctx github status", "archctx daemon start", "archctx explore start --foreground", "archctx export likec4", "archctx import structurizr --content '<json>'", "archctx tunnel"]
|
|
30237
|
+
commands: ["init", "sync", "validate", "context", "status", "daemon", "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"],
|
|
30238
|
+
examples: ["archctx init --name MyApp", "archctx ledger migrate --from-yaml --dry-run", "archctx ledger promote --mode authoritative --preflight --rollback-plan", "archctx book recommendations --open --explain", "archctx recommendations accept --id recommendation.<id> --reason 'Accepted after local readback.'", "archctx recommendations metrics", "archctx practices validate --strict", "archctx practices list --json", "archctx practices waivers", "archctx practices waive --practice-id modularity.no-new-cycle --owner team-architecture --reason 'External migration window requires this edge until cutover.' --review-at 2026-07-10T00:00:00.000Z --expires-at 2026-07-24T00:00:00.000Z --evidence-digest sha256:<64-hex> --subject module.a->module.b", "archctx checkpoint --task-session-id task_cli", "archctx investigate --runner-port codex", "archctx agents status --status queued,running", "archctx agents budget", "archctx hook enqueue --event post-edit --path src/app.ts", "archctx jobs list --status queued", "archctx audit run --reason 'quarterly architecture audit'", "archctx audit run --no-wait", "archctx audit list --status pending", "archctx audit show audit_run.<id>", "archctx audit approve audit_run.<id>", "archctx audit approve audit_run.<id> --confirm-public-repo public:<owner/repo>:<baseSha>:<runId>", "archctx audit approve audit_run.<id> --resume", "archctx hooks install --host codex", "archctx paths", "archctx update --check", "archctx doctor --check-updates", "archctx github connect", "archctx github status", "archctx daemon start", "archctx explore start --foreground", "archctx export likec4", "archctx import structurizr --content '<json>'", "archctx resolve --path packages/core/projection-engine/src/index.ts", "archctx tunnel"]
|
|
29633
30239
|
}
|
|
29634
30240
|
};
|
|
29635
30241
|
}
|
|
@@ -30074,37 +30680,10 @@ function buildArchitectureDocsProjection(root, generatedAt) {
|
|
|
30074
30680
|
sourceDigest,
|
|
30075
30681
|
generatedAt
|
|
30076
30682
|
});
|
|
30077
|
-
const manifestBody = `${JSON.stringify({
|
|
30078
|
-
schemaVersion: "archcontext.architecture-docs-projection-manifest/v1",
|
|
30079
|
-
rendererVersion: plan.rendererVersion,
|
|
30080
|
-
sourceDigest: plan.sourceDigest,
|
|
30081
|
-
projectionDigest: plan.projectionDigest,
|
|
30082
|
-
targetCount: plan.targets.length,
|
|
30083
|
-
fileCount: plan.files.length,
|
|
30084
|
-
targets: plan.targets.map((target) => ({
|
|
30085
|
-
targetId: target.targetId,
|
|
30086
|
-
type: target.type,
|
|
30087
|
-
scope: target.scope,
|
|
30088
|
-
path: target.path,
|
|
30089
|
-
ownership: target.ownership,
|
|
30090
|
-
rendererVersion: target.rendererVersion,
|
|
30091
|
-
format: target.format,
|
|
30092
|
-
sourceDigest: target.sourceDigest,
|
|
30093
|
-
outputDigest: target.outputDigest
|
|
30094
|
-
}))
|
|
30095
|
-
}, null, 2)}
|
|
30096
|
-
`;
|
|
30097
|
-
const manifest = {
|
|
30098
|
-
path: "docs/architecture/.projection-manifest.json",
|
|
30099
|
-
body: manifestBody,
|
|
30100
|
-
digest: digestJson({ path: "docs/architecture/.projection-manifest.json", body: manifestBody }),
|
|
30101
|
-
target: plan.targets[0],
|
|
30102
|
-
generatedBodyDigest: digestJson({ body: manifestBody })
|
|
30103
|
-
};
|
|
30104
30683
|
return {
|
|
30105
30684
|
plan,
|
|
30106
|
-
manifest,
|
|
30107
|
-
files: [...plan.files, manifest]
|
|
30685
|
+
manifest: plan.manifest,
|
|
30686
|
+
files: [...plan.files, plan.manifest]
|
|
30108
30687
|
};
|
|
30109
30688
|
}
|
|
30110
30689
|
function architectureDocsRenderProjectionOperation(root, files) {
|
|
@@ -30120,7 +30699,7 @@ function architectureDocsRenderProjectionOperation(root, files) {
|
|
|
30120
30699
|
}
|
|
30121
30700
|
function currentBodyHash(root, path) {
|
|
30122
30701
|
const absolute = resolve17(root, path);
|
|
30123
|
-
return
|
|
30702
|
+
return existsSync15(absolute) ? digestJson({ body: readFileSync13(absolute, "utf8") }) : "missing";
|
|
30124
30703
|
}
|
|
30125
30704
|
async function runPracticesCommand(args2, cwd, daemon) {
|
|
30126
30705
|
const subcommand = args2[0] ?? "list";
|
|
@@ -30468,7 +31047,7 @@ function auditManifestGateRoot(cwd) {
|
|
|
30468
31047
|
}
|
|
30469
31048
|
function auditGithubIssuesEnabled(cwd) {
|
|
30470
31049
|
const manifestPath = resolve17(auditManifestGateRoot(cwd), ".archcontext/manifest.yaml");
|
|
30471
|
-
if (!
|
|
31050
|
+
if (!existsSync15(manifestPath))
|
|
30472
31051
|
return false;
|
|
30473
31052
|
let raw;
|
|
30474
31053
|
try {
|
|
@@ -31081,7 +31660,7 @@ async function writeGithubDeveloperReviewState(cwd, state) {
|
|
|
31081
31660
|
return { state, path };
|
|
31082
31661
|
}
|
|
31083
31662
|
function readGithubDeveloperReviewState(path) {
|
|
31084
|
-
if (!
|
|
31663
|
+
if (!existsSync15(path))
|
|
31085
31664
|
return;
|
|
31086
31665
|
try {
|
|
31087
31666
|
const parsed = JSON.parse(readFileSync13(path, "utf8"));
|
|
@@ -31133,7 +31712,7 @@ function defaultGithubConnectionPath(cwd) {
|
|
|
31133
31712
|
return join10(dirname10(defaultDaemonConnectionPath(cwd)), "github-connection.json");
|
|
31134
31713
|
}
|
|
31135
31714
|
function readGithubConnection(path) {
|
|
31136
|
-
if (!
|
|
31715
|
+
if (!existsSync15(path))
|
|
31137
31716
|
return;
|
|
31138
31717
|
try {
|
|
31139
31718
|
const parsed = JSON.parse(readFileSync13(path, "utf8"));
|
|
@@ -31647,10 +32226,10 @@ function doctorSqlite(cwd) {
|
|
|
31647
32226
|
const legacyLocalStore = inspectLegacyLocalStoreMigration(cwd);
|
|
31648
32227
|
return {
|
|
31649
32228
|
path,
|
|
31650
|
-
exists:
|
|
32229
|
+
exists: existsSync15(path),
|
|
31651
32230
|
migrations: productVersionManifest().runtime.sqliteMigrations,
|
|
31652
32231
|
legacyPath: paths.legacyLocalStorePath,
|
|
31653
|
-
legacyExists:
|
|
32232
|
+
legacyExists: existsSync15(paths.legacyLocalStorePath),
|
|
31654
32233
|
legacyLocalStore
|
|
31655
32234
|
};
|
|
31656
32235
|
}
|
|
@@ -31681,7 +32260,7 @@ function runtimePathsReport(cwd) {
|
|
|
31681
32260
|
};
|
|
31682
32261
|
}
|
|
31683
32262
|
function pathAccess(path) {
|
|
31684
|
-
const exists =
|
|
32263
|
+
const exists = existsSync15(path);
|
|
31685
32264
|
return {
|
|
31686
32265
|
path,
|
|
31687
32266
|
exists,
|
|
@@ -32164,6 +32743,16 @@ function renderResult(result, format) {
|
|
|
32164
32743
|
return `OK ${result.requestId}
|
|
32165
32744
|
${JSON.stringify(result.data, null, 2)}`;
|
|
32166
32745
|
}
|
|
32746
|
+
function resolveCommandExitCode(result) {
|
|
32747
|
+
if (!result || result.ok !== true)
|
|
32748
|
+
return 1;
|
|
32749
|
+
const data = result.data;
|
|
32750
|
+
if (data?.matched === true)
|
|
32751
|
+
return 0;
|
|
32752
|
+
if (data?.ambiguous === true)
|
|
32753
|
+
return 2;
|
|
32754
|
+
return 1;
|
|
32755
|
+
}
|
|
32167
32756
|
function readFlag(args2, flag) {
|
|
32168
32757
|
const index = args2.indexOf(flag);
|
|
32169
32758
|
if (index === -1)
|
|
@@ -32193,5 +32782,6 @@ function readRepeatedFlag(args2, flag) {
|
|
|
32193
32782
|
}
|
|
32194
32783
|
export {
|
|
32195
32784
|
runForegroundDaemon,
|
|
32196
|
-
runCli
|
|
32785
|
+
runCli,
|
|
32786
|
+
resolveCommandExitCode
|
|
32197
32787
|
};
|