archctx 0.6.1 → 0.6.3

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
@@ -1331,7 +1331,7 @@ function productVersionManifest() {
1331
1331
  }
1332
1332
  };
1333
1333
  }
1334
- var ARCHCONTEXT_PRODUCT_NAME = "archctx", ARCHCONTEXT_PRODUCT_VERSION = "0.6.1", ARCHCONTEXT_PACKAGE_MANAGER = "bun@1.4.0", ARCHCONTEXT_NODE_RANGE = ">=22.22 <26", LOCAL_RUNTIME_RPC_SCHEMA_VERSION = "archcontext.runtime-rpc/v1", ARCHCONTEXT_SCHEMA_SET_VERSION = "2026-07-11.explorer-view-v2";
1334
+ var ARCHCONTEXT_PRODUCT_NAME = "archctx", ARCHCONTEXT_PRODUCT_VERSION = "0.6.3", ARCHCONTEXT_PACKAGE_MANAGER = "bun@1.4.0", ARCHCONTEXT_NODE_RANGE = ">=22.22 <26", LOCAL_RUNTIME_RPC_SCHEMA_VERSION = "archcontext.runtime-rpc/v1", ARCHCONTEXT_SCHEMA_SET_VERSION = "2026-07-11.explorer-view-v2";
1335
1335
 
1336
1336
  // packages/contracts/src/refactor.ts
1337
1337
  function refactorProposalDigest(proposal) {
@@ -18114,6 +18114,14 @@ ${wrapped}`;
18114
18114
  }
18115
18115
  return `${existing.slice(0, region.start)}${wrapped}${existing.slice(region.end)}`;
18116
18116
  }
18117
+ function comparableExpectedManifestDigest(expected, actual) {
18118
+ const baseline = actual && typeof actual === "object" && !Array.isArray(actual) ? actual.semanticBaseline : undefined;
18119
+ if (!baseline || typeof baseline !== "object" || Array.isArray(baseline) || "evidence" in baseline)
18120
+ return expected.digest;
18121
+ const value = JSON.parse(expected.body);
18122
+ const { evidence: _evidence, ...semanticBaseline } = value.semanticBaseline;
18123
+ return digestJson({ ...value, semanticBaseline });
18124
+ }
18117
18125
  function architectureDocumentationProjectionDrift(input) {
18118
18126
  const existingByPath = new Map(input.existingFiles.map((file) => [file.path, file]));
18119
18127
  const expectedByPath = new Map(input.expectedFiles.map((file) => [file.path, file]));
@@ -18130,7 +18138,7 @@ function architectureDocumentationProjectionDrift(input) {
18130
18138
  try {
18131
18139
  const parsed = JSON.parse(existingManifest.body);
18132
18140
  const actualDigest = digestJson(parsed);
18133
- if (actualDigest !== input.expectedManifest.digest) {
18141
+ if (actualDigest !== comparableExpectedManifestDigest(input.expectedManifest, parsed)) {
18134
18142
  diffs.push({
18135
18143
  path: input.expectedManifest.path,
18136
18144
  reasonCode: "projection-manifest-stale",
@@ -19981,8 +19989,8 @@ function localEgressStatus(env = process.env, live = {}) {
19981
19989
 
19982
19990
  // packages/surfaces/cli/src/main.ts
19983
19991
  import { spawn as spawn2, spawnSync as spawnSync3 } from "child_process";
19984
- import { accessSync as accessSync2, closeSync as closeSync10, constants as constants2, existsSync as existsSync25, mkdirSync as mkdirSync13, openSync as openSync10, readFileSync as readFileSync27, rmSync as rmSync16, statSync as statSync10 } from "fs";
19985
- import { dirname as dirname15, join as join18, resolve as resolve26 } from "path";
19992
+ import { accessSync as accessSync2, closeSync as closeSync10, constants as constants2, existsSync as existsSync26, mkdirSync as mkdirSync13, openSync as openSync10, readFileSync as readFileSync27, rmSync as rmSync16, statSync as statSync10 } from "fs";
19993
+ import { dirname as dirname15, join as join18, resolve as resolve27 } from "path";
19986
19994
  import { fileURLToPath as fileURLToPath2 } from "url";
19987
19995
 
19988
19996
  // packages/contracts/src/index.ts
@@ -46938,6 +46946,12 @@ function recommendationActorKind(input) {
46938
46946
  return "cli";
46939
46947
  }
46940
46948
 
46949
+ // packages/local-runtime/runtime-daemon/src/changeset-authority.ts
46950
+ import { randomBytes as randomBytes5 } from "node:crypto";
46951
+ import { existsSync as existsSync24, readFileSync as readFileSync26 } from "node:fs";
46952
+ import { resolve as resolve25 } from "node:path";
46953
+ init_src();
46954
+
46941
46955
  // packages/local-runtime/runtime-daemon/src/committed-change-acceptance.ts
46942
46956
  import { execFileSync as execFileSync10 } from "node:child_process";
46943
46957
  import { closeSync as closeSync8, constants as fsConstants3, fstatSync as fstatSync3, lstatSync as lstatSync11, openSync as openSync9, readdirSync as readdirSync12, readFileSync as readFileSync25 } from "node:fs";
@@ -47369,12 +47383,546 @@ function sameStrings(left, right) {
47369
47383
  return left.length === right.length && left.every((value, index) => value === right[index]);
47370
47384
  }
47371
47385
 
47386
+ // packages/local-runtime/runtime-daemon/src/changeset-authority.ts
47387
+ class RuntimeUpdateInputError extends Error {
47388
+ }
47389
+ function decodeRuntimeWorktreeDigestProfile(value, field) {
47390
+ switch (value) {
47391
+ case "repository":
47392
+ case "architecture-documentation-projection":
47393
+ return value;
47394
+ default:
47395
+ throw new RuntimeUpdateInputError(`${field} must be repository or architecture-documentation-projection`);
47396
+ }
47397
+ }
47398
+ function decodeRuntimePlanUpdateInput(value) {
47399
+ const input = runtimeUpdateInputRecord(value, "plan_update input");
47400
+ if (typeof input.id !== "string" || input.id.length === 0) {
47401
+ throw new RuntimeUpdateInputError("plan_update id must be a non-empty string");
47402
+ }
47403
+ if (!Array.isArray(input.operations)) {
47404
+ throw new RuntimeUpdateInputError("plan_update operations must be an array");
47405
+ }
47406
+ if (input.approvalChannel !== undefined && input.approvalChannel !== "mcp") {
47407
+ throw new RuntimeUpdateInputError("plan_update approvalChannel must be mcp");
47408
+ }
47409
+ let worktreeDigestPrecondition;
47410
+ if (input.worktreeDigestPrecondition !== undefined) {
47411
+ const precondition = runtimeUpdateInputRecord(input.worktreeDigestPrecondition, "plan_update worktreeDigestPrecondition");
47412
+ const profile = decodeRuntimeWorktreeDigestProfile(precondition.profile, "plan_update worktreeDigestPrecondition.profile");
47413
+ if (profile !== "architecture-documentation-projection") {
47414
+ throw new RuntimeUpdateInputError("plan_update worktreeDigestPrecondition.profile must be architecture-documentation-projection");
47415
+ }
47416
+ if (typeof precondition.expectedDigest !== "string" || precondition.expectedDigest.length === 0) {
47417
+ throw new RuntimeUpdateInputError("plan_update worktreeDigestPrecondition.expectedDigest must be a non-empty string");
47418
+ }
47419
+ worktreeDigestPrecondition = { profile, expectedDigest: precondition.expectedDigest };
47420
+ }
47421
+ return {
47422
+ id: input.id,
47423
+ operations: input.operations,
47424
+ ...input.approvalChannel === "mcp" ? { approvalChannel: "mcp" } : {},
47425
+ ...input.reason === undefined ? {} : { reason: input.reason },
47426
+ ...worktreeDigestPrecondition === undefined ? {} : { worktreeDigestPrecondition }
47427
+ };
47428
+ }
47429
+ function decodeRuntimeApplyUpdateInput(value) {
47430
+ const input = runtimeUpdateInputRecord(value, "apply_update input");
47431
+ if (typeof input.id !== "string" || input.id.length === 0) {
47432
+ throw new RuntimeUpdateInputError("apply_update id must be a non-empty string");
47433
+ }
47434
+ if (typeof input.approved !== "boolean") {
47435
+ throw new RuntimeUpdateInputError("apply_update approved must be a boolean");
47436
+ }
47437
+ if (typeof input.expectedWorktreeDigest !== "string" || input.expectedWorktreeDigest.length === 0) {
47438
+ throw new RuntimeUpdateInputError("apply_update expectedWorktreeDigest must be a non-empty string");
47439
+ }
47440
+ const worktreeDigestProfile = input.worktreeDigestProfile === undefined ? undefined : decodeRuntimeWorktreeDigestProfile(input.worktreeDigestProfile, "apply_update worktreeDigestProfile");
47441
+ return {
47442
+ id: input.id,
47443
+ approved: input.approved,
47444
+ expectedWorktreeDigest: input.expectedWorktreeDigest,
47445
+ ...worktreeDigestProfile === undefined ? {} : { worktreeDigestProfile },
47446
+ ...input.projectionApplyReceipt === undefined ? {} : { projectionApplyReceipt: input.projectionApplyReceipt }
47447
+ };
47448
+ }
47449
+ function runtimeUpdateInputRecord(value, field) {
47450
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
47451
+ throw new RuntimeUpdateInputError(`${field} must be an object`);
47452
+ }
47453
+ return value;
47454
+ }
47455
+
47456
+ class ChangeSetAuthorityService {
47457
+ context;
47458
+ readModelStore;
47459
+ localStore;
47460
+ changeSetEngine;
47461
+ architectureLedger;
47462
+ changesets = new Map;
47463
+ changeSetRoots = new Map;
47464
+ mcpChangeSets = new Set;
47465
+ mcpApprovals = new Map;
47466
+ changeSetWorktreeDigestProfiles = new Map;
47467
+ constructor(context) {
47468
+ this.context = context;
47469
+ this.readModelStore = context.readModelStore;
47470
+ this.localStore = context.localStore;
47471
+ this.changeSetEngine = context.changeSetEngine;
47472
+ this.architectureLedger = context.architectureLedger;
47473
+ }
47474
+ clearApprovals() {
47475
+ this.mcpApprovals.clear();
47476
+ }
47477
+ async planPracticeWaiver(root, input) {
47478
+ this.assertRunning();
47479
+ const session = await this.openSession(root);
47480
+ const model = await this.readModelStore.validateModel(session.workspace);
47481
+ let ownerRegistry;
47482
+ try {
47483
+ ownerRegistry = loadPracticeWaiverOwnerRegistry(session.workspace.root);
47484
+ } catch (error) {
47485
+ return errorEnvelope("practices.waive", "AC_SCHEMA_INVALID", error instanceof Error ? error.message : String(error));
47486
+ }
47487
+ const waiver = {
47488
+ schemaVersion: "archcontext.practice-waiver/v1",
47489
+ practiceId: input.practiceId,
47490
+ ...input.checkId === undefined ? {} : { checkId: input.checkId },
47491
+ scope: {
47492
+ ...input.pathGlobs && input.pathGlobs.length > 0 ? { pathGlobs: input.pathGlobs } : {},
47493
+ ...input.subjects && input.subjects.length > 0 ? { subjects: input.subjects } : {}
47494
+ },
47495
+ owner: input.owner,
47496
+ reason: input.reason,
47497
+ createdAt: input.createdAt ?? this.clock(),
47498
+ reviewAt: input.reviewAt,
47499
+ expiresAt: input.expiresAt,
47500
+ evidenceDigest: input.evidenceDigest
47501
+ };
47502
+ let waiverId;
47503
+ try {
47504
+ validatePracticeWaiver(waiver, "practice waiver input", { allowedOwners: ownerRegistry.owners });
47505
+ waiverId = safePracticeWaiverId(input.waiverId, waiver);
47506
+ } catch (error) {
47507
+ return errorEnvelope("practices.waive", "AC_SCHEMA_INVALID", error instanceof Error ? error.message : String(error));
47508
+ }
47509
+ const path = `.archcontext/waivers/${waiverId}.json`;
47510
+ const absolute = resolve25(session.workspace.root, path);
47511
+ const body = `${JSON.stringify(waiver, null, 2)}
47512
+ `;
47513
+ const expectedHash = existsSync24(absolute) ? digestJson({ body: readFileSync26(absolute, "utf8") }) : "missing";
47514
+ const draft = this.changeSetEngine.plan({
47515
+ id: input.id ?? `changeset.practice-waiver-${waiverId.replace(/[^A-Za-z0-9_-]/g, "-")}`,
47516
+ base: {
47517
+ headSha: session.workspace.headSha,
47518
+ worktreeDigest: session.snapshot.worktreeDigest,
47519
+ modelDigest: model.modelDigest
47520
+ },
47521
+ reason: { taskSessionId: input.taskSessionId ?? "task_runtime" },
47522
+ operations: [{ op: "write_waiver", path, expectedHash, body }]
47523
+ });
47524
+ this.changesets.set(draft.id, draft);
47525
+ this.changeSetRoots.set(draft.id, canonicalRepositoryRoot(root));
47526
+ this.changeSetWorktreeDigestProfiles.set(draft.id, "repository");
47527
+ this.mcpChangeSets.delete(draft.id);
47528
+ return okEnvelope("practices.waive", {
47529
+ schemaVersion: "archcontext.practice-waiver-plan/v1",
47530
+ waiver,
47531
+ waiverDigest: digestJson(waiver),
47532
+ ownerRegistry,
47533
+ path,
47534
+ draft,
47535
+ preview: this.changeSetEngine.preview(session.workspace.root, draft)
47536
+ });
47537
+ }
47538
+ async planUpdate(root, rawInput) {
47539
+ this.assertRunning();
47540
+ let input;
47541
+ try {
47542
+ input = decodeRuntimePlanUpdateInput(rawInput);
47543
+ } catch (error) {
47544
+ if (error instanceof RuntimeUpdateInputError) {
47545
+ return errorEnvelope("plan_update", "AC_SCHEMA_INVALID", error.message);
47546
+ }
47547
+ throw error;
47548
+ }
47549
+ const session = await this.openSession(root);
47550
+ const model = await this.readModelStore.validateModel(session.workspace);
47551
+ const worktreeDigestProfile = input.worktreeDigestPrecondition?.profile ?? "repository";
47552
+ const worktreeDigest = runtimeWorktreeDigest(root, worktreeDigestProfile);
47553
+ if (input.worktreeDigestPrecondition && input.worktreeDigestPrecondition.expectedDigest !== worktreeDigest) {
47554
+ throw new Error("Worktree digest changed before plan");
47555
+ }
47556
+ const draft = this.changeSetEngine.plan({
47557
+ id: input.id,
47558
+ base: {
47559
+ headSha: session.workspace.headSha,
47560
+ worktreeDigest,
47561
+ modelDigest: model.modelDigest
47562
+ },
47563
+ reason: input.reason ?? { taskSessionId: "task_runtime" },
47564
+ operations: input.operations
47565
+ });
47566
+ this.changesets.set(draft.id, draft);
47567
+ this.changeSetRoots.set(draft.id, canonicalRepositoryRoot(root));
47568
+ this.changeSetWorktreeDigestProfiles.set(draft.id, worktreeDigestProfile);
47569
+ if (input.approvalChannel === "mcp")
47570
+ this.mcpChangeSets.add(draft.id);
47571
+ else
47572
+ this.mcpChangeSets.delete(draft.id);
47573
+ return okEnvelope("plan_update", {
47574
+ draft,
47575
+ changeSetDigest: digestJson(draft),
47576
+ preview: this.changeSetEngine.preview(root, draft)
47577
+ });
47578
+ }
47579
+ async approveMcpProjection(root, input) {
47580
+ this.assertRunning();
47581
+ try {
47582
+ validateProjectionInvocation(input);
47583
+ if (!projectionInvocationWrites(input))
47584
+ throw new Error("Projection approval requires an apply, adopt or recover invocation");
47585
+ assertProjectionInvocationSnapshot(root, input);
47586
+ } catch (error) {
47587
+ return errorEnvelope("projection.approve", "AC_PRECONDITION_FAILED", error instanceof Error ? error.message : String(error));
47588
+ }
47589
+ const now = Date.parse(this.clock());
47590
+ if (!Number.isFinite(now))
47591
+ return errorEnvelope("projection.approve", "AC_PRECONDITION_FAILED", "Approval clock is invalid");
47592
+ for (const [key, grant] of this.mcpApprovals)
47593
+ if (grant.expiresAt <= now)
47594
+ this.mcpApprovals.delete(key);
47595
+ if (this.mcpApprovals.size >= 256)
47596
+ return errorEnvelope("projection.approve", "AC_PRECONDITION_FAILED", "Too many outstanding approvals; consume an approval or wait for expiry");
47597
+ const approvalToken = randomBytes5(32).toString("hex");
47598
+ const expiresAt = now + 5 * 60000;
47599
+ this.mcpApprovals.set(digestJson(approvalToken), { scope: "projection", root: canonicalRepositoryRoot(root), invocationDigest: digestJson(input), expiresAt });
47600
+ return okEnvelope("projection.approve", { approvalToken, expiresAt: new Date(expiresAt).toISOString() });
47601
+ }
47602
+ async mcpProjection(root, input, approvalToken) {
47603
+ this.assertRunning();
47604
+ try {
47605
+ validateProjectionInvocation(input);
47606
+ } catch (error) {
47607
+ return errorEnvelope("projection", "AC_SCHEMA_INVALID", error instanceof Error ? error.message : String(error));
47608
+ }
47609
+ if (projectionInvocationWrites(input)) {
47610
+ const denied = () => errorEnvelope("projection", "AC_USER_CONFIRMATION_REQUIRED", "A fresh one-time token from archctx projection approve is required");
47611
+ if (typeof approvalToken !== "string" || !/^[a-f0-9]{64}$/.test(approvalToken))
47612
+ return denied();
47613
+ const key = digestJson(approvalToken);
47614
+ const grant = this.mcpApprovals.get(key);
47615
+ this.mcpApprovals.delete(key);
47616
+ const now = Date.parse(this.clock());
47617
+ if (!grant || grant.scope !== "projection" || !Number.isFinite(now) || grant.expiresAt <= now || grant.root !== canonicalRepositoryRoot(root) || grant.invocationDigest !== digestJson(input))
47618
+ return denied();
47619
+ }
47620
+ return this.projection(root, input);
47621
+ }
47622
+ async approveMcpUpdate(root, input) {
47623
+ this.assertRunning();
47624
+ const draft = this.changesets.get(input?.id);
47625
+ const canonicalRoot = canonicalRepositoryRoot(root);
47626
+ const now = Date.parse(this.clock());
47627
+ if (!draft || this.changeSetRoots.get(input.id) !== canonicalRoot || this.changeSetWorktreeDigestProfiles.get(input.id) !== "repository" || input.expectedChangeSetDigest !== digestJson(draft) || input.expectedWorktreeDigest !== draft.base.worktreeDigest || input.expectedWorktreeDigest !== runtimeWorktreeDigest(root, "repository") || !Number.isFinite(now)) {
47628
+ return errorEnvelope("approve_mcp_update", "AC_PRECONDITION_FAILED", "Approval must match the current ChangeSet preview and repository");
47629
+ }
47630
+ for (const [key, grant] of this.mcpApprovals)
47631
+ if (grant.expiresAt <= now)
47632
+ this.mcpApprovals.delete(key);
47633
+ if (this.mcpApprovals.size >= 256)
47634
+ return errorEnvelope("approve_mcp_update", "AC_PRECONDITION_FAILED", "Too many outstanding approvals; consume an approval or wait for expiry");
47635
+ const approvalToken = randomBytes5(32).toString("hex");
47636
+ const expiresAt = now + 5 * 60000;
47637
+ this.mcpApprovals.set(digestJson(approvalToken), {
47638
+ scope: "changeset",
47639
+ root: canonicalRoot,
47640
+ id: input.id,
47641
+ draftDigest: input.expectedChangeSetDigest,
47642
+ worktreeDigest: input.expectedWorktreeDigest,
47643
+ expiresAt
47644
+ });
47645
+ return okEnvelope("approve_mcp_update", { approvalToken, expiresAt: new Date(expiresAt).toISOString() });
47646
+ }
47647
+ async applyMcpUpdate(root, input) {
47648
+ this.assertRunning();
47649
+ const denied = () => errorEnvelope("apply_update", "AC_USER_CONFIRMATION_REQUIRED", "A fresh one-time token from archctx approve is required");
47650
+ if (typeof input?.approvalToken !== "string" || !/^[a-f0-9]{64}$/.test(input.approvalToken))
47651
+ return denied();
47652
+ const key = digestJson(input.approvalToken);
47653
+ const grant = this.mcpApprovals.get(key);
47654
+ if (!grant)
47655
+ return denied();
47656
+ this.mcpApprovals.delete(key);
47657
+ const draft = this.changesets.get(input.id);
47658
+ const now = Date.parse(this.clock());
47659
+ if (grant.scope !== "changeset" || !Number.isFinite(now) || grant.expiresAt <= now || grant.root !== canonicalRepositoryRoot(root) || grant.id !== input.id || grant.worktreeDigest !== input.expectedWorktreeDigest || !draft || grant.draftDigest !== digestJson(draft))
47660
+ return denied();
47661
+ return this.applyAuthorizedUpdate(root, { id: input.id, expectedWorktreeDigest: input.expectedWorktreeDigest, approved: true });
47662
+ }
47663
+ async applyUpdate(root, rawInput) {
47664
+ this.assertRunning();
47665
+ if (this.mcpChangeSets.has(rawInput?.id)) {
47666
+ return errorEnvelope("apply_update", "AC_USER_CONFIRMATION_REQUIRED", "MCP ChangeSets require a one-time approval token through applyMcpUpdate");
47667
+ }
47668
+ return this.applyAuthorizedUpdate(root, rawInput);
47669
+ }
47670
+ async applyAuthorizedUpdate(root, rawInput) {
47671
+ this.assertRunning();
47672
+ let input;
47673
+ try {
47674
+ input = decodeRuntimeApplyUpdateInput(rawInput);
47675
+ } catch (error) {
47676
+ if (error instanceof RuntimeUpdateInputError) {
47677
+ return errorEnvelope("apply_update", "AC_SCHEMA_INVALID", error.message);
47678
+ }
47679
+ throw error;
47680
+ }
47681
+ return this.withWriter(async () => {
47682
+ const draft = this.changesets.get(input.id);
47683
+ if (!draft)
47684
+ throw new Error(`Unknown ChangeSet: ${input.id}`);
47685
+ const plannedProfile = this.changeSetWorktreeDigestProfiles.get(input.id);
47686
+ if (!plannedProfile)
47687
+ throw new Error("ChangeSet worktree digest profile missing before apply");
47688
+ const requestedProfile = input.worktreeDigestProfile ?? "repository";
47689
+ if (requestedProfile !== plannedProfile)
47690
+ throw new Error("ChangeSet worktree digest profile changed before apply");
47691
+ const current = runtimeWorktreeDigest(root, plannedProfile);
47692
+ if (current !== input.expectedWorktreeDigest)
47693
+ throw new Error("Worktree digest changed before apply");
47694
+ const session = await this.openSession(root);
47695
+ if (draft.base.headSha !== session.workspace.headSha)
47696
+ throw new Error("ChangeSet HEAD changed before apply");
47697
+ if (draft.base.worktreeDigest !== current)
47698
+ throw new Error("ChangeSet worktree digest changed before apply");
47699
+ const currentModel = await this.readModelStore.validateModel(session.workspace);
47700
+ const baseErrors = baseModelBlockingErrors(currentModel);
47701
+ if (baseErrors.length > 0) {
47702
+ throw new Error(`ChangeSet base model is invalid: ${baseErrors.join("; ")}`);
47703
+ }
47704
+ if (draft.base.modelDigest !== currentModel.modelDigest)
47705
+ throw new Error("ChangeSet model digest changed before apply");
47706
+ if (input.projectionApplyReceipt && await this.localStore.inspectProjectionApplyReceipt(input.projectionApplyReceipt.identity.lookupKey)) {
47707
+ return errorEnvelope("apply_update", "AC_PRECONDITION_FAILED", "committed projection receipt requires explicit projection recover");
47708
+ }
47709
+ const approved = input.approved ? this.changeSetEngine.approve(draft) : draft;
47710
+ const transitionBase = captureModelTransitionBase(root, approved);
47711
+ let ledgerAppend;
47712
+ let appliedJournalId;
47713
+ const writesLedger = architectureLedgerWriteAppendsEvents(this.architectureLedger.writeMode);
47714
+ const result = await this.changeSetEngine.apply(root, approved, {
47715
+ approved: input.approved,
47716
+ afterModelValidatedBeforeCommit: async ({ journalId }) => {
47717
+ appliedJournalId = journalId;
47718
+ if (transitionBase && journalId)
47719
+ await recordModelTransitionEvidence(this.localStore, root, journalId, transitionBase);
47720
+ if (journalId && this.context.projectionPlannedDrafts.has(draft))
47721
+ await this.localStore.recordChangeSetProjectionOwner(journalId);
47722
+ if (input.projectionApplyReceipt) {
47723
+ if (!journalId)
47724
+ throw new Error("projection apply receipt requires a durable ChangeSet journal");
47725
+ await this.localStore.recordProjectionApplyReceipt(journalId, input.projectionApplyReceipt);
47726
+ }
47727
+ if (writesLedger) {
47728
+ const appended = await this.appendAppliedChangeSetToArchitectureLedger(root, session, approved, journalId);
47729
+ ledgerAppend = {
47730
+ status: "appended",
47731
+ appendedEventCount: appended.appendedEvents.length,
47732
+ duplicateEventCount: appended.duplicateEvents.length,
47733
+ graphDigest: appended.graphDigest,
47734
+ entityCount: appended.entityCount,
47735
+ relationCount: appended.relationCount,
47736
+ constraintCount: appended.constraintCount
47737
+ };
47738
+ }
47739
+ return { journalCommitted: writesLedger && Boolean(journalId) };
47740
+ }
47741
+ });
47742
+ return okEnvelope("apply_update", {
47743
+ ...result,
47744
+ ...appliedJournalId ? { journalId: appliedJournalId } : {},
47745
+ architectureLedger: {
47746
+ ...this.architectureLedger,
47747
+ append: writesLedger ? ledgerAppend ?? { status: "not-appended" } : { status: "not-applicable" }
47748
+ }
47749
+ });
47750
+ });
47751
+ }
47752
+ async appendAppliedChangeSetToArchitectureLedger(root, session, draft, journalId) {
47753
+ const paths = runtimeStatePaths(root);
47754
+ const scope = {
47755
+ repository: {
47756
+ repositoryId: session.workspace.repositoryId,
47757
+ storageRepositoryId: paths.storageRepositoryId
47758
+ },
47759
+ worktree: {
47760
+ workspaceId: paths.workspaceId,
47761
+ storageWorkspaceId: paths.storageWorkspaceId,
47762
+ branch: readCurrentBranch(root),
47763
+ headSha: session.workspace.headSha,
47764
+ worktreeDigest: computeWorktreeDigest(root)
47765
+ }
47766
+ };
47767
+ const plan = planChangeSetApplyToArchitectureLedgerEvent({
47768
+ ...scope,
47769
+ draft,
47770
+ files: listModelFiles(root),
47771
+ previousEvidenceState: await this.localStore.replayArchitectureLedgerEvidence(scope),
47772
+ createdAt: this.clock(),
47773
+ writeMode: this.architectureLedger.writeMode === "ledger-with-projection" ? "ledger-with-projection" : "dual",
47774
+ command: "archctx apply"
47775
+ });
47776
+ if (journalId)
47777
+ await this.localStore.recordChangeSetLedgerPlan(journalId, { event: plan.event });
47778
+ const appendInput = { writer: "runtime-daemon", events: [plan.event] };
47779
+ const result = await this.appendArchitectureEventsWithFeed(root, appendInput, journalId);
47780
+ return result;
47781
+ }
47782
+ async acceptCommittedChange(root, rawInput) {
47783
+ this.assertRunning();
47784
+ let input;
47785
+ try {
47786
+ input = decodeAcceptCommittedChangeInput(rawInput);
47787
+ } catch (error) {
47788
+ if (error instanceof AcceptCommittedChangeInputError)
47789
+ return errorEnvelope("ledger.accept-committed", "AC_SCHEMA_INVALID", error.message);
47790
+ throw error;
47791
+ }
47792
+ if (this.architectureLedger.readMode !== "yaml" || this.architectureLedger.writeMode !== "yaml") {
47793
+ return errorEnvelope("ledger.accept-committed", "AC_PRECONDITION_FAILED", "committed YAML acceptance requires YAML read and write authority");
47794
+ }
47795
+ return this.withWriter(async () => {
47796
+ try {
47797
+ const canonicalRoot = canonicalRepositoryRoot(root);
47798
+ const journals = [];
47799
+ for (const ref of input.journals)
47800
+ journals.push(await this.localStore.readCommittedChangeSet(canonicalRoot, ref.journalId));
47801
+ const model = loadNativeModelFromArchContext(canonicalRoot);
47802
+ const worktreeDigest = architectureDocumentationProjectionWorktreeDigest(canonicalRoot, model);
47803
+ const projection3 = buildArchitectureDocsProjection(this.projectionHost(), canonicalRoot, new Date(0).toISOString(), "repo-harness/v1");
47804
+ const scope = acceptedCommittedChangeScope(canonicalRoot, worktreeDigest, resolveAcceptanceHeadSha(canonicalRoot));
47805
+ const manifestWrite = await this.localStore.readLatestCommittedChangeSetFile(canonicalRoot, "docs/architecture/.projection-manifest.json");
47806
+ const acceptance = planCommittedChangeAcceptance(canonicalRoot, {
47807
+ requested: input.journals,
47808
+ journals,
47809
+ model,
47810
+ existingFiles: projection3.loaded.existingFiles,
47811
+ latestJournaledManifest: manifestWrite,
47812
+ projection: projection3.plan,
47813
+ currentEvidence: projection3.snapshotEvidence,
47814
+ projectionWorktreeDigest: worktreeDigest,
47815
+ scope
47816
+ });
47817
+ if (!input.approved) {
47818
+ return okEnvelope("ledger.accept-committed", { status: "preview", plan: acceptance.plan, acceptancePlanId: acceptance.acceptancePlanId, expectedWorktreeDigest: worktreeDigest });
47819
+ }
47820
+ if (input.expectedWorktreeDigest !== worktreeDigest)
47821
+ throw new Error("accepted ChangeSet expected worktree digest mismatch");
47822
+ if (input.acceptancePlanId !== acceptance.acceptancePlanId)
47823
+ throw new Error("accepted ChangeSet acceptance plan changed since preview");
47824
+ const accepted = (event2, replayed) => {
47825
+ const recorded = acceptedChangeFromEventV2(event2);
47826
+ if (!recorded || !event2.eventHash)
47827
+ throw new Error("committed ChangeSet acceptance readback failed");
47828
+ return okEnvelope("ledger.accept-committed", {
47829
+ status: "accepted",
47830
+ replayed,
47831
+ acceptedChange: recorded.acceptedChange,
47832
+ acceptancePlanId: recorded.acceptancePlanId,
47833
+ journalIds: input.journals.map((journal) => journal.journalId),
47834
+ eventHash: event2.eventHash,
47835
+ fileSetDigest: acceptance.plan.fileSetDigest
47836
+ });
47837
+ };
47838
+ const existing = await this.localStore.readArchitectureEvent({ ...scope, eventId: acceptance.acceptedChange.eventId });
47839
+ if (existing) {
47840
+ if (acceptedChangeFromEventV2(existing)?.acceptancePlanId === acceptance.acceptancePlanId)
47841
+ return accepted(existing, true);
47842
+ throw new Error(`committed ChangeSet already has a different acceptance event at this snapshot: ${existing.eventId}`);
47843
+ }
47844
+ const ledgerGraphDigest = architectureLedgerStateDigest(await this.localStore.readArchitectureLedgerState(scope));
47845
+ const event = acceptedCommittedChangeEventV2({ acceptance, scope, ledgerGraphDigest, timestamp: this.clock() });
47846
+ const appended = await this.appendArchitectureEventsWithFeed(canonicalRoot, { writer: "runtime-daemon", events: [event] });
47847
+ if (appended.appendedEvents.length !== 1)
47848
+ throw new Error("committed ChangeSet acceptance event was not appended");
47849
+ const readback = await this.localStore.readArchitectureEvent({ ...scope, eventId: event.eventId });
47850
+ if (!readback || readback.eventType !== event.eventType || readback.payloadVersion !== event.payloadVersion) {
47851
+ throw new Error("committed ChangeSet acceptance readback failed");
47852
+ }
47853
+ return accepted(readback, false);
47854
+ } catch (error) {
47855
+ return errorEnvelope("ledger.accept-committed", "AC_PRECONDITION_FAILED", error instanceof Error ? error.message : String(error));
47856
+ }
47857
+ });
47858
+ }
47859
+ assertRunning() {
47860
+ this.context.assertRunning();
47861
+ }
47862
+ clock() {
47863
+ return this.context.clock();
47864
+ }
47865
+ openSession(root) {
47866
+ return this.context.openSession(root);
47867
+ }
47868
+ withWriter(fn) {
47869
+ return this.context.withWriter(fn);
47870
+ }
47871
+ appendArchitectureEventsWithFeed(root, input, journalId) {
47872
+ return this.context.appendArchitectureEventsWithFeed(root, input, journalId);
47873
+ }
47874
+ projectionHost() {
47875
+ return this.context.projectionHost();
47876
+ }
47877
+ projection(root, input) {
47878
+ return this.context.projection(root, input);
47879
+ }
47880
+ }
47881
+ function runtimeWorktreeDigest(root, profile) {
47882
+ switch (profile) {
47883
+ case "repository":
47884
+ return computeWorktreeDigest(root);
47885
+ case "architecture-documentation-projection":
47886
+ return architectureDocumentationProjectionWorktreeDigest(root, loadNativeModelFromArchContext(root));
47887
+ default:
47888
+ throw new RuntimeUpdateInputError(`unsupported worktree digest profile: ${String(profile)}`);
47889
+ }
47890
+ }
47891
+ function acceptedCommittedChangeScope(root, worktreeDigest, headSha) {
47892
+ const paths = runtimeStatePaths(root);
47893
+ return {
47894
+ repository: { repositoryId: repositoryFingerprint(root), storageRepositoryId: paths.storageRepositoryId },
47895
+ worktree: {
47896
+ workspaceId: projectionWorkspaceId(root),
47897
+ storageWorkspaceId: paths.storageWorkspaceId,
47898
+ branch: readCurrentBranch(root),
47899
+ headSha,
47900
+ worktreeDigest
47901
+ }
47902
+ };
47903
+ }
47904
+ function safePracticeWaiverId(explicit, waiver) {
47905
+ const explicitTrimmed = explicit?.trim();
47906
+ if (explicitTrimmed && (explicitTrimmed === "." || explicitTrimmed === ".." || explicitTrimmed.includes("/") || explicitTrimmed.includes("\\"))) {
47907
+ throw new Error("practice-waiver-id-invalid");
47908
+ }
47909
+ const evidencePrefix = waiver.evidenceDigest.startsWith("sha256:") ? waiver.evidenceDigest.slice("sha256:".length, "sha256:".length + 12) : waiver.evidenceDigest.slice(0, 12);
47910
+ const candidate = (explicitTrimmed || [waiver.practiceId.replace(/\./g, "-"), waiver.checkId ?? "all", evidencePrefix].join("-")).replace(/[^A-Za-z0-9_.-]/g, "-").replace(/-+/g, "-").replace(/^-+|-+$/g, "").slice(0, 96);
47911
+ if (!candidate || candidate === "." || candidate === ".." || candidate.includes("/") || candidate.includes("\\")) {
47912
+ throw new Error("practice-waiver-id-invalid");
47913
+ }
47914
+ return candidate;
47915
+ }
47916
+ function architectureLedgerWriteAppendsEvents(mode) {
47917
+ return mode === "dual" || mode === "ledger-with-projection";
47918
+ }
47919
+
47372
47920
  // packages/local-runtime/runtime-daemon/src/rpc-server.ts
47373
47921
  init_src();
47374
47922
  import { createServer as createServer2 } from "node:http";
47375
47923
  import { closeSync as closeSync9, mkdirSync as mkdirSync12, rmSync as rmSync15 } from "node:fs";
47376
47924
  import { dirname as dirname14 } from "node:path";
47377
- import { randomBytes as randomBytes5 } from "node:crypto";
47925
+ import { randomBytes as randomBytes6 } from "node:crypto";
47378
47926
  var DEFAULT_DAEMON_IDLE_TIMEOUT_MS = 30 * 60000;
47379
47927
  var DAEMON_IDLE_TIMEOUT_ENV = "ARCHCONTEXT_DAEMON_IDLE_TIMEOUT_MS";
47380
47928
  function resolveDaemonIdleTimeoutMs(explicit) {
@@ -47444,7 +47992,7 @@ class ArchctxRuntimeRpcServer {
47444
47992
  mkdirSync12(dirname14(connectionPath), { recursive: true });
47445
47993
  mkdirSync12(dirname14(lockPath), { recursive: true });
47446
47994
  this.lockFd = acquireDaemonLock(lockPath, root);
47447
- const token = this.options.token ?? randomBytes5(18).toString("base64url");
47995
+ const token = this.options.token ?? randomBytes6(18).toString("base64url");
47448
47996
  const server = createServer2((request, response) => {
47449
47997
  this.handleRequest(request, response).catch((error) => {
47450
47998
  writeJson(response, 500, { schemaVersion: RUNTIME_RPC_VERSION, ok: false, error: error instanceof Error ? error.message : String(error) });
@@ -47710,10 +48258,9 @@ async function readRpcRequestBody(request, limits) {
47710
48258
  }
47711
48259
 
47712
48260
  // packages/local-runtime/runtime-daemon/src/index.ts
47713
- import { randomBytes as randomBytes6 } from "node:crypto";
47714
48261
  import { execFileSync as execFileSync11 } from "node:child_process";
47715
- import { existsSync as existsSync24, readFileSync as readFileSync26 } from "node:fs";
47716
- import { resolve as resolve25 } from "node:path";
48262
+ import { existsSync as existsSync25 } from "node:fs";
48263
+ import { resolve as resolve26 } from "node:path";
47717
48264
  init_src();
47718
48265
 
47719
48266
  // packages/local-runtime/runtime-daemon/src/egress.ts
@@ -47783,75 +48330,6 @@ function localEgressStatus2(env = process.env, live = {}) {
47783
48330
  }
47784
48331
 
47785
48332
  // packages/local-runtime/runtime-daemon/src/index.ts
47786
- class RuntimeUpdateInputError extends Error {
47787
- }
47788
- function decodeRuntimeWorktreeDigestProfile(value, field) {
47789
- switch (value) {
47790
- case "repository":
47791
- case "architecture-documentation-projection":
47792
- return value;
47793
- default:
47794
- throw new RuntimeUpdateInputError(`${field} must be repository or architecture-documentation-projection`);
47795
- }
47796
- }
47797
- function decodeRuntimePlanUpdateInput(value) {
47798
- const input = runtimeUpdateInputRecord(value, "plan_update input");
47799
- if (typeof input.id !== "string" || input.id.length === 0) {
47800
- throw new RuntimeUpdateInputError("plan_update id must be a non-empty string");
47801
- }
47802
- if (!Array.isArray(input.operations)) {
47803
- throw new RuntimeUpdateInputError("plan_update operations must be an array");
47804
- }
47805
- if (input.approvalChannel !== undefined && input.approvalChannel !== "mcp") {
47806
- throw new RuntimeUpdateInputError("plan_update approvalChannel must be mcp");
47807
- }
47808
- let worktreeDigestPrecondition;
47809
- if (input.worktreeDigestPrecondition !== undefined) {
47810
- const precondition = runtimeUpdateInputRecord(input.worktreeDigestPrecondition, "plan_update worktreeDigestPrecondition");
47811
- const profile = decodeRuntimeWorktreeDigestProfile(precondition.profile, "plan_update worktreeDigestPrecondition.profile");
47812
- if (profile !== "architecture-documentation-projection") {
47813
- throw new RuntimeUpdateInputError("plan_update worktreeDigestPrecondition.profile must be architecture-documentation-projection");
47814
- }
47815
- if (typeof precondition.expectedDigest !== "string" || precondition.expectedDigest.length === 0) {
47816
- throw new RuntimeUpdateInputError("plan_update worktreeDigestPrecondition.expectedDigest must be a non-empty string");
47817
- }
47818
- worktreeDigestPrecondition = { profile, expectedDigest: precondition.expectedDigest };
47819
- }
47820
- return {
47821
- id: input.id,
47822
- operations: input.operations,
47823
- ...input.approvalChannel === "mcp" ? { approvalChannel: "mcp" } : {},
47824
- ...input.reason === undefined ? {} : { reason: input.reason },
47825
- ...worktreeDigestPrecondition === undefined ? {} : { worktreeDigestPrecondition }
47826
- };
47827
- }
47828
- function decodeRuntimeApplyUpdateInput(value) {
47829
- const input = runtimeUpdateInputRecord(value, "apply_update input");
47830
- if (typeof input.id !== "string" || input.id.length === 0) {
47831
- throw new RuntimeUpdateInputError("apply_update id must be a non-empty string");
47832
- }
47833
- if (typeof input.approved !== "boolean") {
47834
- throw new RuntimeUpdateInputError("apply_update approved must be a boolean");
47835
- }
47836
- if (typeof input.expectedWorktreeDigest !== "string" || input.expectedWorktreeDigest.length === 0) {
47837
- throw new RuntimeUpdateInputError("apply_update expectedWorktreeDigest must be a non-empty string");
47838
- }
47839
- const worktreeDigestProfile = input.worktreeDigestProfile === undefined ? undefined : decodeRuntimeWorktreeDigestProfile(input.worktreeDigestProfile, "apply_update worktreeDigestProfile");
47840
- return {
47841
- id: input.id,
47842
- approved: input.approved,
47843
- expectedWorktreeDigest: input.expectedWorktreeDigest,
47844
- ...worktreeDigestProfile === undefined ? {} : { worktreeDigestProfile },
47845
- ...input.projectionApplyReceipt === undefined ? {} : { projectionApplyReceipt: input.projectionApplyReceipt }
47846
- };
47847
- }
47848
- function runtimeUpdateInputRecord(value, field) {
47849
- if (!value || typeof value !== "object" || Array.isArray(value)) {
47850
- throw new RuntimeUpdateInputError(`${field} must be an object`);
47851
- }
47852
- return value;
47853
- }
47854
-
47855
48333
  class ArchitectureLedgerReadModelStore {
47856
48334
  fallback;
47857
48335
  localStore;
@@ -47940,12 +48418,8 @@ class ArchctxDaemon {
47940
48418
  maxRepoSessions;
47941
48419
  composition;
47942
48420
  sessions = new Map;
47943
- changesets = new Map;
47944
- changeSetRoots = new Map;
47945
- mcpChangeSets = new Set;
48421
+ changeSetAuthority;
47946
48422
  projectionPlannedDrafts = new WeakSet;
47947
- mcpApprovals = new Map;
47948
- changeSetWorktreeDigestProfiles = new Map;
47949
48423
  auditRunAbortControllers = new Map;
47950
48424
  landscapes;
47951
48425
  explorerServer;
@@ -48077,6 +48551,20 @@ class ArchctxDaemon {
48077
48551
  worktreeDigest: runtimeWorktreeDigest,
48078
48552
  loadSourceChanges: loadCapabilitySourceChangesSinceStamps
48079
48553
  });
48554
+ this.changeSetAuthority = new ChangeSetAuthorityService({
48555
+ projectionPlannedDrafts: this.projectionPlannedDrafts,
48556
+ assertRunning: () => this.assertRunning(),
48557
+ clock: this.clock,
48558
+ openSession: (root) => this.openSession(root),
48559
+ withWriter: (run) => this.withWriter(run),
48560
+ appendArchitectureEventsWithFeed: (root, input, journalId) => this.appendArchitectureEventsWithFeed(root, input, journalId),
48561
+ projectionHost: () => this.projectionHost(),
48562
+ projection: (root, input) => this.projection(root, input),
48563
+ readModelStore: this.readModelStore,
48564
+ localStore: this.localStore,
48565
+ changeSetEngine: this.changeSetEngine,
48566
+ architectureLedger: this.architectureLedger
48567
+ });
48080
48568
  this.developerReviewRuns = new DeveloperReviewRunService(() => this.assertRunning(), this.clock);
48081
48569
  this.externalDocumentation = deps.externalDocumentation ?? new Context7ExternalDocumentationAdapter({
48082
48570
  enabled: process.env[CONTEXT7_ENABLED_ENV] === "1",
@@ -48146,7 +48634,7 @@ class ArchctxDaemon {
48146
48634
  try {
48147
48635
  for (const controller of this.auditRunAbortControllers.values())
48148
48636
  controller.abort();
48149
- this.mcpApprovals.clear();
48637
+ this.changeSetAuthority.clearApprovals();
48150
48638
  this.sessions.clear();
48151
48639
  this.practiceCheckpoints.clear();
48152
48640
  this.explorerProjections.clearDeferredChangeFeedFailures();
@@ -48290,65 +48778,7 @@ class ArchctxDaemon {
48290
48778
  return this.practiceCheckpoints.practiceWaivers(root);
48291
48779
  }
48292
48780
  async planPracticeWaiver(root, input) {
48293
- this.assertRunning();
48294
- const session = await this.openSession(root);
48295
- const model = await this.readModelStore.validateModel(session.workspace);
48296
- let ownerRegistry;
48297
- try {
48298
- ownerRegistry = loadPracticeWaiverOwnerRegistry(session.workspace.root);
48299
- } catch (error) {
48300
- return errorEnvelope("practices.waive", "AC_SCHEMA_INVALID", error instanceof Error ? error.message : String(error));
48301
- }
48302
- const waiver = {
48303
- schemaVersion: "archcontext.practice-waiver/v1",
48304
- practiceId: input.practiceId,
48305
- ...input.checkId === undefined ? {} : { checkId: input.checkId },
48306
- scope: {
48307
- ...input.pathGlobs && input.pathGlobs.length > 0 ? { pathGlobs: input.pathGlobs } : {},
48308
- ...input.subjects && input.subjects.length > 0 ? { subjects: input.subjects } : {}
48309
- },
48310
- owner: input.owner,
48311
- reason: input.reason,
48312
- createdAt: input.createdAt ?? this.clock(),
48313
- reviewAt: input.reviewAt,
48314
- expiresAt: input.expiresAt,
48315
- evidenceDigest: input.evidenceDigest
48316
- };
48317
- let waiverId;
48318
- try {
48319
- validatePracticeWaiver(waiver, "practice waiver input", { allowedOwners: ownerRegistry.owners });
48320
- waiverId = safePracticeWaiverId(input.waiverId, waiver);
48321
- } catch (error) {
48322
- return errorEnvelope("practices.waive", "AC_SCHEMA_INVALID", error instanceof Error ? error.message : String(error));
48323
- }
48324
- const path = `.archcontext/waivers/${waiverId}.json`;
48325
- const absolute = resolve25(session.workspace.root, path);
48326
- const body = `${JSON.stringify(waiver, null, 2)}
48327
- `;
48328
- const expectedHash = existsSync24(absolute) ? digestJson({ body: readFileSync26(absolute, "utf8") }) : "missing";
48329
- const draft = this.changeSetEngine.plan({
48330
- id: input.id ?? `changeset.practice-waiver-${waiverId.replace(/[^A-Za-z0-9_-]/g, "-")}`,
48331
- base: {
48332
- headSha: session.workspace.headSha,
48333
- worktreeDigest: session.snapshot.worktreeDigest,
48334
- modelDigest: model.modelDigest
48335
- },
48336
- reason: { taskSessionId: input.taskSessionId ?? "task_runtime" },
48337
- operations: [{ op: "write_waiver", path, expectedHash, body }]
48338
- });
48339
- this.changesets.set(draft.id, draft);
48340
- this.changeSetRoots.set(draft.id, canonicalRepositoryRoot(root));
48341
- this.changeSetWorktreeDigestProfiles.set(draft.id, "repository");
48342
- this.mcpChangeSets.delete(draft.id);
48343
- return okEnvelope("practices.waive", {
48344
- schemaVersion: "archcontext.practice-waiver-plan/v1",
48345
- waiver,
48346
- waiverDigest: digestJson(waiver),
48347
- ownerRegistry,
48348
- path,
48349
- draft,
48350
- preview: this.changeSetEngine.preview(session.workspace.root, draft)
48351
- });
48781
+ return this.changeSetAuthority.planPracticeWaiver(root, input);
48352
48782
  }
48353
48783
  async docs(root, input) {
48354
48784
  return this.externalDocumentationService.docs(root, input);
@@ -48357,45 +48787,7 @@ class ArchctxDaemon {
48357
48787
  return this.externalDocumentationService.readResource(root, uri);
48358
48788
  }
48359
48789
  async planUpdate(root, rawInput) {
48360
- this.assertRunning();
48361
- let input;
48362
- try {
48363
- input = decodeRuntimePlanUpdateInput(rawInput);
48364
- } catch (error) {
48365
- if (error instanceof RuntimeUpdateInputError) {
48366
- return errorEnvelope("plan_update", "AC_SCHEMA_INVALID", error.message);
48367
- }
48368
- throw error;
48369
- }
48370
- const session = await this.openSession(root);
48371
- const model = await this.readModelStore.validateModel(session.workspace);
48372
- const worktreeDigestProfile = input.worktreeDigestPrecondition?.profile ?? "repository";
48373
- const worktreeDigest = runtimeWorktreeDigest(root, worktreeDigestProfile);
48374
- if (input.worktreeDigestPrecondition && input.worktreeDigestPrecondition.expectedDigest !== worktreeDigest) {
48375
- throw new Error("Worktree digest changed before plan");
48376
- }
48377
- const draft = this.changeSetEngine.plan({
48378
- id: input.id,
48379
- base: {
48380
- headSha: session.workspace.headSha,
48381
- worktreeDigest,
48382
- modelDigest: model.modelDigest
48383
- },
48384
- reason: input.reason ?? { taskSessionId: "task_runtime" },
48385
- operations: input.operations
48386
- });
48387
- this.changesets.set(draft.id, draft);
48388
- this.changeSetRoots.set(draft.id, canonicalRepositoryRoot(root));
48389
- this.changeSetWorktreeDigestProfiles.set(draft.id, worktreeDigestProfile);
48390
- if (input.approvalChannel === "mcp")
48391
- this.mcpChangeSets.add(draft.id);
48392
- else
48393
- this.mcpChangeSets.delete(draft.id);
48394
- return okEnvelope("plan_update", {
48395
- draft,
48396
- changeSetDigest: digestJson(draft),
48397
- preview: this.changeSetEngine.preview(root, draft)
48398
- });
48790
+ return this.changeSetAuthority.planUpdate(root, rawInput);
48399
48791
  }
48400
48792
  async completeTask(root, input = {}) {
48401
48793
  this.assertRunning();
@@ -48504,177 +48896,19 @@ class ArchctxDaemon {
48504
48896
  return runProjectionProtocolCommand(input, root, this.projectionHost());
48505
48897
  }
48506
48898
  async approveMcpProjection(root, input) {
48507
- this.assertRunning();
48508
- try {
48509
- validateProjectionInvocation(input);
48510
- if (!projectionInvocationWrites(input))
48511
- throw new Error("Projection approval requires an apply, adopt or recover invocation");
48512
- assertProjectionInvocationSnapshot(root, input);
48513
- } catch (error) {
48514
- return errorEnvelope("projection.approve", "AC_PRECONDITION_FAILED", error instanceof Error ? error.message : String(error));
48515
- }
48516
- const now = Date.parse(this.clock());
48517
- if (!Number.isFinite(now))
48518
- return errorEnvelope("projection.approve", "AC_PRECONDITION_FAILED", "Approval clock is invalid");
48519
- for (const [key, grant] of this.mcpApprovals)
48520
- if (grant.expiresAt <= now)
48521
- this.mcpApprovals.delete(key);
48522
- if (this.mcpApprovals.size >= 256)
48523
- return errorEnvelope("projection.approve", "AC_PRECONDITION_FAILED", "Too many outstanding approvals; consume an approval or wait for expiry");
48524
- const approvalToken = randomBytes6(32).toString("hex");
48525
- const expiresAt = now + 5 * 60000;
48526
- this.mcpApprovals.set(digestJson(approvalToken), { scope: "projection", root: canonicalRepositoryRoot(root), invocationDigest: digestJson(input), expiresAt });
48527
- return okEnvelope("projection.approve", { approvalToken, expiresAt: new Date(expiresAt).toISOString() });
48899
+ return this.changeSetAuthority.approveMcpProjection(root, input);
48528
48900
  }
48529
48901
  async mcpProjection(root, input, approvalToken) {
48530
- this.assertRunning();
48531
- try {
48532
- validateProjectionInvocation(input);
48533
- } catch (error) {
48534
- return errorEnvelope("projection", "AC_SCHEMA_INVALID", error instanceof Error ? error.message : String(error));
48535
- }
48536
- if (projectionInvocationWrites(input)) {
48537
- const denied = () => errorEnvelope("projection", "AC_USER_CONFIRMATION_REQUIRED", "A fresh one-time token from archctx projection approve is required");
48538
- if (typeof approvalToken !== "string" || !/^[a-f0-9]{64}$/.test(approvalToken))
48539
- return denied();
48540
- const key = digestJson(approvalToken);
48541
- const grant = this.mcpApprovals.get(key);
48542
- this.mcpApprovals.delete(key);
48543
- const now = Date.parse(this.clock());
48544
- if (!grant || grant.scope !== "projection" || !Number.isFinite(now) || grant.expiresAt <= now || grant.root !== canonicalRepositoryRoot(root) || grant.invocationDigest !== digestJson(input))
48545
- return denied();
48546
- }
48547
- return this.projection(root, input);
48902
+ return this.changeSetAuthority.mcpProjection(root, input, approvalToken);
48548
48903
  }
48549
48904
  async approveMcpUpdate(root, input) {
48550
- this.assertRunning();
48551
- const draft = this.changesets.get(input?.id);
48552
- const canonicalRoot = canonicalRepositoryRoot(root);
48553
- const now = Date.parse(this.clock());
48554
- if (!draft || this.changeSetRoots.get(input.id) !== canonicalRoot || this.changeSetWorktreeDigestProfiles.get(input.id) !== "repository" || input.expectedChangeSetDigest !== digestJson(draft) || input.expectedWorktreeDigest !== draft.base.worktreeDigest || input.expectedWorktreeDigest !== runtimeWorktreeDigest(root, "repository") || !Number.isFinite(now)) {
48555
- return errorEnvelope("approve_mcp_update", "AC_PRECONDITION_FAILED", "Approval must match the current ChangeSet preview and repository");
48556
- }
48557
- for (const [key, grant] of this.mcpApprovals)
48558
- if (grant.expiresAt <= now)
48559
- this.mcpApprovals.delete(key);
48560
- if (this.mcpApprovals.size >= 256)
48561
- return errorEnvelope("approve_mcp_update", "AC_PRECONDITION_FAILED", "Too many outstanding approvals; consume an approval or wait for expiry");
48562
- const approvalToken = randomBytes6(32).toString("hex");
48563
- const expiresAt = now + 5 * 60000;
48564
- this.mcpApprovals.set(digestJson(approvalToken), {
48565
- scope: "changeset",
48566
- root: canonicalRoot,
48567
- id: input.id,
48568
- draftDigest: input.expectedChangeSetDigest,
48569
- worktreeDigest: input.expectedWorktreeDigest,
48570
- expiresAt
48571
- });
48572
- return okEnvelope("approve_mcp_update", { approvalToken, expiresAt: new Date(expiresAt).toISOString() });
48905
+ return this.changeSetAuthority.approveMcpUpdate(root, input);
48573
48906
  }
48574
48907
  async applyMcpUpdate(root, input) {
48575
- this.assertRunning();
48576
- const denied = () => errorEnvelope("apply_update", "AC_USER_CONFIRMATION_REQUIRED", "A fresh one-time token from archctx approve is required");
48577
- if (typeof input?.approvalToken !== "string" || !/^[a-f0-9]{64}$/.test(input.approvalToken))
48578
- return denied();
48579
- const key = digestJson(input.approvalToken);
48580
- const grant = this.mcpApprovals.get(key);
48581
- if (!grant)
48582
- return denied();
48583
- this.mcpApprovals.delete(key);
48584
- const draft = this.changesets.get(input.id);
48585
- const now = Date.parse(this.clock());
48586
- if (grant.scope !== "changeset" || !Number.isFinite(now) || grant.expiresAt <= now || grant.root !== canonicalRepositoryRoot(root) || grant.id !== input.id || grant.worktreeDigest !== input.expectedWorktreeDigest || !draft || grant.draftDigest !== digestJson(draft))
48587
- return denied();
48588
- return this.applyAuthorizedUpdate(root, { id: input.id, expectedWorktreeDigest: input.expectedWorktreeDigest, approved: true });
48908
+ return this.changeSetAuthority.applyMcpUpdate(root, input);
48589
48909
  }
48590
48910
  async applyUpdate(root, rawInput) {
48591
- this.assertRunning();
48592
- if (this.mcpChangeSets.has(rawInput?.id)) {
48593
- return errorEnvelope("apply_update", "AC_USER_CONFIRMATION_REQUIRED", "MCP ChangeSets require a one-time approval token through applyMcpUpdate");
48594
- }
48595
- return this.applyAuthorizedUpdate(root, rawInput);
48596
- }
48597
- async applyAuthorizedUpdate(root, rawInput) {
48598
- this.assertRunning();
48599
- let input;
48600
- try {
48601
- input = decodeRuntimeApplyUpdateInput(rawInput);
48602
- } catch (error) {
48603
- if (error instanceof RuntimeUpdateInputError) {
48604
- return errorEnvelope("apply_update", "AC_SCHEMA_INVALID", error.message);
48605
- }
48606
- throw error;
48607
- }
48608
- return this.withWriter(async () => {
48609
- const draft = this.changesets.get(input.id);
48610
- if (!draft)
48611
- throw new Error(`Unknown ChangeSet: ${input.id}`);
48612
- const plannedProfile = this.changeSetWorktreeDigestProfiles.get(input.id);
48613
- if (!plannedProfile)
48614
- throw new Error("ChangeSet worktree digest profile missing before apply");
48615
- const requestedProfile = input.worktreeDigestProfile ?? "repository";
48616
- if (requestedProfile !== plannedProfile)
48617
- throw new Error("ChangeSet worktree digest profile changed before apply");
48618
- const current = runtimeWorktreeDigest(root, plannedProfile);
48619
- if (current !== input.expectedWorktreeDigest)
48620
- throw new Error("Worktree digest changed before apply");
48621
- const session = await this.openSession(root);
48622
- if (draft.base.headSha !== session.workspace.headSha)
48623
- throw new Error("ChangeSet HEAD changed before apply");
48624
- if (draft.base.worktreeDigest !== current)
48625
- throw new Error("ChangeSet worktree digest changed before apply");
48626
- const currentModel = await this.readModelStore.validateModel(session.workspace);
48627
- const baseErrors = baseModelBlockingErrors(currentModel);
48628
- if (baseErrors.length > 0) {
48629
- throw new Error(`ChangeSet base model is invalid: ${baseErrors.join("; ")}`);
48630
- }
48631
- if (draft.base.modelDigest !== currentModel.modelDigest)
48632
- throw new Error("ChangeSet model digest changed before apply");
48633
- if (input.projectionApplyReceipt && await this.localStore.inspectProjectionApplyReceipt(input.projectionApplyReceipt.identity.lookupKey)) {
48634
- return errorEnvelope("apply_update", "AC_PRECONDITION_FAILED", "committed projection receipt requires explicit projection recover");
48635
- }
48636
- const approved = input.approved ? this.changeSetEngine.approve(draft) : draft;
48637
- const transitionBase = captureModelTransitionBase(root, approved);
48638
- let ledgerAppend;
48639
- let appliedJournalId;
48640
- const writesLedger = architectureLedgerWriteAppendsEvents(this.architectureLedger.writeMode);
48641
- const result = await this.changeSetEngine.apply(root, approved, {
48642
- approved: input.approved,
48643
- afterModelValidatedBeforeCommit: async ({ journalId }) => {
48644
- appliedJournalId = journalId;
48645
- if (transitionBase && journalId)
48646
- await recordModelTransitionEvidence(this.localStore, root, journalId, transitionBase);
48647
- if (journalId && this.projectionPlannedDrafts.has(draft))
48648
- await this.localStore.recordChangeSetProjectionOwner(journalId);
48649
- if (input.projectionApplyReceipt) {
48650
- if (!journalId)
48651
- throw new Error("projection apply receipt requires a durable ChangeSet journal");
48652
- await this.localStore.recordProjectionApplyReceipt(journalId, input.projectionApplyReceipt);
48653
- }
48654
- if (writesLedger) {
48655
- const appended = await this.appendAppliedChangeSetToArchitectureLedger(root, session, approved, journalId);
48656
- ledgerAppend = {
48657
- status: "appended",
48658
- appendedEventCount: appended.appendedEvents.length,
48659
- duplicateEventCount: appended.duplicateEvents.length,
48660
- graphDigest: appended.graphDigest,
48661
- entityCount: appended.entityCount,
48662
- relationCount: appended.relationCount,
48663
- constraintCount: appended.constraintCount
48664
- };
48665
- }
48666
- return { journalCommitted: writesLedger && Boolean(journalId) };
48667
- }
48668
- });
48669
- return okEnvelope("apply_update", {
48670
- ...result,
48671
- ...appliedJournalId ? { journalId: appliedJournalId } : {},
48672
- architectureLedger: {
48673
- ...this.architectureLedger,
48674
- append: writesLedger ? ledgerAppend ?? { status: "not-appended" } : { status: "not-applicable" }
48675
- }
48676
- });
48677
- });
48911
+ return this.changeSetAuthority.applyUpdate(root, rawInput);
48678
48912
  }
48679
48913
  async inspectProjectionApplyReceipt(root, lookupKey) {
48680
48914
  return this.projectionApplies.inspectProjectionApplyReceipt(root, lookupKey);
@@ -48688,112 +48922,8 @@ class ArchctxDaemon {
48688
48922
  async recoverProjectionApply(root, intent) {
48689
48923
  return this.projectionApplies.recoverProjectionApply(root, intent);
48690
48924
  }
48691
- async appendAppliedChangeSetToArchitectureLedger(root, session, draft, journalId) {
48692
- const paths = runtimeStatePaths(root);
48693
- const scope = {
48694
- repository: {
48695
- repositoryId: session.workspace.repositoryId,
48696
- storageRepositoryId: paths.storageRepositoryId
48697
- },
48698
- worktree: {
48699
- workspaceId: paths.workspaceId,
48700
- storageWorkspaceId: paths.storageWorkspaceId,
48701
- branch: readCurrentBranch(root),
48702
- headSha: session.workspace.headSha,
48703
- worktreeDigest: computeWorktreeDigest(root)
48704
- }
48705
- };
48706
- const plan = planChangeSetApplyToArchitectureLedgerEvent({
48707
- ...scope,
48708
- draft,
48709
- files: listModelFiles(root),
48710
- previousEvidenceState: await this.localStore.replayArchitectureLedgerEvidence(scope),
48711
- createdAt: this.clock(),
48712
- writeMode: this.architectureLedger.writeMode === "ledger-with-projection" ? "ledger-with-projection" : "dual",
48713
- command: "archctx apply"
48714
- });
48715
- if (journalId)
48716
- await this.localStore.recordChangeSetLedgerPlan(journalId, { event: plan.event });
48717
- const appendInput = { writer: "runtime-daemon", events: [plan.event] };
48718
- const result = await this.appendArchitectureEventsWithFeed(root, appendInput, journalId);
48719
- return result;
48720
- }
48721
48925
  async acceptCommittedChange(root, rawInput) {
48722
- this.assertRunning();
48723
- let input;
48724
- try {
48725
- input = decodeAcceptCommittedChangeInput(rawInput);
48726
- } catch (error) {
48727
- if (error instanceof AcceptCommittedChangeInputError)
48728
- return errorEnvelope("ledger.accept-committed", "AC_SCHEMA_INVALID", error.message);
48729
- throw error;
48730
- }
48731
- if (this.architectureLedger.readMode !== "yaml" || this.architectureLedger.writeMode !== "yaml") {
48732
- return errorEnvelope("ledger.accept-committed", "AC_PRECONDITION_FAILED", "committed YAML acceptance requires YAML read and write authority");
48733
- }
48734
- return this.withWriter(async () => {
48735
- try {
48736
- const canonicalRoot = canonicalRepositoryRoot(root);
48737
- const journals = [];
48738
- for (const ref of input.journals)
48739
- journals.push(await this.localStore.readCommittedChangeSet(canonicalRoot, ref.journalId));
48740
- const model = loadNativeModelFromArchContext(canonicalRoot);
48741
- const worktreeDigest = architectureDocumentationProjectionWorktreeDigest(canonicalRoot, model);
48742
- const projection3 = buildArchitectureDocsProjection(this.projectionHost(), canonicalRoot, new Date(0).toISOString(), "repo-harness/v1");
48743
- const scope = acceptedCommittedChangeScope(canonicalRoot, worktreeDigest, resolveAcceptanceHeadSha(canonicalRoot));
48744
- const manifestWrite = await this.localStore.readLatestCommittedChangeSetFile(canonicalRoot, "docs/architecture/.projection-manifest.json");
48745
- const acceptance = planCommittedChangeAcceptance(canonicalRoot, {
48746
- requested: input.journals,
48747
- journals,
48748
- model,
48749
- existingFiles: projection3.loaded.existingFiles,
48750
- latestJournaledManifest: manifestWrite,
48751
- projection: projection3.plan,
48752
- currentEvidence: projection3.snapshotEvidence,
48753
- projectionWorktreeDigest: worktreeDigest,
48754
- scope
48755
- });
48756
- if (!input.approved) {
48757
- return okEnvelope("ledger.accept-committed", { status: "preview", plan: acceptance.plan, acceptancePlanId: acceptance.acceptancePlanId, expectedWorktreeDigest: worktreeDigest });
48758
- }
48759
- if (input.expectedWorktreeDigest !== worktreeDigest)
48760
- throw new Error("accepted ChangeSet expected worktree digest mismatch");
48761
- if (input.acceptancePlanId !== acceptance.acceptancePlanId)
48762
- throw new Error("accepted ChangeSet acceptance plan changed since preview");
48763
- const accepted = (event2, replayed) => {
48764
- const recorded = acceptedChangeFromEventV2(event2);
48765
- if (!recorded || !event2.eventHash)
48766
- throw new Error("committed ChangeSet acceptance readback failed");
48767
- return okEnvelope("ledger.accept-committed", {
48768
- status: "accepted",
48769
- replayed,
48770
- acceptedChange: recorded.acceptedChange,
48771
- acceptancePlanId: recorded.acceptancePlanId,
48772
- journalIds: input.journals.map((journal) => journal.journalId),
48773
- eventHash: event2.eventHash,
48774
- fileSetDigest: acceptance.plan.fileSetDigest
48775
- });
48776
- };
48777
- const existing = await this.localStore.readArchitectureEvent({ ...scope, eventId: acceptance.acceptedChange.eventId });
48778
- if (existing) {
48779
- if (acceptedChangeFromEventV2(existing)?.acceptancePlanId === acceptance.acceptancePlanId)
48780
- return accepted(existing, true);
48781
- throw new Error(`committed ChangeSet already has a different acceptance event at this snapshot: ${existing.eventId}`);
48782
- }
48783
- const ledgerGraphDigest = architectureLedgerStateDigest(await this.localStore.readArchitectureLedgerState(scope));
48784
- const event = acceptedCommittedChangeEventV2({ acceptance, scope, ledgerGraphDigest, timestamp: this.clock() });
48785
- const appended = await this.appendArchitectureEventsWithFeed(canonicalRoot, { writer: "runtime-daemon", events: [event] });
48786
- if (appended.appendedEvents.length !== 1)
48787
- throw new Error("committed ChangeSet acceptance event was not appended");
48788
- const readback = await this.localStore.readArchitectureEvent({ ...scope, eventId: event.eventId });
48789
- if (!readback || readback.eventType !== event.eventType || readback.payloadVersion !== event.payloadVersion) {
48790
- throw new Error("committed ChangeSet acceptance readback failed");
48791
- }
48792
- return accepted(readback, false);
48793
- } catch (error) {
48794
- return errorEnvelope("ledger.accept-committed", "AC_PRECONDITION_FAILED", error instanceof Error ? error.message : String(error));
48795
- }
48796
- });
48926
+ return this.changeSetAuthority.acceptCommittedChange(root, rawInput);
48797
48927
  }
48798
48928
  async ledgerState(root) {
48799
48929
  this.assertRunning();
@@ -49073,7 +49203,7 @@ class ArchctxDaemon {
49073
49203
  }
49074
49204
  async restoreRepositorySessions() {
49075
49205
  for (const record3 of await this.localStore.listRepositorySessions()) {
49076
- if (!record3.root || !existsSync24(record3.root))
49206
+ if (!record3.root || !existsSync25(record3.root))
49077
49207
  continue;
49078
49208
  if (repositoryFingerprint(record3.root) !== record3.repositoryId)
49079
49209
  continue;
@@ -49135,16 +49265,6 @@ class ArchctxDaemon {
49135
49265
  function shortDigest3(digest2) {
49136
49266
  return digest2.replace(/^sha256:/, "").slice(0, 16);
49137
49267
  }
49138
- function runtimeWorktreeDigest(root, profile) {
49139
- switch (profile) {
49140
- case "repository":
49141
- return computeWorktreeDigest(root);
49142
- case "architecture-documentation-projection":
49143
- return architectureDocumentationProjectionWorktreeDigest(root, loadNativeModelFromArchContext(root));
49144
- default:
49145
- throw new RuntimeUpdateInputError(`unsupported worktree digest profile: ${String(profile)}`);
49146
- }
49147
- }
49148
49268
  function codeFactsDigest(snapshot) {
49149
49269
  return digestJson({
49150
49270
  schemaVersion: "archcontext.code-facts-digest/v1",
@@ -49170,19 +49290,6 @@ function architectureLedgerScopeForWorkspace(workspace) {
49170
49290
  }
49171
49291
  };
49172
49292
  }
49173
- function acceptedCommittedChangeScope(root, worktreeDigest, headSha) {
49174
- const paths = runtimeStatePaths(root);
49175
- return {
49176
- repository: { repositoryId: repositoryFingerprint(root), storageRepositoryId: paths.storageRepositoryId },
49177
- worktree: {
49178
- workspaceId: projectionWorkspaceId(root),
49179
- storageWorkspaceId: paths.storageWorkspaceId,
49180
- branch: readCurrentBranch(root),
49181
- headSha,
49182
- worktreeDigest
49183
- }
49184
- };
49185
- }
49186
49293
  function validateModelFiles(files) {
49187
49294
  const errors2 = [];
49188
49295
  const paths = new Set(files.map((file) => file.path));
@@ -49227,18 +49334,6 @@ function schemaVersionFromModelBody(body) {
49227
49334
  } catch {}
49228
49335
  return "";
49229
49336
  }
49230
- function safePracticeWaiverId(explicit, waiver) {
49231
- const explicitTrimmed = explicit?.trim();
49232
- if (explicitTrimmed && (explicitTrimmed === "." || explicitTrimmed === ".." || explicitTrimmed.includes("/") || explicitTrimmed.includes("\\"))) {
49233
- throw new Error("practice-waiver-id-invalid");
49234
- }
49235
- const evidencePrefix = waiver.evidenceDigest.startsWith("sha256:") ? waiver.evidenceDigest.slice("sha256:".length, "sha256:".length + 12) : waiver.evidenceDigest.slice(0, 12);
49236
- const candidate = (explicitTrimmed || [waiver.practiceId.replace(/\./g, "-"), waiver.checkId ?? "all", evidencePrefix].join("-")).replace(/[^A-Za-z0-9_.-]/g, "-").replace(/-+/g, "-").replace(/^-+|-+$/g, "").slice(0, 96);
49237
- if (!candidate || candidate === "." || candidate === ".." || candidate.includes("/") || candidate.includes("\\")) {
49238
- throw new Error("practice-waiver-id-invalid");
49239
- }
49240
- return candidate;
49241
- }
49242
49337
  async function createStartedDaemon(deps = {}) {
49243
49338
  const daemon = new ArchctxDaemon(deps);
49244
49339
  await daemon.start();
@@ -49359,9 +49454,6 @@ function readRuntimeArchitectureLedgerWriteMode(value) {
49359
49454
  function architectureLedgerReadAuthority(mode) {
49360
49455
  return mode === "ledger" ? "ledger" : "yaml";
49361
49456
  }
49362
- function architectureLedgerWriteAppendsEvents(mode) {
49363
- return mode === "dual" || mode === "ledger-with-projection";
49364
- }
49365
49457
  var GIT_OBJECT_NAME_PATTERN = /^[0-9a-f]{7,64}$/;
49366
49458
  function readChangedPathsSince(root, commit) {
49367
49459
  if (!GIT_OBJECT_NAME_PATTERN.test(commit)) {
@@ -49400,7 +49492,7 @@ function blockedProductionInjections(deps) {
49400
49492
  ].filter((key) => (key in deps));
49401
49493
  }
49402
49494
  function completeTaskProjectionDrift(root) {
49403
- if (!existsSync24(resolve25(root, "docs/architecture/.projection-manifest.json")))
49495
+ if (!existsSync25(resolve26(root, "docs/architecture/.projection-manifest.json")))
49404
49496
  return;
49405
49497
  const profile = loadArchitectureDocumentationProfile(root);
49406
49498
  const loaded = loadArchitectureDocumentationInputs(root, profile);
@@ -50280,7 +50372,7 @@ async function runCliUnchecked(command2 = "help", args2 = [], cwd, deps = {}) {
50280
50372
  const subcommand = args2[0] ?? "list";
50281
50373
  if (subcommand === "add") {
50282
50374
  const root = readFlag(args2, "--root") ?? cwd;
50283
- if (resolve26(root) !== resolve26(cwd)) {
50375
+ if (resolve27(root) !== resolve27(cwd)) {
50284
50376
  return errorEnvelope("repo.add", "AC_CAPABILITY_UNSUPPORTED", "Multi-repo architecture context is outside the Local Core MVP; run archctx inside one Git repository.");
50285
50377
  }
50286
50378
  const daemon2 = await runtime();
@@ -51517,8 +51609,8 @@ function auditManifestGateRoot(cwd) {
51517
51609
  }
51518
51610
  }
51519
51611
  function auditGithubIssuesEnabled(cwd) {
51520
- const manifestPath = resolve26(auditManifestGateRoot(cwd), ".archcontext/manifest.yaml");
51521
- if (!existsSync25(manifestPath))
51612
+ const manifestPath = resolve27(auditManifestGateRoot(cwd), ".archcontext/manifest.yaml");
51613
+ if (!existsSync26(manifestPath))
51522
51614
  return false;
51523
51615
  let raw;
51524
51616
  try {
@@ -51904,7 +51996,7 @@ async function runGithubCommand(args2, cwd, deps) {
51904
51996
  }
51905
51997
  if (subcommand === "disconnect") {
51906
51998
  const connection = await deps.githubConnectionReader?.readVerifiedConnection({ repositoryRoot: cwd });
51907
- const localRecordRemoved = existsSync25(connectionPath);
51999
+ const localRecordRemoved = existsSync26(connectionPath);
51908
52000
  rmSync16(connectionPath, { force: true });
51909
52001
  return okEnvelope("github.disconnect", {
51910
52002
  connected: connection !== undefined,
@@ -52216,7 +52308,7 @@ async function readReviewChallengeV2Arg(args2, cwd, commandName = "github verify
52216
52308
  if (!inline && !challengePath)
52217
52309
  return { ok: false, message: `${commandName} requires --challenge-json or --challenge-path` };
52218
52310
  try {
52219
- const raw = inline ?? readFileSync27(resolve26(cwd, challengePath), "utf8");
52311
+ const raw = inline ?? readFileSync27(resolve27(cwd, challengePath), "utf8");
52220
52312
  const parsed = JSON.parse(raw);
52221
52313
  const attestation = await Promise.resolve().then(() => (init_src6(), exports_src2));
52222
52314
  const assertReviewChallengeV23 = attestation.assertReviewChallengeV2;
@@ -52691,10 +52783,10 @@ function doctorSqlite(cwd) {
52691
52783
  const legacyLocalStore = inspectLegacyLocalStoreMigration(cwd);
52692
52784
  return {
52693
52785
  path,
52694
- exists: existsSync25(path),
52786
+ exists: existsSync26(path),
52695
52787
  migrations: productVersionManifest().runtime.sqliteMigrations,
52696
52788
  legacyPath: paths.legacyLocalStorePath,
52697
- legacyExists: existsSync25(paths.legacyLocalStorePath),
52789
+ legacyExists: existsSync26(paths.legacyLocalStorePath),
52698
52790
  legacyLocalStore
52699
52791
  };
52700
52792
  }
@@ -52725,7 +52817,7 @@ function runtimePathsReport(cwd) {
52725
52817
  };
52726
52818
  }
52727
52819
  function pathAccess(path) {
52728
- const exists = existsSync25(path);
52820
+ const exists = existsSync26(path);
52729
52821
  return {
52730
52822
  path,
52731
52823
  exists,
@@ -53183,7 +53275,7 @@ async function waitForDaemonReady(cwd, timeoutMs, shouldStop) {
53183
53275
  return;
53184
53276
  }
53185
53277
  function sleep(ms) {
53186
- return new Promise((resolve27) => setTimeout(resolve27, ms));
53278
+ return new Promise((resolve28) => setTimeout(resolve28, ms));
53187
53279
  }
53188
53280
  function readFileTail(path, maxBytes = 4096) {
53189
53281
  try {
@@ -53196,8 +53288,8 @@ function readFileTail(path, maxBytes = 4096) {
53196
53288
  async function runForegroundDaemon(cwd, args2) {
53197
53289
  const daemon = await createStartedProductionDaemon({ root: cwd });
53198
53290
  let resolveStopped;
53199
- const stopped = new Promise((resolve27) => {
53200
- resolveStopped = resolve27;
53291
+ const stopped = new Promise((resolve28) => {
53292
+ resolveStopped = resolve28;
53201
53293
  });
53202
53294
  const idleTimeoutFlag = readFlag(args2, "--idle-timeout-ms");
53203
53295
  const server = new ArchctxRuntimeRpcServer(daemon, {