archctx 0.2.1 → 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/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.1", 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";
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 existsSync14, mkdirSync as mkdirSync9, openSync as openSync7, readFileSync as readFileSync13, rmSync as rmSync10, statSync as statSync8, writeFileSync as writeFileSync8 } from "fs";
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: sourceDigest,
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
- ...entity.path ? { path: entity.path } : {},
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 body = canonicalArchitectureYaml({
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
- kind: constraint.kind,
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: stringField(value, "kind") ?? "component",
8457
- canonicalName: stringField(value, "name") ?? stringField(value, "canonicalName") ?? entityId,
8458
- status: activeStatus(value.status),
8459
- ...stringField(value, "path") ? { path: stringField(value, "path") } : {},
8460
- ...stringField(value, "summary") ? { summary: stringField(value, "summary") } : {},
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: stringField(value, "kind") ?? "depends_on",
8466
+ kind: requireStringField(value, "kind", path),
8470
8467
  sourceEntityId: requireStringField(value, "source", path),
8471
8468
  targetEntityId: requireStringField(value, "target", path),
8472
- status: activeStatus(value.status),
8473
- ...stringField(value, "summary") ?? stringField(value, "intent") ? { summary: stringField(value, "summary") ?? stringField(value, "intent") } : {},
8474
- ...metadataField(value, ["schemaVersion", "id", "kind", "source", "target", "status", "summary", "intent"])
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: stringField(value, "kind") ?? "depends_on",
8481
+ kind: requireStringField(value, "kind", path),
8485
8482
  sourceEntityId: source,
8486
8483
  targetEntityId: target,
8487
- status: activeStatus(value.status),
8488
- ...stringField(value, "intent") ? { summary: stringField(value, "intent") } : {},
8489
- ...metadataField(value, ["schemaVersion", "id", "kind", "source", "target", "status", "intent"])
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: stringField(value, "kind") ?? "constraint",
8498
- subjectId: stringField(value, "subject") ?? stringField(value, "subjectId") ?? "repository",
8499
- status: activeStatus(value.status),
8500
- ...severityField(value.severity) ? { severity: severityField(value.severity) } : {},
8501
- ...stringField(value, "summary") ? { summary: stringField(value, "summary") } : {},
8502
- ...metadataField(value, ["schemaVersion", "id", "kind", "subject", "subjectId", "status", "severity", "summary"])
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 metadataField(value, omitted) {
8640
- const metadata = {};
8641
- const explicit = value.metadata;
8642
- if (isRecord(explicit)) {
8643
- for (const key of Object.keys(explicit).sort())
8644
- metadata[key] = explicit[key];
8645
- }
8646
- for (const key of Object.keys(value).sort()) {
8647
- if (omitted.includes(key) || key === "metadata")
8648
- continue;
8649
- metadata[key] = value[key];
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}`);
8650
8646
  }
8651
- return Object.keys(metadata).length === 0 ? {} : { metadata };
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 activeStatus(value) {
8685
- return value === "deprecated" || value === "removed" ? value : "active";
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 existsSync12, 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";
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 existsSync4, 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";
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
- this.rebuildGeneratedProjection(root, deps);
10190
- await this.validateModel(root, draft, deps);
10191
- await options.afterModelValidatedBeforeCommit?.({ root, draft, journalId });
10192
- if (journalId) {
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
- rebuildGeneratedProjection(root, deps) {
10214
- deps.projection.rebuildGeneratedProjection(root);
10215
- }
10216
- async validateModel(root, draft, deps) {
10217
- await deps.modelStore.validateModel({ root, repositoryId: draft.reason.taskSessionId, headSha: draft.base.headSha });
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 = existsSync4(absolute);
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 (existsSync4(backupPath))
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
- renameSync2(absolute, backupPath);
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 (existsSync4(absolute) && lstatSync2(absolute).isSymbolicLink()) {
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
- rmSync3(backup.path, { recursive: true, force: true });
10336
- if (backup.existed && existsSync4(backup.backupPath))
10337
- renameSync2(backup.backupPath, backup.path);
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 existsSync7, statSync as statSync5 } from "node:fs";
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 existsSync5, lstatSync as lstatSync3, readdirSync as readdirSync4, readFileSync as readFileSync6, realpathSync as realpathSync5 } from "node:fs";
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 (!existsSync5(overlayRoot))
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 (!existsSync5(dir))
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 realpathSync5.native(path);
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 existsSync6, lstatSync as lstatSync4, readdirSync as readdirSync5, readFileSync as readFileSync7 } from "node:fs";
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 (!existsSync6(path))
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 (!existsSync6(dir))
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 (existsSync6(modelDir)) {
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 (!existsSync7(absolute))
13772
+ if (!existsSync8(absolute))
13646
13773
  return "deleted";
13647
13774
  try {
13648
13775
  if (!statSync5(absolute).isFile())
@@ -14848,7 +14975,7 @@ function uniqueSorted4(values) {
14848
14975
 
14849
14976
  // packages/core/projection-engine/src/index.ts
14850
14977
  init_src();
14851
- import { existsSync as existsSync8, readdirSync as readdirSync6, readFileSync as readFileSync8 } from "node:fs";
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";
14853
14980
  function nativeNodeSource(node) {
14854
14981
  const value = node.source;
@@ -14891,12 +15018,6 @@ function renderArchitectureDocumentationProjection(input) {
14891
15018
  });
14892
15019
  const targets = rendered.map((file) => file.target);
14893
15020
  const expectedByPath = new Map(rendered.map((file) => [file.path, file]));
14894
- const drift = architectureDocumentationProjectionDrift({
14895
- targets,
14896
- expectedFiles: rendered,
14897
- existingFiles: input.existingFiles ?? []
14898
- });
14899
- const rejected = drift.diffs.filter((diff) => diff.reasonCode === "projection-ambiguous-ownership");
14900
15021
  const projectionDigest = digestJson({
14901
15022
  rendererVersion,
14902
15023
  sourceDigest: input.sourceDigest,
@@ -14907,6 +15028,38 @@ function renderArchitectureDocumentationProjection(input) {
14907
15028
  generatedBodyDigest: file.generatedBodyDigest
14908
15029
  })).sort((left, right) => left.path.localeCompare(right.path))
14909
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");
14910
15063
  return {
14911
15064
  schemaVersion: "archcontext.architecture-docs-projection-plan/v1",
14912
15065
  rendererVersion,
@@ -14914,6 +15067,7 @@ function renderArchitectureDocumentationProjection(input) {
14914
15067
  projectionDigest,
14915
15068
  targets,
14916
15069
  files: rendered.filter((file) => !rejected.some((diff) => diff.path === file.path && diff.targetId === file.target.targetId)),
15070
+ manifest,
14917
15071
  drift: {
14918
15072
  ...drift,
14919
15073
  diffs: drift.diffs.map((diff) => ({
@@ -14944,7 +15098,7 @@ function architectureDocumentationSourceDigest(input) {
14944
15098
  }
14945
15099
  function loadArchitectureDecisionRecords(root) {
14946
15100
  const dir = resolve11(root, "docs/adr");
14947
- if (!existsSync8(dir))
15101
+ if (!existsSync9(dir))
14948
15102
  return [];
14949
15103
  return readdirSync6(dir).filter((file) => /^ADR-\d{4}-.+\.md$/.test(file)).sort().map((file) => {
14950
15104
  const path = `docs/adr/${file}`;
@@ -14971,12 +15125,12 @@ function loadArchitectureDocumentationFiles(root) {
14971
15125
  "docs/architecture/.projection-manifest.json"
14972
15126
  ]) {
14973
15127
  const absolute = resolve11(root, entry);
14974
- if (existsSync8(absolute))
15128
+ if (existsSync9(absolute))
14975
15129
  files.push({ path: entry, body: readFileSync8(absolute, "utf8") });
14976
15130
  }
14977
15131
  for (const dir of ["docs/architecture/modules", "docs/architecture/relations"]) {
14978
15132
  const absoluteDir = resolve11(root, dir);
14979
- if (!existsSync8(absoluteDir))
15133
+ if (!existsSync9(absoluteDir))
14980
15134
  continue;
14981
15135
  for (const file of readdirSync6(absoluteDir).filter((name) => name.endsWith(".md")).sort()) {
14982
15136
  const path = `${dir}/${file}`;
@@ -15082,7 +15236,7 @@ function mermaidId(id) {
15082
15236
  return stableId(id).replace(/-/g, "_").replace(/\./g, "_");
15083
15237
  }
15084
15238
  function readYamlObjects(dir) {
15085
- if (!existsSync8(dir))
15239
+ if (!existsSync9(dir))
15086
15240
  return [];
15087
15241
  return readdirSync6(dir).filter((file) => /\.ya?ml$/.test(file)).sort().map((file) => {
15088
15242
  const path = resolve11(dir, file);
@@ -15317,6 +15471,34 @@ function architectureDocumentationProjectionDrift(input) {
15317
15471
  const expectedByPath = new Map(input.expectedFiles.map((file) => [file.path, file]));
15318
15472
  const targetIds = new Set(input.targets.map((target) => target.targetId));
15319
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
+ }
15320
15502
  for (const expected of input.expectedFiles) {
15321
15503
  const existing = existingByPath.get(expected.path);
15322
15504
  if (!existing) {
@@ -15355,6 +15537,8 @@ function architectureDocumentationProjectionDrift(input) {
15355
15537
  }
15356
15538
  }
15357
15539
  for (const existing of input.existingFiles) {
15540
+ if (existing.path === input.expectedManifest.path)
15541
+ continue;
15358
15542
  if (!isManagedArchitectureDocumentationPath(existing.path) || expectedByPath.has(existing.path))
15359
15543
  continue;
15360
15544
  const region = findAnyGeneratedRegion(existing.body);
@@ -15403,7 +15587,7 @@ function generatedEndMarker(targetId) {
15403
15587
  return `${ARCHITECTURE_DOCS_GENERATED_END_PREFIX} target="${targetId}" -->`;
15404
15588
  }
15405
15589
  function isManagedArchitectureDocumentationPath(path) {
15406
- 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";
15407
15591
  }
15408
15592
  function pathSegment2(id) {
15409
15593
  return stableId(id).replace(/\./g, "-");
@@ -16613,7 +16797,7 @@ init_src();
16613
16797
 
16614
16798
  // packages/local-runtime/git-adapter/src/index.ts
16615
16799
  import { execFileSync as execFileSync4, spawnSync as spawnSync2 } from "node:child_process";
16616
- import { existsSync as existsSync9, mkdirSync as mkdirSync4, mkdtempSync as mkdtempSync2, rmSync as rmSync4 } from "node:fs";
16800
+ import { existsSync as existsSync10, mkdirSync as mkdirSync4, mkdtempSync as mkdtempSync2, rmSync as rmSync4 } from "node:fs";
16617
16801
  import { dirname as dirname5, join as join5, resolve as resolve12 } from "node:path";
16618
16802
  import { tmpdir } from "node:os";
16619
16803
  init_src();
@@ -16627,7 +16811,7 @@ function findRepositoryRoot2(start) {
16627
16811
  } catch {
16628
16812
  let cursor = resolve12(start);
16629
16813
  while (true) {
16630
- if (existsSync9(resolve12(cursor, ".git")))
16814
+ if (existsSync10(resolve12(cursor, ".git")))
16631
16815
  return cursor;
16632
16816
  const parent = dirname5(cursor);
16633
16817
  if (parent === cursor)
@@ -16898,7 +17082,7 @@ function isGitWorktreeError(error) {
16898
17082
  // packages/local-runtime/local-store-sqlite/src/index.ts
16899
17083
  import { execFileSync as execFileSync5 } from "node:child_process";
16900
17084
  import { createHash as createHash6, randomUUID as randomUUID2 } from "node:crypto";
16901
- import { chmodSync as chmodSync2, closeSync as closeSync4, existsSync as existsSync10, fsyncSync as fsyncSync3, lstatSync as lstatSync5, mkdirSync as mkdirSync5, openSync as openSync4, readFileSync as readFileSync9, realpathSync as realpathSync6, renameSync as renameSync3, rmSync as rmSync5, writeFileSync as writeFileSync3 } from "node:fs";
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";
16902
17086
  import { readdir, readFile } from "node:fs/promises";
16903
17087
  import { createRequire as createRequire3 } from "node:module";
16904
17088
  import { homedir as homedir2 } from "node:os";
@@ -16941,6 +17125,7 @@ var REQUIRED_LOCAL_STORE_TABLES2 = [
16941
17125
  ];
16942
17126
  var SQLITE_PRAGMAS2 = [
16943
17127
  "PRAGMA journal_mode = WAL",
17128
+ "PRAGMA synchronous = FULL",
16944
17129
  "PRAGMA foreign_keys = ON",
16945
17130
  "PRAGMA busy_timeout = 5000"
16946
17131
  ];
@@ -17462,8 +17647,16 @@ var LOCAL_SQLITE_MIGRATIONS2 = [
17462
17647
  )`,
17463
17648
  "CREATE INDEX IF NOT EXISTS idx_audit_runs_status ON audit_runs(storage_repository_id, storage_workspace_id, status)"
17464
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
+ ]
17465
17657
  }
17466
17658
  ];
17659
+ var CHANGESET_STARTUP_CLEANUP_LIMIT = 100;
17467
17660
  var ARCHCONTEXT_STATE_DIR_ENV2 = "ARCHCONTEXT_STATE_DIR";
17468
17661
  var ARCHCONTEXT_LOCAL_STORE_PATH_ENV2 = "ARCHCONTEXT_LOCAL_STORE_PATH";
17469
17662
  function defaultArchContextStateRoot2(env = process.env, platform = process.platform, home = homedir2()) {
@@ -17522,10 +17715,10 @@ function migrateLegacyLocalStoreIfNeeded2(root = process.cwd(), env = process.en
17522
17715
  });
17523
17716
  }
17524
17717
  ensurePrivateDir2(dirname6(paths.localStorePath));
17525
- const legacyExists = existsSync10(paths.legacyLocalStorePath);
17718
+ const legacyExists = existsSync11(paths.legacyLocalStorePath);
17526
17719
  const integrityCheck = {};
17527
17720
  const quarantinedFiles = [];
17528
- const targetExists = existsSync10(paths.localStorePath);
17721
+ const targetExists = existsSync11(paths.localStorePath);
17529
17722
  if (targetExists) {
17530
17723
  try {
17531
17724
  integrityCheck.target = assertCurrentLocalStore2(paths.localStorePath);
@@ -17989,6 +18182,11 @@ class SqliteLocalStore {
17989
18182
  throw new Error(`ChangeSet journal not found: ${journalId}`);
17990
18183
  db.prepare("UPDATE changeset_journal SET status = ?, updated_at = ?, completed_at = ? WHERE journal_id = ?").run("committed", nowIso2(), nowIso2(), journalId);
17991
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
+ }
17992
18190
  async abortChangeSet(journalId, reason) {
17993
18191
  const db = await this.database();
17994
18192
  const row = db.prepare("SELECT metadata_json FROM changeset_journal WHERE journal_id = ?").get(journalId);
@@ -17999,37 +18197,61 @@ class SqliteLocalStore {
17999
18197
  }
18000
18198
  recoverPendingChangeSets() {
18001
18199
  const db = this.requireOpenDatabase();
18002
- const committed = db.prepare("SELECT files_json FROM changeset_journal WHERE status = ?").all("committed");
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);
18003
18204
  for (const row of committed) {
18004
- cleanupCommittedJournalFiles(JSON.parse(String(row.files_json)));
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 {}
18005
18210
  }
18006
18211
  const rows = db.prepare("SELECT journal_id, root, files_json, metadata_json FROM changeset_journal WHERE status = ?").all("pending");
18212
+ let recovered = 0;
18007
18213
  for (const row of rows) {
18008
- const files = JSON.parse(String(row.files_json));
18009
- const metadata = JSON.parse(String(row.metadata_json));
18010
- const plannedLedgerEvent = changeSetJournalPlannedLedgerEvent(metadata);
18011
- const existingLedgerEvent = plannedLedgerEvent ? architectureEventByIdempotency(db, plannedLedgerEvent) : undefined;
18012
- if (plannedLedgerEvent && existingLedgerEvent) {
18013
- const expectedEventHash = normalizeArchitectureLedgerEvent(plannedLedgerEvent, existingLedgerEvent.previousEventHash).eventHash;
18014
- if (expectedEventHash !== existingLedgerEvent.event.eventHash) {
18015
- throw new Error(`changeset-ledger-recovery-idempotency-conflict: ${plannedLedgerEvent.idempotencyKey}`);
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;
18016
18238
  }
18017
- cleanupCommittedJournalFiles(files);
18018
- db.prepare("UPDATE changeset_journal SET status = ?, metadata_json = ?, updated_at = ?, completed_at = ? WHERE journal_id = ?").run("committed", stableJson(withChangeSetJournalArchitectureLedger(metadata, {
18019
- recovery: {
18020
- schemaVersion: "archcontext.changeset-ledger-recovery/v1",
18021
- status: "ledger-append-detected",
18022
- eventId: existingLedgerEvent.event.eventId,
18023
- eventHash: existingLedgerEvent.event.eventHash ?? "",
18024
- recoveredAt: nowIso2()
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()
18025
18250
  }
18026
- })), nowIso2(), nowIso2(), String(row.journal_id));
18027
- continue;
18251
+ }), nowIso2(), String(row.journal_id));
18028
18252
  }
18029
- recoverJournalFiles(String(row.root), files);
18030
- db.prepare("UPDATE changeset_journal SET status = ?, updated_at = ?, completed_at = ? WHERE journal_id = ?").run("recovered", nowIso2(), nowIso2(), String(row.journal_id));
18031
18253
  }
18032
- return rows.length;
18254
+ return recovered;
18033
18255
  }
18034
18256
  async saveTaskState(taskSessionId, state) {
18035
18257
  const db = await this.database();
@@ -18127,52 +18349,38 @@ class SqliteLocalStore {
18127
18349
  for (const event of input.events)
18128
18350
  validateArchitectureLedgerEvent(event);
18129
18351
  const db = await this.database();
18130
- const startedAt = Date.now();
18131
- const appendedEvents = [];
18132
- const duplicateEvents = [];
18133
18352
  db.exec("BEGIN IMMEDIATE");
18134
18353
  try {
18135
- let processed = 0;
18136
- for (const event of input.events) {
18137
- const duplicate = architectureEventByIdempotency(db, event);
18138
- if (duplicate) {
18139
- const expectedDuplicateHash = normalizeArchitectureLedgerEvent(event, duplicate.previousEventHash).eventHash;
18140
- if (expectedDuplicateHash !== duplicate.event.eventHash) {
18141
- throw new Error(`architecture-ledger-idempotency-conflict: ${event.idempotencyKey}`);
18142
- }
18143
- duplicateEvents.push(duplicate.event);
18144
- continue;
18145
- }
18146
- const previousEventHash = latestArchitectureEventHash(db, event.repository.storageRepositoryId, event.worktree.storageWorkspaceId);
18147
- const normalized = normalizeArchitectureLedgerEvent(event, previousEventHash);
18148
- insertArchitectureEvent(db, normalized);
18149
- persistArchitectureLedgerArtifacts(db, normalized);
18150
- materializeArchitectureLedgerEvent(db, normalized);
18151
- appendedEvents.push(normalized);
18152
- processed += 1;
18153
- if (input.faultAfterEvents !== undefined && processed >= input.faultAfterEvents)
18154
- throw new Error("architecture-ledger-fault-injection");
18155
- }
18156
- const scope = architectureScopeFromEvent(input.events[0] ?? duplicateEvents[0]);
18157
- const state = scope ? readArchitectureLedgerStateFromDb(db, scope) : emptyArchitectureLedgerState();
18158
- if (scope) {
18159
- recordArchitectureLedgerOperation(db, {
18160
- scope,
18161
- operationKind: "append_events",
18162
- durationMs: Date.now() - startedAt,
18163
- rowCount: appendedEvents.length,
18164
- rebuildReason: null
18165
- });
18166
- }
18354
+ const result = appendArchitectureEventsInOpenTransaction(db, input);
18167
18355
  db.exec("COMMIT");
18168
- return {
18169
- appendedEvents,
18170
- duplicateEvents,
18171
- graphDigest: architectureLedgerStateDigest(state),
18172
- entityCount: state.entities.length,
18173
- relationCount: state.relations.length,
18174
- constraintCount: state.constraints.length
18175
- };
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;
18176
18384
  } catch (error) {
18177
18385
  db.exec("ROLLBACK");
18178
18386
  throw error;
@@ -18186,24 +18394,50 @@ class SqliteLocalStore {
18186
18394
  WHERE storage_repository_id = ?
18187
18395
  AND storage_workspace_id = ?
18188
18396
  ${statusClause}
18189
- ORDER BY created_at DESC, run_id ASC`).all(input.repository.storageRepositoryId, input.worktree.storageWorkspaceId, ...statuses).map((row) => JSON.parse(String(row.run_json)));
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;
18190
18424
  }
18191
18425
  async getAuditRun(input) {
18192
18426
  const db = await this.database();
18193
18427
  const row = db.prepare(`SELECT run_json FROM audit_runs
18194
- WHERE storage_repository_id = ? AND storage_workspace_id = ? AND run_id = ?`).get(input.repository.storageRepositoryId, input.worktree.storageWorkspaceId, input.runId);
18428
+ WHERE storage_repository_id = ? AND storage_workspace_id = ? AND run_id = ?`).get(input.repository.storageRepositoryId, architectureLedgerWorkspaceKey(input.worktree), architectureLedgerStorageId(input.worktree, input.runId));
18195
18429
  return row ? JSON.parse(String(row.run_json)) : undefined;
18196
18430
  }
18197
18431
  async readArchitectureLedgerSourceCursor(input) {
18198
18432
  const db = await this.database();
18199
18433
  const row = db.prepare(`SELECT cursor_json FROM source_cursors
18200
- WHERE storage_repository_id = ? AND storage_workspace_id = ? AND cursor_id = ?`).get(input.repository.storageRepositoryId, input.worktree.storageWorkspaceId, input.cursorId);
18434
+ WHERE storage_repository_id = ? AND storage_workspace_id = ? AND cursor_id = ?`).get(input.repository.storageRepositoryId, architectureLedgerWorkspaceKey(input.worktree), architectureLedgerStorageId(input.worktree, input.cursorId));
18201
18435
  return row ? JSON.parse(String(row.cursor_json)) : undefined;
18202
18436
  }
18203
18437
  async createArchitectureLedgerSnapshot(input) {
18204
18438
  const db = await this.database();
18205
18439
  const startedAt = Date.now();
18206
- const latest = latestArchitectureEvent(db, input.repository.storageRepositoryId, input.worktree.storageWorkspaceId);
18440
+ const latest = latestArchitectureEvent(db, input.repository.storageRepositoryId, architectureLedgerWorkspaceKey(input.worktree));
18207
18441
  if (!latest)
18208
18442
  throw new Error("architecture-ledger-snapshot-requires-event");
18209
18443
  const state = readArchitectureLedgerStateFromDb(db, input);
@@ -18217,7 +18451,7 @@ class SqliteLocalStore {
18217
18451
  (snapshot_id, repository_id, storage_repository_id, workspace_id, storage_workspace_id, branch, head_sha, worktree_digest,
18218
18452
  source_mode, last_event_id, last_event_hash, graph_digest, projection_digest, entity_count, relation_count,
18219
18453
  constraint_count, input_digests_json, snapshot_json, created_at)
18220
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(snapshot.snapshotId, snapshot.repository.repositoryId, snapshot.repository.storageRepositoryId, snapshot.worktree.workspaceId, snapshot.worktree.storageWorkspaceId, snapshot.worktree.branch, snapshot.worktree.headSha, snapshot.worktree.worktreeDigest, snapshot.sourceMode, snapshot.eventCursor.lastEventId, snapshot.eventCursor.lastEventHash, snapshot.graphDigest, snapshot.projectionDigest, snapshot.entityCount, snapshot.relationCount, snapshot.constraintCount, stableJson(snapshot.inputDigests), stableJson(snapshot), snapshot.createdAt);
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);
18221
18455
  recordArchitectureLedgerOperation(db, {
18222
18456
  scope: input,
18223
18457
  operationKind: "create_snapshot",
@@ -18286,16 +18520,16 @@ class SqliteLocalStore {
18286
18520
  const db = await this.database();
18287
18521
  const startedAt = Date.now();
18288
18522
  const snapshot = db.prepare(`SELECT last_event_id FROM architecture_snapshots
18289
- WHERE snapshot_id = ? AND storage_repository_id = ? AND storage_workspace_id = ?`).get(input.beforeSnapshotId, input.repository.storageRepositoryId, input.worktree.storageWorkspaceId);
18523
+ WHERE snapshot_id = ? AND storage_repository_id = ? AND storage_workspace_id = ?`).get(input.beforeSnapshotId, input.repository.storageRepositoryId, architectureLedgerWorkspaceKey(input.worktree));
18290
18524
  if (!snapshot)
18291
18525
  throw new Error(`architecture-ledger-snapshot-not-found: ${input.beforeSnapshotId}`);
18292
18526
  const cursor = db.prepare("SELECT event_sequence FROM architecture_events WHERE event_id = ?").get(String(snapshot.last_event_id));
18293
18527
  if (!cursor)
18294
18528
  throw new Error(`architecture-ledger-snapshot-cursor-not-found: ${String(snapshot.last_event_id)}`);
18295
18529
  const before = Number(db.prepare(`SELECT COUNT(*) AS count FROM architecture_events
18296
- WHERE storage_repository_id = ? AND storage_workspace_id = ? AND event_sequence <= ? AND compacted_by_snapshot_id IS NULL`).get(input.repository.storageRepositoryId, input.worktree.storageWorkspaceId, Number(cursor.event_sequence))?.count ?? 0);
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);
18297
18531
  db.prepare(`UPDATE architecture_events SET compacted_by_snapshot_id = ?
18298
- 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.storageWorkspaceId, Number(cursor.event_sequence));
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));
18299
18533
  recordArchitectureLedgerOperation(db, {
18300
18534
  scope: input,
18301
18535
  operationKind: "compact_events",
@@ -18315,7 +18549,7 @@ class SqliteLocalStore {
18315
18549
  const replay = await this.verifyArchitectureLedgerReplay(input);
18316
18550
  if (!replay.ok)
18317
18551
  failures.push(...replay.mismatches);
18318
- 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.storageWorkspaceId)?.count ?? 0);
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);
18319
18553
  const result = {
18320
18554
  ok: failures.length === 0,
18321
18555
  graphDigest: replay.materializedDigest,
@@ -18335,7 +18569,7 @@ class SqliteLocalStore {
18335
18569
  async backupArchitectureLedger(input) {
18336
18570
  const db = await this.database();
18337
18571
  ensurePrivateDir2(dirname6(input.backupPath));
18338
- if (existsSync10(input.backupPath))
18572
+ if (existsSync11(input.backupPath))
18339
18573
  rmSync5(input.backupPath, { force: true });
18340
18574
  db.exec(`VACUUM INTO ${sqliteStringLiteral2(input.backupPath)}`);
18341
18575
  return { backupPath: input.backupPath, integrity: assertSqliteIntegrity2(input.backupPath) };
@@ -18366,9 +18600,80 @@ class SqliteLocalStore {
18366
18600
  function architectureScopeFromEvent(event) {
18367
18601
  return event ? { repository: event.repository, worktree: event.worktree } : undefined;
18368
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
+ }
18369
18674
  function architectureEventByIdempotency(db, event) {
18370
18675
  const row = db.prepare(`SELECT event_json, previous_event_hash FROM architecture_events
18371
- WHERE storage_repository_id = ? AND storage_workspace_id = ? AND idempotency_key = ?`).get(event.repository.storageRepositoryId, event.worktree.storageWorkspaceId, event.idempotencyKey);
18676
+ WHERE storage_repository_id = ? AND storage_workspace_id = ? AND idempotency_key = ?`).get(event.repository.storageRepositoryId, architectureLedgerWorkspaceKey(event.worktree), event.idempotencyKey);
18372
18677
  return row ? {
18373
18678
  event: JSON.parse(String(row.event_json)),
18374
18679
  previousEventHash: row.previous_event_hash === null || row.previous_event_hash === undefined ? null : String(row.previous_event_hash)
@@ -18384,11 +18689,12 @@ function latestArchitectureEvent(db, storageRepositoryId, storageWorkspaceId) {
18384
18689
  return row ? { event: JSON.parse(String(row.event_json)), eventHash: String(row.event_hash) } : undefined;
18385
18690
  }
18386
18691
  function insertArchitectureEvent(db, event) {
18692
+ const workspaceKey = architectureLedgerWorkspaceKey(event.worktree);
18387
18693
  db.prepare(`INSERT INTO architecture_events
18388
18694
  (event_id, repository_id, storage_repository_id, workspace_id, storage_workspace_id, branch, head_sha, worktree_digest,
18389
18695
  event_type, payload_version, source, actor_kind, actor_id, base_digest, resulting_digest, previous_event_hash,
18390
18696
  event_hash, idempotency_key, payload_json, provenance_json, event_json, created_at)
18391
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(event.eventId, event.repository.repositoryId, event.repository.storageRepositoryId, event.worktree.workspaceId, event.worktree.storageWorkspaceId, 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);
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);
18392
18698
  insertArchitectureLedgerFts(db, "event", architectureLedgerPayload(event).summary ?? "", architectureLedgerPayload(event).rationale ?? "", architectureLedgerPayload(event).title ?? "", "");
18393
18699
  const payload = architectureLedgerPayload(event);
18394
18700
  insertArchitectureLedgerSearchDoc(db, event, {
@@ -18402,11 +18708,13 @@ function insertArchitectureEvent(db, event) {
18402
18708
  }
18403
18709
  function persistArchitectureLedgerArtifacts(db, event) {
18404
18710
  const payload = architectureLedgerPayload(event);
18711
+ const workspaceKey = architectureLedgerWorkspaceKey(event.worktree);
18712
+ const storageEventId = architectureLedgerStorageId(event.worktree, event.eventId);
18405
18713
  for (const evidence of payload.evidenceItems ?? []) {
18406
18714
  db.prepare(`INSERT OR REPLACE INTO evidence_items
18407
18715
  (evidence_id, repository_id, storage_repository_id, workspace_id, storage_workspace_id, event_id, kind, strength,
18408
18716
  polarity, origin, subject, selector_json, summary, coverage_json, supports_json, provenance_json, evidence_json, digest, created_at)
18409
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(evidence.evidenceId, event.repository.repositoryId, event.repository.storageRepositoryId, event.worktree.workspaceId, event.worktree.storageWorkspaceId, event.eventId, 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);
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);
18410
18718
  insertArchitectureLedgerFts(db, "evidence", evidence.summary, "", "", evidence.summary);
18411
18719
  insertArchitectureLedgerSearchDoc(db, event, {
18412
18720
  docId: `evidence:${evidence.evidenceId}`,
@@ -18421,7 +18729,7 @@ function persistArchitectureLedgerArtifacts(db, event) {
18421
18729
  db.prepare(`INSERT OR REPLACE INTO evidence_bindings
18422
18730
  (binding_id, evidence_id, repository_id, storage_repository_id, workspace_id, storage_workspace_id, event_id,
18423
18731
  target_kind, target_id, binding_reason, authority_effect, provenance_json, binding_json, created_at)
18424
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(binding.bindingId, binding.evidenceId, event.repository.repositoryId, event.repository.storageRepositoryId, event.worktree.workspaceId, event.worktree.storageWorkspaceId, event.eventId, binding.target.kind, binding.target.id, binding.bindingReason, binding.authorityEffect, stableJson(binding.provenance), stableJson(binding), binding.createdAt);
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);
18425
18733
  }
18426
18734
  for (const run of payload.recommendationRuns ?? [])
18427
18735
  persistRecommendationRun(db, event, run);
@@ -18441,17 +18749,19 @@ function persistArchitectureLedgerArtifacts(db, event) {
18441
18749
  persistProjectionState(db, event, payload.projectionState);
18442
18750
  }
18443
18751
  function persistRecommendationRun(db, event, run) {
18752
+ const workspaceKey = architectureLedgerWorkspaceKey(event.worktree);
18444
18753
  db.prepare(`INSERT OR REPLACE INTO recommendation_runs
18445
18754
  (run_id, repository_id, storage_repository_id, workspace_id, storage_workspace_id, event_id, status, catalog_digest,
18446
18755
  input_digest, output_digest, metrics_json, run_json, started_at, completed_at)
18447
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(run.runId, event.repository.repositoryId, event.repository.storageRepositoryId, event.worktree.workspaceId, event.worktree.storageWorkspaceId, event.eventId, run.status, run.catalogDigest, run.inputDigest, run.outputDigest, stableJson(run.metrics), stableJson(run), run.startedAt, run.completedAt ?? null);
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);
18448
18757
  }
18449
18758
  function persistRecommendation(db, event, recommendation) {
18759
+ const workspaceKey = architectureLedgerWorkspaceKey(event.worktree);
18450
18760
  db.prepare(`INSERT OR REPLACE INTO recommendations
18451
18761
  (recommendation_id, run_id, repository_id, storage_repository_id, workspace_id, storage_workspace_id, event_id, fingerprint,
18452
18762
  subject, practice_id, status, confidence, enforcement, risk, uncertainty, evidence_binding_ids_json, explanation_json,
18453
18763
  recommendation_json, created_at, updated_at)
18454
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(recommendation.recommendationId, recommendation.runId, event.repository.repositoryId, event.repository.storageRepositoryId, event.worktree.workspaceId, event.worktree.storageWorkspaceId, 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);
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);
18455
18765
  insertArchitectureLedgerFts(db, "recommendation", recommendation.explanation.join(`
18456
18766
  `), "", recommendation.subject, recommendation.explanation.join(`
18457
18767
  `));
@@ -18468,44 +18778,51 @@ function persistRecommendation(db, event, recommendation) {
18468
18778
  });
18469
18779
  }
18470
18780
  function persistAgentJob(db, event, job) {
18781
+ const workspaceKey = architectureLedgerWorkspaceKey(event.worktree);
18471
18782
  db.prepare(`INSERT OR REPLACE INTO agent_jobs
18472
18783
  (job_id, repository_id, storage_repository_id, workspace_id, storage_workspace_id, event_id, status, runner_port,
18473
18784
  fingerprint, input_digest, output_digest, stale_policy, job_json, queued_at, updated_at)
18474
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(job.jobId, event.repository.repositoryId, event.repository.storageRepositoryId, event.worktree.workspaceId, event.worktree.storageWorkspaceId, event.eventId, job.status, job.runnerPort, job.fingerprint, job.inputDigest, job.outputDigest ?? null, job.stalePolicy, stableJson(job), job.queuedAt, job.updatedAt);
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);
18475
18786
  }
18476
18787
  function persistAuditRun(db, event, run) {
18788
+ const workspaceKey = architectureLedgerWorkspaceKey(event.worktree);
18477
18789
  db.prepare(`INSERT OR REPLACE INTO audit_runs
18478
18790
  (run_id, repository_id, storage_repository_id, workspace_id, storage_workspace_id, event_id, job_id, report_id, status,
18479
18791
  repo_name_with_owner, repo_visibility, base_sha, input_digest, output_digest, run_json, created_at)
18480
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(run.runId, event.repository.repositoryId, event.repository.storageRepositoryId, event.worktree.workspaceId, event.worktree.storageWorkspaceId, event.eventId, run.jobId, run.reportId, run.status, run.repoNameWithOwner, run.repoVisibility, run.baseSha, run.inputDigest, run.outputDigest, stableJson(run), run.createdAt);
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);
18481
18793
  }
18482
18794
  function persistProjectionState(db, event, state) {
18483
18795
  const path = String(state.path ?? "projection");
18484
18796
  const projectionDigest = typeof state.projectionDigest === "string" ? state.projectionDigest : digestJson(state);
18797
+ const workspaceKey = architectureLedgerWorkspaceKey(event.worktree);
18485
18798
  db.prepare(`INSERT OR REPLACE INTO projection_state
18486
18799
  (projection_id, repository_id, storage_repository_id, workspace_id, storage_workspace_id, event_id, path, projection_digest, state_json, updated_at)
18487
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(String(state.projectionId ?? stableLedgerId("projection", event.eventId, path)), event.repository.repositoryId, event.repository.storageRepositoryId, event.worktree.workspaceId, event.worktree.storageWorkspaceId, event.eventId, path, projectionDigest, stableJson(state), event.timestamp);
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);
18488
18801
  }
18489
18802
  function persistSourceCursor(db, event, cursor) {
18490
18803
  const source = String(cursor.source ?? event.source);
18804
+ const workspaceKey = architectureLedgerWorkspaceKey(event.worktree);
18491
18805
  db.prepare(`INSERT OR REPLACE INTO source_cursors
18492
18806
  (cursor_id, repository_id, storage_repository_id, workspace_id, storage_workspace_id, source, cursor_json, updated_at)
18493
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)`).run(String(cursor.cursorId ?? stableLedgerId("cursor", event.eventId, source)), event.repository.repositoryId, event.repository.storageRepositoryId, event.worktree.workspaceId, event.worktree.storageWorkspaceId, source, stableJson(cursor), event.timestamp);
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);
18494
18808
  }
18495
18809
  function persistGenericLedgerJson(db, event, table, idColumn, jsonColumn, value, prefix) {
18496
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);
18497
18813
  if (table === "recommendation_feedback") {
18498
18814
  db.prepare(`INSERT OR REPLACE INTO recommendation_feedback
18499
18815
  (feedback_id, recommendation_id, repository_id, storage_repository_id, workspace_id, storage_workspace_id, event_id, feedback_json, created_at)
18500
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(id, String(value.recommendationId ?? value.recommendation_id ?? "unknown"), event.repository.repositoryId, event.repository.storageRepositoryId, event.worktree.workspaceId, event.worktree.storageWorkspaceId, event.eventId, stableJson(value), String(value.createdAt ?? event.timestamp));
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));
18501
18817
  return;
18502
18818
  }
18503
18819
  db.prepare(`INSERT OR REPLACE INTO waivers
18504
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)
18505
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(id, event.repository.repositoryId, event.repository.storageRepositoryId, event.worktree.workspaceId, event.worktree.storageWorkspaceId, 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);
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);
18506
18822
  }
18507
18823
  function materializeArchitectureLedgerEvent(db, event) {
18508
18824
  const payload = architectureLedgerPayload(event);
18825
+ const workspaceKey = architectureLedgerWorkspaceKey(event.worktree);
18509
18826
  for (const operation of payload.operations ?? []) {
18510
18827
  switch (operation.op) {
18511
18828
  case "upsert_entity":
@@ -18513,31 +18830,32 @@ function materializeArchitectureLedgerEvent(db, event) {
18513
18830
  break;
18514
18831
  case "delete_entity":
18515
18832
  deleteArchitectureLedgerSearchDocs(db, { repository: event.repository, worktree: event.worktree }, [operation.entityId]);
18516
- db.prepare("DELETE FROM architecture_entities_current WHERE storage_repository_id = ? AND storage_workspace_id = ? AND entity_id = ?").run(event.repository.storageRepositoryId, event.worktree.storageWorkspaceId, operation.entityId);
18517
- 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, event.worktree.storageWorkspaceId, operation.entityId, operation.entityId);
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);
18518
18835
  break;
18519
18836
  case "upsert_relation":
18520
18837
  upsertArchitectureRelation(db, event, operation.relation);
18521
18838
  break;
18522
18839
  case "delete_relation":
18523
18840
  deleteArchitectureLedgerSearchDocs(db, { repository: event.repository, worktree: event.worktree }, [operation.relationId]);
18524
- db.prepare("DELETE FROM architecture_relations_current WHERE storage_repository_id = ? AND storage_workspace_id = ? AND relation_id = ?").run(event.repository.storageRepositoryId, event.worktree.storageWorkspaceId, operation.relationId);
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);
18525
18842
  break;
18526
18843
  case "upsert_constraint":
18527
18844
  upsertArchitectureConstraint(db, event, operation.constraint);
18528
18845
  break;
18529
18846
  case "delete_constraint":
18530
18847
  deleteArchitectureLedgerSearchDocs(db, { repository: event.repository, worktree: event.worktree }, [operation.constraintId]);
18531
- db.prepare("DELETE FROM architecture_constraints_current WHERE storage_repository_id = ? AND storage_workspace_id = ? AND constraint_id = ?").run(event.repository.storageRepositoryId, event.worktree.storageWorkspaceId, operation.constraintId);
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);
18532
18849
  break;
18533
18850
  }
18534
18851
  }
18535
18852
  }
18536
18853
  function upsertArchitectureEntity(db, event, entity) {
18854
+ const workspaceKey = architectureLedgerWorkspaceKey(event.worktree);
18537
18855
  db.prepare(`INSERT OR REPLACE INTO architecture_entities_current
18538
18856
  (storage_repository_id, storage_workspace_id, entity_id, repository_id, workspace_id, branch, head_sha, worktree_digest,
18539
18857
  kind, canonical_name, status, path, summary, metadata_json, last_event_id, updated_at)
18540
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(event.repository.storageRepositoryId, event.worktree.storageWorkspaceId, 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 ?? {}), event.eventId, event.timestamp);
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);
18541
18859
  insertArchitectureLedgerFts(db, "entity", entity.summary ?? "", "", entity.canonicalName, "");
18542
18860
  insertArchitectureLedgerSearchDoc(db, event, {
18543
18861
  docId: `entity:${entity.entityId}`,
@@ -18550,10 +18868,11 @@ function upsertArchitectureEntity(db, event, entity) {
18550
18868
  });
18551
18869
  }
18552
18870
  function upsertArchitectureRelation(db, event, relation) {
18871
+ const workspaceKey = architectureLedgerWorkspaceKey(event.worktree);
18553
18872
  db.prepare(`INSERT OR REPLACE INTO architecture_relations_current
18554
18873
  (storage_repository_id, storage_workspace_id, relation_id, repository_id, workspace_id, branch, head_sha, worktree_digest,
18555
18874
  kind, source_entity_id, target_entity_id, status, summary, metadata_json, last_event_id, updated_at)
18556
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(event.repository.storageRepositoryId, event.worktree.storageWorkspaceId, 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 ?? {}), event.eventId, event.timestamp);
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);
18557
18876
  insertArchitectureLedgerFts(db, "relation", relation.summary ?? "", "", relation.relationId, "");
18558
18877
  insertArchitectureLedgerSearchDoc(db, event, {
18559
18878
  docId: `relation:${relation.relationId}`,
@@ -18566,10 +18885,11 @@ function upsertArchitectureRelation(db, event, relation) {
18566
18885
  });
18567
18886
  }
18568
18887
  function upsertArchitectureConstraint(db, event, constraint) {
18888
+ const workspaceKey = architectureLedgerWorkspaceKey(event.worktree);
18569
18889
  db.prepare(`INSERT OR REPLACE INTO architecture_constraints_current
18570
18890
  (storage_repository_id, storage_workspace_id, constraint_id, repository_id, workspace_id, branch, head_sha, worktree_digest,
18571
18891
  kind, subject_id, status, severity, summary, metadata_json, last_event_id, updated_at)
18572
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(event.repository.storageRepositoryId, event.worktree.storageWorkspaceId, 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 ?? {}), event.eventId, event.timestamp);
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);
18573
18893
  insertArchitectureLedgerFts(db, "constraint", constraint.summary ?? "", "", constraint.constraintId, "");
18574
18894
  insertArchitectureLedgerSearchDoc(db, event, {
18575
18895
  docId: `constraint:${constraint.constraintId}`,
@@ -18582,7 +18902,7 @@ function upsertArchitectureConstraint(db, event, constraint) {
18582
18902
  });
18583
18903
  }
18584
18904
  function readArchitectureLedgerStateFromDb(db, scope) {
18585
- const scopeParams = [scope.repository.storageRepositoryId, scope.worktree.storageWorkspaceId];
18905
+ const scopeParams = [scope.repository.storageRepositoryId, architectureLedgerWorkspaceKey(scope.worktree)];
18586
18906
  const entities = db.prepare(`SELECT entity_id, kind, canonical_name, status, path, summary, metadata_json
18587
18907
  FROM architecture_entities_current
18588
18908
  WHERE storage_repository_id = ? AND storage_workspace_id = ?
@@ -18623,7 +18943,7 @@ function readArchitectureLedgerStateFromDb(db, scope) {
18623
18943
  }
18624
18944
  function readArchitectureLedgerNeighborhoodFromDb(db, input) {
18625
18945
  const depth = Math.max(0, Math.floor(input.depth));
18626
- const scopeParams = [input.repository.storageRepositoryId, input.worktree.storageWorkspaceId];
18946
+ const scopeParams = [input.repository.storageRepositoryId, architectureLedgerWorkspaceKey(input.worktree)];
18627
18947
  const entityIds = [...new Set(db.prepare(`WITH RECURSIVE seed(entity_id) AS (
18628
18948
  SELECT entity_id FROM architecture_entities_current
18629
18949
  WHERE storage_repository_id = ? AND storage_workspace_id = ? AND entity_id = ?
@@ -18704,42 +19024,43 @@ function architectureEventsForReplay(db, input) {
18704
19024
  let untilEventId = input.untilEventId;
18705
19025
  if (input.snapshotId) {
18706
19026
  const snapshot = db.prepare(`SELECT last_event_id FROM architecture_snapshots
18707
- WHERE snapshot_id = ? AND storage_repository_id = ? AND storage_workspace_id = ?`).get(input.snapshotId, input.repository.storageRepositoryId, input.worktree.storageWorkspaceId);
19027
+ WHERE snapshot_id = ? AND storage_repository_id = ? AND storage_workspace_id = ?`).get(input.snapshotId, input.repository.storageRepositoryId, architectureLedgerWorkspaceKey(input.worktree));
18708
19028
  if (!snapshot)
18709
19029
  throw new Error(`architecture-ledger-snapshot-not-found: ${input.snapshotId}`);
18710
19030
  untilEventId = String(snapshot.last_event_id);
18711
19031
  }
18712
19032
  const rows = db.prepare(`SELECT event_id, event_json FROM architecture_events
18713
19033
  WHERE storage_repository_id = ? AND storage_workspace_id = ?
18714
- ORDER BY event_sequence`).all(input.repository.storageRepositoryId, input.worktree.storageWorkspaceId);
19034
+ ORDER BY event_sequence`).all(input.repository.storageRepositoryId, architectureLedgerWorkspaceKey(input.worktree));
18715
19035
  const events = [];
18716
19036
  for (const row of rows) {
18717
19037
  const event = JSON.parse(String(row.event_json));
18718
19038
  events.push(event);
18719
- if (untilEventId && String(row.event_id) === untilEventId)
19039
+ if (untilEventId && (String(row.event_id) === untilEventId || event.eventId === untilEventId))
18720
19040
  break;
18721
19041
  }
18722
19042
  return events;
18723
19043
  }
18724
19044
  function deleteArchitectureCurrentState(db, scope) {
18725
19045
  for (const table of ["architecture_entities_current", "architecture_relations_current", "architecture_constraints_current"]) {
18726
- db.prepare(`DELETE FROM ${table} WHERE storage_repository_id = ? AND storage_workspace_id = ?`).run(scope.repository.storageRepositoryId, scope.worktree.storageWorkspaceId);
19046
+ db.prepare(`DELETE FROM ${table} WHERE storage_repository_id = ? AND storage_workspace_id = ?`).run(scope.repository.storageRepositoryId, architectureLedgerWorkspaceKey(scope.worktree));
18727
19047
  }
18728
19048
  }
18729
19049
  function insertArchitectureLedgerFts(db, kind, summary, rationale, title, evidenceSummary) {
18730
19050
  db.prepare("INSERT INTO architecture_ledger_fts(kind, summary, rationale, title, evidence_summary) VALUES (?, ?, ?, ?, ?)").run(kind, summary, rationale, title, evidenceSummary);
18731
19051
  }
18732
19052
  function insertArchitectureLedgerSearchDoc(db, event, doc) {
19053
+ const workspaceKey = architectureLedgerWorkspaceKey(event.worktree);
18733
19054
  db.prepare(`DELETE FROM architecture_ledger_search_fts
18734
- WHERE storage_repository_id = ? AND storage_workspace_id = ? AND doc_id = ?`).run(event.repository.storageRepositoryId, event.worktree.storageWorkspaceId, doc.docId);
19055
+ WHERE storage_repository_id = ? AND storage_workspace_id = ? AND doc_id = ?`).run(event.repository.storageRepositoryId, workspaceKey, doc.docId);
18735
19056
  db.prepare(`INSERT INTO architecture_ledger_search_fts
18736
19057
  (doc_id, storage_repository_id, storage_workspace_id, target_kind, target_id, subject_id, title, summary, rationale, evidence_summary)
18737
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(doc.docId, event.repository.storageRepositoryId, event.worktree.storageWorkspaceId, doc.targetKind, doc.targetId, doc.subjectId ?? null, doc.title ?? "", doc.summary ?? "", doc.rationale ?? "", doc.evidenceSummary ?? "");
19058
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(doc.docId, event.repository.storageRepositoryId, workspaceKey, doc.targetKind, doc.targetId, doc.subjectId ?? null, doc.title ?? "", doc.summary ?? "", doc.rationale ?? "", doc.evidenceSummary ?? "");
18738
19059
  }
18739
19060
  function deleteArchitectureLedgerSearchDocs(db, scope, ids) {
18740
19061
  for (const id of ids) {
18741
19062
  db.prepare(`DELETE FROM architecture_ledger_search_fts
18742
- WHERE storage_repository_id = ? AND storage_workspace_id = ? AND (target_id = ? OR subject_id = ?)`).run(scope.repository.storageRepositoryId, scope.worktree.storageWorkspaceId, id, id);
19063
+ WHERE storage_repository_id = ? AND storage_workspace_id = ? AND (target_id = ? OR subject_id = ?)`).run(scope.repository.storageRepositoryId, architectureLedgerWorkspaceKey(scope.worktree), id, id);
18743
19064
  }
18744
19065
  }
18745
19066
  function queryArchitectureLedgerSearchFts(db, input) {
@@ -18753,7 +19074,7 @@ function queryArchitectureLedgerSearchFts(db, input) {
18753
19074
  WHERE storage_repository_id = ? AND storage_workspace_id = ?
18754
19075
  AND architecture_ledger_search_fts MATCH ?
18755
19076
  ORDER BY rank, target_kind, target_id
18756
- LIMIT ?`).all(input.repository.storageRepositoryId, input.worktree.storageWorkspaceId, matchQuery, limit);
19077
+ LIMIT ?`).all(input.repository.storageRepositoryId, architectureLedgerWorkspaceKey(input.worktree), matchQuery, limit);
18757
19078
  return rows.map((row) => {
18758
19079
  const title = String(row.title ?? "");
18759
19080
  const summary = String(row.summary ?? "");
@@ -18797,7 +19118,7 @@ function ftsMatchKind(query, doc) {
18797
19118
  function recordArchitectureLedgerOperation(db, input) {
18798
19119
  db.prepare(`INSERT INTO architecture_ledger_operations
18799
19120
  (operation_id, storage_repository_id, storage_workspace_id, operation_kind, duration_ms, row_count, rebuild_reason, created_at)
18800
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)`).run(`ledger_operation_${randomUUID2()}`, input.scope.repository.storageRepositoryId, input.scope.worktree.storageWorkspaceId, input.operationKind, Math.max(0, Math.trunc(input.durationMs)), input.rowCount, input.rebuildReason, nowIso2());
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());
18801
19122
  }
18802
19123
  function stableLedgerId(prefix, ...parts) {
18803
19124
  return `${prefix}.${createHash6("sha256").update(parts.join("\x00")).digest("hex").slice(0, 24)}`;
@@ -18980,7 +19301,7 @@ function assertTrustedLegacyLocalStoreSource2(paths) {
18980
19301
  if (!stat.isFile()) {
18981
19302
  throw new Error(`Legacy SQLite source must be a regular file: ${paths.legacyLocalStorePath}`);
18982
19303
  }
18983
- const sourceRealPath = realpathSync6.native(paths.legacyLocalStorePath);
19304
+ const sourceRealPath = realpathSync7.native(paths.legacyLocalStorePath);
18984
19305
  if (!isPathInsideOrSame2(sourceRealPath, paths.repositoryRoot)) {
18985
19306
  throw new Error(`Legacy SQLite source must stay inside the repository root: ${paths.legacyLocalStorePath}`);
18986
19307
  }
@@ -19034,7 +19355,7 @@ function sqliteStringLiteral2(value) {
19034
19355
  function publishStagedLocalStore2(stagingPath, targetPath) {
19035
19356
  for (const suffix of SQLITE_SIDECAR_SUFFIXES2) {
19036
19357
  const path = `${targetPath}${suffix}`;
19037
- if (existsSync10(path))
19358
+ if (existsSync11(path))
19038
19359
  throw new Error(`Cannot publish migrated SQLite over existing target file: ${path}`);
19039
19360
  }
19040
19361
  renameSync3(stagingPath, targetPath);
@@ -19106,14 +19427,14 @@ function quarantineExistingLocalStore2(paths) {
19106
19427
  const quarantinedFiles = [];
19107
19428
  for (const suffix of SQLITE_SIDECAR_SUFFIXES2) {
19108
19429
  const source = `${paths.localStorePath}${suffix}`;
19109
- if (!existsSync10(source))
19430
+ if (!existsSync11(source))
19110
19431
  continue;
19111
19432
  const target = join6(quarantineDir, `runtime.sqlite${suffix}`);
19112
19433
  renameSync3(source, target);
19113
19434
  quarantinedFiles.push(target);
19114
19435
  }
19115
19436
  const markerPath = legacyMigrationMarkerPath2(paths);
19116
- if (existsSync10(markerPath)) {
19437
+ if (existsSync11(markerPath)) {
19117
19438
  const target = join6(quarantineDir, LEGACY_MIGRATION_MARKER_FILE2);
19118
19439
  renameSync3(markerPath, target);
19119
19440
  quarantinedFiles.push(target);
@@ -19173,7 +19494,7 @@ function resolveMaybeRelative2(base, path) {
19173
19494
  function canonicalPath2(path) {
19174
19495
  const resolved = resolve13(path);
19175
19496
  try {
19176
- return realpathSync6.native(resolved);
19497
+ return realpathSync7.native(resolved);
19177
19498
  } catch {
19178
19499
  return resolved;
19179
19500
  }
@@ -19363,18 +19684,22 @@ function changeSetLedgerAppendSummary(result) {
19363
19684
  }
19364
19685
  function changeSetJournalPlannedLedgerEvent(metadata) {
19365
19686
  const architectureLedger = metadata.architectureLedger;
19366
- if (!isJsonRecord(architectureLedger))
19687
+ if (architectureLedger === undefined)
19367
19688
  return;
19689
+ if (!isJsonRecord(architectureLedger))
19690
+ throw new Error("changeset-ledger-recovery-metadata-malformed");
19368
19691
  const plannedEvent = architectureLedger.plannedEvent;
19369
- if (!isJsonRecord(plannedEvent))
19692
+ if (plannedEvent === undefined)
19370
19693
  return;
19694
+ if (!isJsonRecord(plannedEvent))
19695
+ throw new Error("changeset-ledger-recovery-planned-event-malformed");
19696
+ const event = plannedEvent;
19371
19697
  try {
19372
- const event = plannedEvent;
19373
19698
  validateArchitectureLedgerEvent(event);
19374
- return event;
19375
- } catch {
19376
- return;
19699
+ } catch (error) {
19700
+ throw new Error(`changeset-ledger-recovery-planned-event-invalid: ${error instanceof Error ? error.message : String(error)}`);
19377
19701
  }
19702
+ return event;
19378
19703
  }
19379
19704
  function isJsonRecord(value) {
19380
19705
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -19384,9 +19709,13 @@ function recoverJournalFiles(root, files) {
19384
19709
  const absolute = resolve13(root, file.path);
19385
19710
  if (file.tempPath)
19386
19711
  rmSync5(file.tempPath, { recursive: true, force: true });
19387
- rmSync5(absolute, { recursive: true, force: true });
19388
- if (file.existed && file.backupPath && existsSync10(file.backupPath)) {
19389
- renameSync3(file.backupPath, absolute);
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 });
19390
19719
  }
19391
19720
  fsyncDirectory3(dirname6(absolute));
19392
19721
  }
@@ -19443,8 +19772,18 @@ async function readRelationFiles(root, relationsDir) {
19443
19772
 
19444
19773
  // packages/local-runtime/model-store-yaml/src/index.ts
19445
19774
  init_src();
19446
- import { existsSync as existsSync11, mkdirSync as mkdirSync6, readdirSync as readdirSync7, readFileSync as readFileSync10, rmSync as rmSync6, writeFileSync as writeFileSync4 } from "node:fs";
19775
+ import { existsSync as existsSync12, mkdirSync as mkdirSync6, readdirSync as readdirSync7, readFileSync as readFileSync10, rmSync as rmSync6, writeFileSync as writeFileSync4 } from "node:fs";
19447
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
+ `);
19448
19787
  function createDefaultManifest(productId, productName) {
19449
19788
  return {
19450
19789
  schemaVersion: "archcontext.manifest/v1",
@@ -19594,16 +19933,50 @@ function initializeArchContextModel(root, productName = "ArchContext Project") {
19594
19933
  rebuildGeneratedProjection(root);
19595
19934
  }
19596
19935
  function rebuildGeneratedProjection(root) {
19597
- rmSync6(resolve14(root, ".archcontext/generated"), { recursive: true, force: true });
19598
- writeFile(root, ".archcontext/generated/ARCHITECTURE.md", [
19599
- "<!-- Generated by ArchContext. Do not edit by hand. -->",
19600
- "",
19601
- "# Architecture",
19602
- "",
19603
- "Model projection is generated from `.archcontext/model`.",
19604
- ""
19605
- ].join(`
19606
- `));
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();
19607
19980
  }
19608
19981
 
19609
19982
  class YamlModelStore {
@@ -19660,7 +20033,7 @@ function collectArchContextFiles(root, entries) {
19660
20033
  const files = [];
19661
20034
  for (const entry of entries) {
19662
20035
  const absolute = resolve14(root, entry);
19663
- if (!existsSync11(absolute))
20036
+ if (!existsSync12(absolute))
19664
20037
  continue;
19665
20038
  const stat = readdirOrFile(absolute);
19666
20039
  if (stat === "file") {
@@ -20075,7 +20448,8 @@ class ArchitectureLedgerReadModelStore {
20075
20448
  return this.fallback.writeChangeSetPreview(changeSet);
20076
20449
  }
20077
20450
  async loadLedgerModel(workspace) {
20078
- const state = await this.localStore.readArchitectureLedgerState(architectureLedgerScopeForWorkspace(workspace));
20451
+ const scope = await this.localStore.resolveArchitectureLedgerScope(architectureLedgerScopeForWorkspace(workspace));
20452
+ const state = await this.localStore.readArchitectureLedgerState(scope);
20079
20453
  const projectedFiles = projectArchitectureLedgerStateToYamlFiles(state).map((file) => ({
20080
20454
  path: file.path,
20081
20455
  body: file.body,
@@ -20159,7 +20533,7 @@ class ArchctxDaemon {
20159
20533
  this.readModelStore = new ArchitectureLedgerReadModelStore(this.modelStore, this.localStore, this.architectureLedger);
20160
20534
  this.changeSetEngine = deps.changeSetEngine ?? new ChangeSetEngine({
20161
20535
  modelStore: this.modelStore,
20162
- projection: { rebuildGeneratedProjection },
20536
+ projection: { planGeneratedProjection },
20163
20537
  journal: this.localStore
20164
20538
  });
20165
20539
  this.devicePrivateKeySigner = deps.devicePrivateKeySigner;
@@ -21111,7 +21485,7 @@ class ArchctxDaemon {
21111
21485
  const absolute = resolve15(session.workspace.root, path);
21112
21486
  const body = `${JSON.stringify(waiver, null, 2)}
21113
21487
  `;
21114
- const expectedHash = existsSync12(absolute) ? digestJson({ body: readFileSync11(absolute, "utf8") }) : "missing";
21488
+ const expectedHash = existsSync13(absolute) ? digestJson({ body: readFileSync11(absolute, "utf8") }) : "missing";
21115
21489
  const draft = this.changeSetEngine.plan({
21116
21490
  id: input.id ?? `changeset.practice-waiver-${waiverId.replace(/[^A-Za-z0-9_-]/g, "-")}`,
21117
21491
  base: {
@@ -21450,6 +21824,16 @@ class ArchctxDaemon {
21450
21824
  const draft = this.changesets.get(input.id);
21451
21825
  if (!draft)
21452
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");
21453
21837
  const approved = input.approved ? this.changeSetEngine.approve(draft) : draft;
21454
21838
  let ledgerAppend;
21455
21839
  const writesLedger = architectureLedgerWriteAppendsEvents(this.architectureLedger.writeMode);
@@ -21466,6 +21850,7 @@ class ArchctxDaemon {
21466
21850
  relationCount: appended.relationCount,
21467
21851
  constraintCount: appended.constraintCount
21468
21852
  };
21853
+ return { journalCommitted: Boolean(journalId) };
21469
21854
  } : undefined
21470
21855
  });
21471
21856
  return okEnvelope("apply_update", {
@@ -21499,14 +21884,72 @@ class ArchctxDaemon {
21499
21884
  });
21500
21885
  if (journalId)
21501
21886
  await this.localStore.recordChangeSetLedgerPlan(journalId, { event: plan.event });
21502
- const result = await this.localStore.appendArchitectureEvents({
21503
- writer: "runtime-daemon",
21504
- events: [plan.event]
21505
- });
21506
- if (journalId)
21507
- 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);
21508
21889
  return result;
21509
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
+ }
21510
21953
  async ledgerState(root) {
21511
21954
  this.assertRunning();
21512
21955
  return okEnvelope("ledger.state", await this.architectureLedgerReadback(root));
@@ -21860,8 +22303,13 @@ class ArchctxDaemon {
21860
22303
  const scope = await this.architectureLedgerScope(root);
21861
22304
  const state = await this.localStore.readArchitectureLedgerState(scope);
21862
22305
  const projectedFiles = projectArchitectureLedgerStateToYamlFiles(state);
21863
- if (writes)
21864
- writeArchitectureProjectionFiles(root, projectedFiles);
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
+ }
21865
22313
  const drift = compareArchitectureLedgerStateToYaml({
21866
22314
  state,
21867
22315
  files: listModelFiles(root),
@@ -22011,7 +22459,7 @@ class ArchctxDaemon {
22011
22459
  const projectedFiles = projectArchitectureLedgerStateToYamlFiles(state);
22012
22460
  const currentManagedFiles = listModelFiles(root).filter((file) => isArchitectureLedgerManagedModelPath(file.path));
22013
22461
  const backupPlan = architectureProjectionRollbackBackup(currentManagedFiles);
22014
- const writeResult = writes ? replaceArchitectureProjectionFilesForYamlRollback(root, projectedFiles, currentManagedFiles, this.clock()) : { backup: backupPlan.backup, writtenPaths: [], removedPaths: [] };
22462
+ const writeResult = writes ? await this.applyArchitectureProjectionRollbackChangeSet(root, projectedFiles, currentManagedFiles, this.clock()) : { backup: backupPlan.backup, writtenPaths: [], removedPaths: [] };
22015
22463
  const drift = compareArchitectureLedgerStateToYaml({
22016
22464
  state,
22017
22465
  files: listModelFiles(root),
@@ -22050,9 +22498,12 @@ class ArchctxDaemon {
22050
22498
  return errorEnvelope("ledger.rebuild", "AC_SCHEMA_INVALID", "ledger rebuild currently requires --from-git");
22051
22499
  return this.withWriter(async () => {
22052
22500
  this.assertFreshWorktree(root, input.expectedWorktreeDigest, "ledger rebuild --from-git");
22053
- const scope = await this.architectureLedgerScope(root);
22501
+ const scope = await this.architectureLedgerGitScope(root);
22054
22502
  const files = listModelFiles(root);
22055
- const previousState = await this.localStore.readArchitectureLedgerState(scope);
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);
22056
22507
  const previousGraphDigest = architectureLedgerStateDigest(previousState);
22057
22508
  const rebuildCommand = input.acceptExternalProjection ? "archctx ledger rebuild --from-git --accept-external-projection" : "archctx ledger rebuild --from-git";
22058
22509
  const plan = planYamlToArchitectureLedgerRebuild({
@@ -22062,12 +22513,18 @@ class ArchctxDaemon {
22062
22513
  command: rebuildCommand,
22063
22514
  previousState
22064
22515
  });
22516
+ const importPlan = planYamlToArchitectureLedgerImport({
22517
+ ...scope,
22518
+ files,
22519
+ createdAt: this.clock(),
22520
+ command: rebuildCommand
22521
+ });
22065
22522
  if (plan.unsupportedFiles.length > 0) {
22066
22523
  return errorEnvelope("ledger.rebuild", "AC_SCHEMA_INVALID", "ledger rebuild requires supported YAML model files");
22067
22524
  }
22068
22525
  const cursor = architectureLedgerGitCursorFromPlan({ ...scope, plan });
22069
22526
  const previousCursor = await this.localStore.readArchitectureLedgerSourceCursor({
22070
- ...scope,
22527
+ ...authorityScope,
22071
22528
  cursorId: ARCHITECTURE_LEDGER_GIT_CURSOR_ID
22072
22529
  });
22073
22530
  const cursorChanged = previousCursor?.cursorDigest !== cursor.cursorDigest;
@@ -22083,7 +22540,13 @@ class ArchctxDaemon {
22083
22540
  let rebuildStatus = "unchanged";
22084
22541
  let proposedExternalProjectionChange;
22085
22542
  if (previousGraphDigest === plan.graphDigest) {
22086
- if (cursorChanged) {
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) {
22087
22550
  const cursorPlan = planGitCursorRefreshToArchitectureLedgerEvent({
22088
22551
  ...scope,
22089
22552
  cursor,
@@ -22099,7 +22562,7 @@ class ArchctxDaemon {
22099
22562
  }
22100
22563
  } else if (!previousStateEmpty && !input.acceptExternalProjection) {
22101
22564
  const proposal = planExternalProjectionChangeToArchitectureLedgerEvent({
22102
- ...scope,
22565
+ ...authorityScope,
22103
22566
  files,
22104
22567
  createdAt: this.clock(),
22105
22568
  command: rebuildCommand,
@@ -22122,11 +22585,12 @@ class ArchctxDaemon {
22122
22585
  } else {
22123
22586
  append = await this.localStore.appendArchitectureEvents({
22124
22587
  writer: "runtime-daemon",
22125
- events: [plan.event]
22588
+ events: [exactScopeHasEvents ? plan.event : importPlan.event]
22126
22589
  });
22127
22590
  rebuildStatus = previousStateEmpty ? "rebuilt" : "external-projection-accepted";
22128
22591
  }
22129
- const replay = await this.localStore.rebuildArchitectureLedgerCurrentState(scope);
22592
+ const replayScope = rebuildStatus === "external-projection-proposed" ? authorityScope : scope;
22593
+ const replay = await this.localStore.rebuildArchitectureLedgerCurrentState(replayScope);
22130
22594
  const drift = compareArchitectureLedgerStateToYaml({
22131
22595
  state: replay.state,
22132
22596
  files: listModelFiles(root),
@@ -22272,6 +22736,9 @@ class ArchctxDaemon {
22272
22736
  return this.running ? this.architectureLedgerContextPort(root) : undefined;
22273
22737
  }
22274
22738
  async architectureLedgerScope(root) {
22739
+ return this.localStore.resolveArchitectureLedgerScope(await this.architectureLedgerGitScope(root));
22740
+ }
22741
+ async architectureLedgerGitScope(root) {
22275
22742
  const session = await this.openSession(root);
22276
22743
  const paths = runtimeStatePaths2(root);
22277
22744
  return {
@@ -22334,7 +22801,7 @@ class ArchctxDaemon {
22334
22801
  cleanup: "remove-run-root"
22335
22802
  }
22336
22803
  };
22337
- if (existsSync12(paths.lockPath) || existsSync12(paths.manifestPath)) {
22804
+ if (existsSync13(paths.lockPath) || existsSync13(paths.manifestPath)) {
22338
22805
  removePathWithRetry(paths.runRoot);
22339
22806
  throw new Error(`developer-review-run-already-active: ${input.challenge.challengeId}`);
22340
22807
  }
@@ -22391,7 +22858,7 @@ class ArchctxDaemon {
22391
22858
  const errors = [];
22392
22859
  if (run.worktree) {
22393
22860
  try {
22394
- const hadWorktree = existsSync12(run.worktree.worktreeRoot);
22861
+ const hadWorktree = existsSync13(run.worktree.worktreeRoot);
22395
22862
  removeDetachedReviewWorktree(run.worktree);
22396
22863
  if (hadWorktree)
22397
22864
  removed.push("worktree");
@@ -22405,7 +22872,7 @@ class ArchctxDaemon {
22405
22872
  ["lock", run.lockPath]
22406
22873
  ]) {
22407
22874
  try {
22408
- const existed = existsSync12(path);
22875
+ const existed = existsSync13(path);
22409
22876
  removePathWithRetry(path);
22410
22877
  if (existed)
22411
22878
  removed.push(kind);
@@ -22434,7 +22901,7 @@ class ArchctxDaemon {
22434
22901
  removedLocks: [],
22435
22902
  skippedActive: []
22436
22903
  };
22437
- if (!existsSync12(stateDir))
22904
+ if (!existsSync13(stateDir))
22438
22905
  return recovery;
22439
22906
  for (const entry of readdirSync8(stateDir).sort()) {
22440
22907
  if (!entry.endsWith(".json"))
@@ -22802,7 +23269,7 @@ class ArchctxDaemon {
22802
23269
  }
22803
23270
  async restoreRepositorySessions() {
22804
23271
  for (const record of await this.localStore.listRepositorySessions()) {
22805
- if (!record.root || !existsSync12(record.root))
23272
+ if (!record.root || !existsSync13(record.root))
22806
23273
  continue;
22807
23274
  if (repositoryFingerprint2(record.root) !== record.repositoryId)
22808
23275
  continue;
@@ -23595,7 +24062,7 @@ function recoverStaleDaemonControlFiles(root = process.cwd(), options = {}) {
23595
24062
  rmSync8(connectionPath, { force: true });
23596
24063
  removed.push(connectionReason);
23597
24064
  }
23598
- if (existsSync12(lockPath) && isStaleLock(lockPath)) {
24065
+ if (existsSync13(lockPath) && isStaleLock(lockPath)) {
23599
24066
  rmSync8(lockPath, { force: true });
23600
24067
  removed.push("stale-lock-file");
23601
24068
  }
@@ -24043,14 +24510,14 @@ function expandWorkspacePackageJson(root, pattern) {
24043
24510
  return [];
24044
24511
  if (!pattern.includes("*")) {
24045
24512
  const path = resolve15(root, pattern, "package.json");
24046
- return existsSync12(path) ? [path] : [];
24513
+ return existsSync13(path) ? [path] : [];
24047
24514
  }
24048
24515
  if (!pattern.endsWith("/*"))
24049
24516
  return [];
24050
24517
  const base = resolve15(root, pattern.slice(0, -2));
24051
- if (!existsSync12(base) || !statSync6(base).isDirectory())
24518
+ if (!existsSync13(base) || !statSync6(base).isDirectory())
24052
24519
  return [];
24053
- return readdirSync8(base, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => resolve15(base, entry.name, "package.json")).filter((path) => existsSync12(path));
24520
+ return readdirSync8(base, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => resolve15(base, entry.name, "package.json")).filter((path) => existsSync13(path));
24054
24521
  }
24055
24522
  function exactVersionFromManifest(manifest, packageName) {
24056
24523
  if (!manifest || typeof manifest !== "object" || Array.isArray(manifest))
@@ -24066,7 +24533,7 @@ function exactVersionFromManifest(manifest, packageName) {
24066
24533
  return;
24067
24534
  }
24068
24535
  function readJsonFile(path) {
24069
- if (!existsSync12(path))
24536
+ if (!existsSync13(path))
24070
24537
  return;
24071
24538
  try {
24072
24539
  return JSON.parse(readFileSync11(path, "utf8"));
@@ -24079,7 +24546,7 @@ function isExactPackageVersion(value) {
24079
24546
  }
24080
24547
  function readContext7Lockfile(root) {
24081
24548
  const path = resolve15(root, CONTEXT7_LOCKFILE);
24082
- if (!existsSync12(path)) {
24549
+ if (!existsSync13(path)) {
24083
24550
  return {
24084
24551
  schemaVersion: CONTEXT7_LOCKFILE_SCHEMA_VERSION,
24085
24552
  provider: "context7",
@@ -24328,48 +24795,9 @@ function auditApproveResultPayload(runId, status, totalCount, issuedIssues) {
24328
24795
  issuedIssues
24329
24796
  };
24330
24797
  }
24331
- function writeArchitectureProjectionFiles(root, files) {
24332
- for (const file of files) {
24333
- const absolute = resolve15(root, file.path);
24334
- mkdirSync7(dirname8(absolute), { recursive: true });
24335
- writeFileSync6(absolute, file.body.endsWith(`
24336
- `) ? file.body : `${file.body}
24337
- `, "utf8");
24338
- }
24339
- }
24340
- function replaceArchitectureProjectionFilesForYamlRollback(root, projectedFiles, currentFiles, createdAt) {
24341
- const targetPaths = new Set(projectedFiles.map((file) => file.path));
24342
- const backupBase = `.archcontext/backups/ledger-rollback/${safePathSegment(createdAt)}`;
24343
- const backupRelativePath = uniqueBackupPath(root, backupBase);
24344
- const manifestPath = `${backupRelativePath}/manifest.json`;
24345
- const { backup, manifest } = architectureProjectionRollbackBackup(currentFiles, {
24346
- createdAt,
24347
- path: backupRelativePath,
24348
- manifestPath
24349
- });
24350
- for (const file of currentFiles) {
24351
- const backupPath = join8(backupRelativePath, archContextRelativePath(file.path));
24352
- const absolute = resolve15(root, backupPath);
24353
- mkdirSync7(dirname8(absolute), { recursive: true });
24354
- writeFileSync6(absolute, file.body, "utf8");
24355
- }
24356
- const manifestAbsolute = resolve15(root, manifestPath);
24357
- mkdirSync7(dirname8(manifestAbsolute), { recursive: true });
24358
- writeFileSync6(manifestAbsolute, `${JSON.stringify(manifest, null, 2)}
24359
- `, "utf8");
24360
- const removedPaths = [];
24361
- for (const file of currentFiles) {
24362
- if (targetPaths.has(file.path))
24363
- continue;
24364
- rmSync8(resolve15(root, file.path), { force: true });
24365
- removedPaths.push(file.path);
24366
- }
24367
- writeArchitectureProjectionFiles(root, projectedFiles);
24368
- return {
24369
- backup,
24370
- writtenPaths: projectedFiles.map((file) => file.path),
24371
- removedPaths
24372
- };
24798
+ function expectedFileHash(root, path) {
24799
+ const absolute = resolve15(root, path);
24800
+ return existsSync13(absolute) ? digestJson({ body: readFileSync11(absolute, "utf8") }) : "missing";
24373
24801
  }
24374
24802
  function architectureProjectionRollbackBackup(files, options = {}) {
24375
24803
  const manifest = {
@@ -24396,7 +24824,7 @@ function architectureProjectionRollbackBackup(files, options = {}) {
24396
24824
  function uniqueBackupPath(root, backupBase) {
24397
24825
  let candidate = backupBase;
24398
24826
  let suffix = 2;
24399
- while (existsSync12(resolve15(root, candidate))) {
24827
+ while (existsSync13(resolve15(root, candidate))) {
24400
24828
  candidate = `${backupBase}-${suffix}`;
24401
24829
  suffix += 1;
24402
24830
  }
@@ -24405,7 +24833,7 @@ function uniqueBackupPath(root, backupBase) {
24405
24833
  function uniqueRuntimeBackupPath(backupBase) {
24406
24834
  let candidate = backupBase;
24407
24835
  let suffix = 2;
24408
- while (existsSync12(candidate)) {
24836
+ while (existsSync13(candidate)) {
24409
24837
  candidate = backupBase.replace(/\.sqlite$/, `-${suffix}.sqlite`);
24410
24838
  suffix += 1;
24411
24839
  }
@@ -24450,7 +24878,7 @@ function isValidRuntimeRpcConnection(value) {
24450
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";
24451
24879
  }
24452
24880
  function staleConnectionFileReason(path, removeUnhealthyConnection) {
24453
- if (!existsSync12(path))
24881
+ if (!existsSync13(path))
24454
24882
  return;
24455
24883
  try {
24456
24884
  if (!isPrivateControlFile(path))
@@ -24512,7 +24940,7 @@ function parsePracticeCheckpointBaselineState(state, repositoryId, taskSessionId
24512
24940
  return record;
24513
24941
  }
24514
24942
  function completeTaskProjectionDrift(root) {
24515
- if (!existsSync12(resolve15(root, "docs/architecture/.projection-manifest.json")))
24943
+ if (!existsSync13(resolve15(root, "docs/architecture/.projection-manifest.json")))
24516
24944
  return;
24517
24945
  const loaded = loadArchitectureDocumentationInputs(root);
24518
24946
  const sourceDigest = architectureDocumentationSourceDigest({
@@ -24788,7 +25216,7 @@ init_src();
24788
25216
  // packages/local-runtime/runtime-daemon/src/index.ts
24789
25217
  import { randomBytes as randomBytes2 } from "node:crypto";
24790
25218
  import { execFileSync as execFileSync7 } from "node:child_process";
24791
- import { chmodSync as chmodSync4, closeSync as closeSync6, existsSync as existsSync13, 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";
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";
24792
25220
  import { createServer as createServer2 } from "node:http";
24793
25221
  import { tmpdir as tmpdir4 } from "node:os";
24794
25222
  import { dirname as dirname9, join as join9, resolve as resolve16 } from "node:path";
@@ -24901,7 +25329,8 @@ class ArchitectureLedgerReadModelStore2 {
24901
25329
  return this.fallback.writeChangeSetPreview(changeSet);
24902
25330
  }
24903
25331
  async loadLedgerModel(workspace) {
24904
- const state = await this.localStore.readArchitectureLedgerState(architectureLedgerScopeForWorkspace2(workspace));
25332
+ const scope = await this.localStore.resolveArchitectureLedgerScope(architectureLedgerScopeForWorkspace2(workspace));
25333
+ const state = await this.localStore.readArchitectureLedgerState(scope);
24905
25334
  const projectedFiles = projectArchitectureLedgerStateToYamlFiles(state).map((file) => ({
24906
25335
  path: file.path,
24907
25336
  body: file.body,
@@ -24985,7 +25414,7 @@ class ArchctxDaemon2 {
24985
25414
  this.readModelStore = new ArchitectureLedgerReadModelStore2(this.modelStore, this.localStore, this.architectureLedger);
24986
25415
  this.changeSetEngine = deps.changeSetEngine ?? new ChangeSetEngine({
24987
25416
  modelStore: this.modelStore,
24988
- projection: { rebuildGeneratedProjection },
25417
+ projection: { planGeneratedProjection },
24989
25418
  journal: this.localStore
24990
25419
  });
24991
25420
  this.devicePrivateKeySigner = deps.devicePrivateKeySigner;
@@ -25937,7 +26366,7 @@ class ArchctxDaemon2 {
25937
26366
  const absolute = resolve16(session.workspace.root, path);
25938
26367
  const body = `${JSON.stringify(waiver, null, 2)}
25939
26368
  `;
25940
- const expectedHash = existsSync13(absolute) ? digestJson({ body: readFileSync12(absolute, "utf8") }) : "missing";
26369
+ const expectedHash = existsSync14(absolute) ? digestJson({ body: readFileSync12(absolute, "utf8") }) : "missing";
25941
26370
  const draft = this.changeSetEngine.plan({
25942
26371
  id: input.id ?? `changeset.practice-waiver-${waiverId.replace(/[^A-Za-z0-9_-]/g, "-")}`,
25943
26372
  base: {
@@ -26276,6 +26705,16 @@ class ArchctxDaemon2 {
26276
26705
  const draft = this.changesets.get(input.id);
26277
26706
  if (!draft)
26278
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");
26279
26718
  const approved = input.approved ? this.changeSetEngine.approve(draft) : draft;
26280
26719
  let ledgerAppend;
26281
26720
  const writesLedger = architectureLedgerWriteAppendsEvents2(this.architectureLedger.writeMode);
@@ -26292,6 +26731,7 @@ class ArchctxDaemon2 {
26292
26731
  relationCount: appended.relationCount,
26293
26732
  constraintCount: appended.constraintCount
26294
26733
  };
26734
+ return { journalCommitted: Boolean(journalId) };
26295
26735
  } : undefined
26296
26736
  });
26297
26737
  return okEnvelope("apply_update", {
@@ -26325,14 +26765,72 @@ class ArchctxDaemon2 {
26325
26765
  });
26326
26766
  if (journalId)
26327
26767
  await this.localStore.recordChangeSetLedgerPlan(journalId, { event: plan.event });
26328
- const result = await this.localStore.appendArchitectureEvents({
26329
- writer: "runtime-daemon",
26330
- events: [plan.event]
26331
- });
26332
- if (journalId)
26333
- 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);
26334
26770
  return result;
26335
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
+ }
26336
26834
  async ledgerState(root) {
26337
26835
  this.assertRunning();
26338
26836
  return okEnvelope("ledger.state", await this.architectureLedgerReadback(root));
@@ -26686,8 +27184,13 @@ class ArchctxDaemon2 {
26686
27184
  const scope = await this.architectureLedgerScope(root);
26687
27185
  const state = await this.localStore.readArchitectureLedgerState(scope);
26688
27186
  const projectedFiles = projectArchitectureLedgerStateToYamlFiles(state);
26689
- if (writes)
26690
- writeArchitectureProjectionFiles2(root, projectedFiles);
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
+ }
26691
27194
  const drift = compareArchitectureLedgerStateToYaml({
26692
27195
  state,
26693
27196
  files: listModelFiles(root),
@@ -26837,7 +27340,7 @@ class ArchctxDaemon2 {
26837
27340
  const projectedFiles = projectArchitectureLedgerStateToYamlFiles(state);
26838
27341
  const currentManagedFiles = listModelFiles(root).filter((file) => isArchitectureLedgerManagedModelPath2(file.path));
26839
27342
  const backupPlan = architectureProjectionRollbackBackup2(currentManagedFiles);
26840
- const writeResult = writes ? replaceArchitectureProjectionFilesForYamlRollback2(root, projectedFiles, currentManagedFiles, this.clock()) : { backup: backupPlan.backup, writtenPaths: [], removedPaths: [] };
27343
+ const writeResult = writes ? await this.applyArchitectureProjectionRollbackChangeSet(root, projectedFiles, currentManagedFiles, this.clock()) : { backup: backupPlan.backup, writtenPaths: [], removedPaths: [] };
26841
27344
  const drift = compareArchitectureLedgerStateToYaml({
26842
27345
  state,
26843
27346
  files: listModelFiles(root),
@@ -26876,9 +27379,12 @@ class ArchctxDaemon2 {
26876
27379
  return errorEnvelope("ledger.rebuild", "AC_SCHEMA_INVALID", "ledger rebuild currently requires --from-git");
26877
27380
  return this.withWriter(async () => {
26878
27381
  this.assertFreshWorktree(root, input.expectedWorktreeDigest, "ledger rebuild --from-git");
26879
- const scope = await this.architectureLedgerScope(root);
27382
+ const scope = await this.architectureLedgerGitScope(root);
26880
27383
  const files = listModelFiles(root);
26881
- const previousState = await this.localStore.readArchitectureLedgerState(scope);
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);
26882
27388
  const previousGraphDigest = architectureLedgerStateDigest(previousState);
26883
27389
  const rebuildCommand = input.acceptExternalProjection ? "archctx ledger rebuild --from-git --accept-external-projection" : "archctx ledger rebuild --from-git";
26884
27390
  const plan = planYamlToArchitectureLedgerRebuild({
@@ -26888,12 +27394,18 @@ class ArchctxDaemon2 {
26888
27394
  command: rebuildCommand,
26889
27395
  previousState
26890
27396
  });
27397
+ const importPlan = planYamlToArchitectureLedgerImport({
27398
+ ...scope,
27399
+ files,
27400
+ createdAt: this.clock(),
27401
+ command: rebuildCommand
27402
+ });
26891
27403
  if (plan.unsupportedFiles.length > 0) {
26892
27404
  return errorEnvelope("ledger.rebuild", "AC_SCHEMA_INVALID", "ledger rebuild requires supported YAML model files");
26893
27405
  }
26894
27406
  const cursor = architectureLedgerGitCursorFromPlan({ ...scope, plan });
26895
27407
  const previousCursor = await this.localStore.readArchitectureLedgerSourceCursor({
26896
- ...scope,
27408
+ ...authorityScope,
26897
27409
  cursorId: ARCHITECTURE_LEDGER_GIT_CURSOR_ID
26898
27410
  });
26899
27411
  const cursorChanged = previousCursor?.cursorDigest !== cursor.cursorDigest;
@@ -26909,7 +27421,13 @@ class ArchctxDaemon2 {
26909
27421
  let rebuildStatus = "unchanged";
26910
27422
  let proposedExternalProjectionChange;
26911
27423
  if (previousGraphDigest === plan.graphDigest) {
26912
- if (cursorChanged) {
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) {
26913
27431
  const cursorPlan = planGitCursorRefreshToArchitectureLedgerEvent({
26914
27432
  ...scope,
26915
27433
  cursor,
@@ -26925,7 +27443,7 @@ class ArchctxDaemon2 {
26925
27443
  }
26926
27444
  } else if (!previousStateEmpty && !input.acceptExternalProjection) {
26927
27445
  const proposal = planExternalProjectionChangeToArchitectureLedgerEvent({
26928
- ...scope,
27446
+ ...authorityScope,
26929
27447
  files,
26930
27448
  createdAt: this.clock(),
26931
27449
  command: rebuildCommand,
@@ -26948,11 +27466,12 @@ class ArchctxDaemon2 {
26948
27466
  } else {
26949
27467
  append = await this.localStore.appendArchitectureEvents({
26950
27468
  writer: "runtime-daemon",
26951
- events: [plan.event]
27469
+ events: [exactScopeHasEvents ? plan.event : importPlan.event]
26952
27470
  });
26953
27471
  rebuildStatus = previousStateEmpty ? "rebuilt" : "external-projection-accepted";
26954
27472
  }
26955
- const replay = await this.localStore.rebuildArchitectureLedgerCurrentState(scope);
27473
+ const replayScope = rebuildStatus === "external-projection-proposed" ? authorityScope : scope;
27474
+ const replay = await this.localStore.rebuildArchitectureLedgerCurrentState(replayScope);
26956
27475
  const drift = compareArchitectureLedgerStateToYaml({
26957
27476
  state: replay.state,
26958
27477
  files: listModelFiles(root),
@@ -27098,6 +27617,9 @@ class ArchctxDaemon2 {
27098
27617
  return this.running ? this.architectureLedgerContextPort(root) : undefined;
27099
27618
  }
27100
27619
  async architectureLedgerScope(root) {
27620
+ return this.localStore.resolveArchitectureLedgerScope(await this.architectureLedgerGitScope(root));
27621
+ }
27622
+ async architectureLedgerGitScope(root) {
27101
27623
  const session = await this.openSession(root);
27102
27624
  const paths = runtimeStatePaths2(root);
27103
27625
  return {
@@ -27160,7 +27682,7 @@ class ArchctxDaemon2 {
27160
27682
  cleanup: "remove-run-root"
27161
27683
  }
27162
27684
  };
27163
- if (existsSync13(paths.lockPath) || existsSync13(paths.manifestPath)) {
27685
+ if (existsSync14(paths.lockPath) || existsSync14(paths.manifestPath)) {
27164
27686
  removePathWithRetry(paths.runRoot);
27165
27687
  throw new Error(`developer-review-run-already-active: ${input.challenge.challengeId}`);
27166
27688
  }
@@ -27217,7 +27739,7 @@ class ArchctxDaemon2 {
27217
27739
  const errors = [];
27218
27740
  if (run.worktree) {
27219
27741
  try {
27220
- const hadWorktree = existsSync13(run.worktree.worktreeRoot);
27742
+ const hadWorktree = existsSync14(run.worktree.worktreeRoot);
27221
27743
  removeDetachedReviewWorktree(run.worktree);
27222
27744
  if (hadWorktree)
27223
27745
  removed.push("worktree");
@@ -27231,7 +27753,7 @@ class ArchctxDaemon2 {
27231
27753
  ["lock", run.lockPath]
27232
27754
  ]) {
27233
27755
  try {
27234
- const existed = existsSync13(path);
27756
+ const existed = existsSync14(path);
27235
27757
  removePathWithRetry(path);
27236
27758
  if (existed)
27237
27759
  removed.push(kind);
@@ -27260,7 +27782,7 @@ class ArchctxDaemon2 {
27260
27782
  removedLocks: [],
27261
27783
  skippedActive: []
27262
27784
  };
27263
- if (!existsSync13(stateDir))
27785
+ if (!existsSync14(stateDir))
27264
27786
  return recovery;
27265
27787
  for (const entry of readdirSync9(stateDir).sort()) {
27266
27788
  if (!entry.endsWith(".json"))
@@ -27628,7 +28150,7 @@ class ArchctxDaemon2 {
27628
28150
  }
27629
28151
  async restoreRepositorySessions() {
27630
28152
  for (const record of await this.localStore.listRepositorySessions()) {
27631
- if (!record.root || !existsSync13(record.root))
28153
+ if (!record.root || !existsSync14(record.root))
27632
28154
  continue;
27633
28155
  if (repositoryFingerprint2(record.root) !== record.repositoryId)
27634
28156
  continue;
@@ -28534,14 +29056,14 @@ function expandWorkspacePackageJson2(root, pattern) {
28534
29056
  return [];
28535
29057
  if (!pattern.includes("*")) {
28536
29058
  const path = resolve16(root, pattern, "package.json");
28537
- return existsSync13(path) ? [path] : [];
29059
+ return existsSync14(path) ? [path] : [];
28538
29060
  }
28539
29061
  if (!pattern.endsWith("/*"))
28540
29062
  return [];
28541
29063
  const base = resolve16(root, pattern.slice(0, -2));
28542
- if (!existsSync13(base) || !statSync7(base).isDirectory())
29064
+ if (!existsSync14(base) || !statSync7(base).isDirectory())
28543
29065
  return [];
28544
- return readdirSync9(base, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => resolve16(base, entry.name, "package.json")).filter((path) => existsSync13(path));
29066
+ return readdirSync9(base, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => resolve16(base, entry.name, "package.json")).filter((path) => existsSync14(path));
28545
29067
  }
28546
29068
  function exactVersionFromManifest2(manifest, packageName) {
28547
29069
  if (!manifest || typeof manifest !== "object" || Array.isArray(manifest))
@@ -28557,7 +29079,7 @@ function exactVersionFromManifest2(manifest, packageName) {
28557
29079
  return;
28558
29080
  }
28559
29081
  function readJsonFile2(path) {
28560
- if (!existsSync13(path))
29082
+ if (!existsSync14(path))
28561
29083
  return;
28562
29084
  try {
28563
29085
  return JSON.parse(readFileSync12(path, "utf8"));
@@ -28570,7 +29092,7 @@ function isExactPackageVersion2(value) {
28570
29092
  }
28571
29093
  function readContext7Lockfile2(root) {
28572
29094
  const path = resolve16(root, CONTEXT7_LOCKFILE2);
28573
- if (!existsSync13(path)) {
29095
+ if (!existsSync14(path)) {
28574
29096
  return {
28575
29097
  schemaVersion: CONTEXT7_LOCKFILE_SCHEMA_VERSION,
28576
29098
  provider: "context7",
@@ -28799,48 +29321,9 @@ function auditApproveResultPayload2(runId, status, totalCount, issuedIssues) {
28799
29321
  issuedIssues
28800
29322
  };
28801
29323
  }
28802
- function writeArchitectureProjectionFiles2(root, files) {
28803
- for (const file of files) {
28804
- const absolute = resolve16(root, file.path);
28805
- mkdirSync8(dirname9(absolute), { recursive: true });
28806
- writeFileSync7(absolute, file.body.endsWith(`
28807
- `) ? file.body : `${file.body}
28808
- `, "utf8");
28809
- }
28810
- }
28811
- function replaceArchitectureProjectionFilesForYamlRollback2(root, projectedFiles, currentFiles, createdAt) {
28812
- const targetPaths = new Set(projectedFiles.map((file) => file.path));
28813
- const backupBase = `.archcontext/backups/ledger-rollback/${safePathSegment2(createdAt)}`;
28814
- const backupRelativePath = uniqueBackupPath2(root, backupBase);
28815
- const manifestPath = `${backupRelativePath}/manifest.json`;
28816
- const { backup, manifest } = architectureProjectionRollbackBackup2(currentFiles, {
28817
- createdAt,
28818
- path: backupRelativePath,
28819
- manifestPath
28820
- });
28821
- for (const file of currentFiles) {
28822
- const backupPath = join9(backupRelativePath, archContextRelativePath2(file.path));
28823
- const absolute = resolve16(root, backupPath);
28824
- mkdirSync8(dirname9(absolute), { recursive: true });
28825
- writeFileSync7(absolute, file.body, "utf8");
28826
- }
28827
- const manifestAbsolute = resolve16(root, manifestPath);
28828
- mkdirSync8(dirname9(manifestAbsolute), { recursive: true });
28829
- writeFileSync7(manifestAbsolute, `${JSON.stringify(manifest, null, 2)}
28830
- `, "utf8");
28831
- const removedPaths = [];
28832
- for (const file of currentFiles) {
28833
- if (targetPaths.has(file.path))
28834
- continue;
28835
- rmSync9(resolve16(root, file.path), { force: true });
28836
- removedPaths.push(file.path);
28837
- }
28838
- writeArchitectureProjectionFiles2(root, projectedFiles);
28839
- return {
28840
- backup,
28841
- writtenPaths: projectedFiles.map((file) => file.path),
28842
- removedPaths
28843
- };
29324
+ function expectedFileHash2(root, path) {
29325
+ const absolute = resolve16(root, path);
29326
+ return existsSync14(absolute) ? digestJson({ body: readFileSync12(absolute, "utf8") }) : "missing";
28844
29327
  }
28845
29328
  function architectureProjectionRollbackBackup2(files, options = {}) {
28846
29329
  const manifest = {
@@ -28867,7 +29350,7 @@ function architectureProjectionRollbackBackup2(files, options = {}) {
28867
29350
  function uniqueBackupPath2(root, backupBase) {
28868
29351
  let candidate = backupBase;
28869
29352
  let suffix = 2;
28870
- while (existsSync13(resolve16(root, candidate))) {
29353
+ while (existsSync14(resolve16(root, candidate))) {
28871
29354
  candidate = `${backupBase}-${suffix}`;
28872
29355
  suffix += 1;
28873
29356
  }
@@ -28876,7 +29359,7 @@ function uniqueBackupPath2(root, backupBase) {
28876
29359
  function uniqueRuntimeBackupPath2(backupBase) {
28877
29360
  let candidate = backupBase;
28878
29361
  let suffix = 2;
28879
- while (existsSync13(candidate)) {
29362
+ while (existsSync14(candidate)) {
28880
29363
  candidate = backupBase.replace(/\.sqlite$/, `-${suffix}.sqlite`);
28881
29364
  suffix += 1;
28882
29365
  }
@@ -28938,7 +29421,7 @@ function parsePracticeCheckpointBaselineState2(state, repositoryId, taskSessionI
28938
29421
  return record;
28939
29422
  }
28940
29423
  function completeTaskProjectionDrift2(root) {
28941
- if (!existsSync13(resolve16(root, "docs/architecture/.projection-manifest.json")))
29424
+ if (!existsSync14(resolve16(root, "docs/architecture/.projection-manifest.json")))
28942
29425
  return;
28943
29426
  const loaded = loadArchitectureDocumentationInputs(root);
28944
29427
  const sourceDigest = architectureDocumentationSourceDigest({
@@ -30197,37 +30680,10 @@ function buildArchitectureDocsProjection(root, generatedAt) {
30197
30680
  sourceDigest,
30198
30681
  generatedAt
30199
30682
  });
30200
- const manifestBody = `${JSON.stringify({
30201
- schemaVersion: "archcontext.architecture-docs-projection-manifest/v1",
30202
- rendererVersion: plan.rendererVersion,
30203
- sourceDigest: plan.sourceDigest,
30204
- projectionDigest: plan.projectionDigest,
30205
- targetCount: plan.targets.length,
30206
- fileCount: plan.files.length,
30207
- targets: plan.targets.map((target) => ({
30208
- targetId: target.targetId,
30209
- type: target.type,
30210
- scope: target.scope,
30211
- path: target.path,
30212
- ownership: target.ownership,
30213
- rendererVersion: target.rendererVersion,
30214
- format: target.format,
30215
- sourceDigest: target.sourceDigest,
30216
- outputDigest: target.outputDigest
30217
- }))
30218
- }, null, 2)}
30219
- `;
30220
- const manifest = {
30221
- path: "docs/architecture/.projection-manifest.json",
30222
- body: manifestBody,
30223
- digest: digestJson({ path: "docs/architecture/.projection-manifest.json", body: manifestBody }),
30224
- target: plan.targets[0],
30225
- generatedBodyDigest: digestJson({ body: manifestBody })
30226
- };
30227
30683
  return {
30228
30684
  plan,
30229
- manifest,
30230
- files: [...plan.files, manifest]
30685
+ manifest: plan.manifest,
30686
+ files: [...plan.files, plan.manifest]
30231
30687
  };
30232
30688
  }
30233
30689
  function architectureDocsRenderProjectionOperation(root, files) {
@@ -30243,7 +30699,7 @@ function architectureDocsRenderProjectionOperation(root, files) {
30243
30699
  }
30244
30700
  function currentBodyHash(root, path) {
30245
30701
  const absolute = resolve17(root, path);
30246
- return existsSync14(absolute) ? digestJson({ body: readFileSync13(absolute, "utf8") }) : "missing";
30702
+ return existsSync15(absolute) ? digestJson({ body: readFileSync13(absolute, "utf8") }) : "missing";
30247
30703
  }
30248
30704
  async function runPracticesCommand(args2, cwd, daemon) {
30249
30705
  const subcommand = args2[0] ?? "list";
@@ -30591,7 +31047,7 @@ function auditManifestGateRoot(cwd) {
30591
31047
  }
30592
31048
  function auditGithubIssuesEnabled(cwd) {
30593
31049
  const manifestPath = resolve17(auditManifestGateRoot(cwd), ".archcontext/manifest.yaml");
30594
- if (!existsSync14(manifestPath))
31050
+ if (!existsSync15(manifestPath))
30595
31051
  return false;
30596
31052
  let raw;
30597
31053
  try {
@@ -31204,7 +31660,7 @@ async function writeGithubDeveloperReviewState(cwd, state) {
31204
31660
  return { state, path };
31205
31661
  }
31206
31662
  function readGithubDeveloperReviewState(path) {
31207
- if (!existsSync14(path))
31663
+ if (!existsSync15(path))
31208
31664
  return;
31209
31665
  try {
31210
31666
  const parsed = JSON.parse(readFileSync13(path, "utf8"));
@@ -31256,7 +31712,7 @@ function defaultGithubConnectionPath(cwd) {
31256
31712
  return join10(dirname10(defaultDaemonConnectionPath(cwd)), "github-connection.json");
31257
31713
  }
31258
31714
  function readGithubConnection(path) {
31259
- if (!existsSync14(path))
31715
+ if (!existsSync15(path))
31260
31716
  return;
31261
31717
  try {
31262
31718
  const parsed = JSON.parse(readFileSync13(path, "utf8"));
@@ -31770,10 +32226,10 @@ function doctorSqlite(cwd) {
31770
32226
  const legacyLocalStore = inspectLegacyLocalStoreMigration(cwd);
31771
32227
  return {
31772
32228
  path,
31773
- exists: existsSync14(path),
32229
+ exists: existsSync15(path),
31774
32230
  migrations: productVersionManifest().runtime.sqliteMigrations,
31775
32231
  legacyPath: paths.legacyLocalStorePath,
31776
- legacyExists: existsSync14(paths.legacyLocalStorePath),
32232
+ legacyExists: existsSync15(paths.legacyLocalStorePath),
31777
32233
  legacyLocalStore
31778
32234
  };
31779
32235
  }
@@ -31804,7 +32260,7 @@ function runtimePathsReport(cwd) {
31804
32260
  };
31805
32261
  }
31806
32262
  function pathAccess(path) {
31807
- const exists = existsSync14(path);
32263
+ const exists = existsSync15(path);
31808
32264
  return {
31809
32265
  path,
31810
32266
  exists,