archctx 0.7.0 → 0.7.1

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
@@ -167,6 +167,65 @@ var init_architecture = __esm(() => {
167
167
  ];
168
168
  });
169
169
 
170
+ // packages/contracts/src/docs-anchors.ts
171
+ function docsAnchorsReportIssues(report) {
172
+ const issues = [];
173
+ const ids = report.nodes.map((node) => node.nodeId);
174
+ const sorted = [...new Set(ids)].sort();
175
+ if (sorted.length !== ids.length || sorted.some((id, index) => id !== ids[index]))
176
+ issues.push("nodes.nodeId must be sorted and unique");
177
+ const findings = report.nodes.flatMap((node) => node.findings);
178
+ const unchecked = report.nodes.flatMap((node) => node.unchecked);
179
+ if (report.summary.citations !== report.nodes.reduce((total, node) => total + node.citationCount, 0))
180
+ issues.push("summary.citations must equal the sum of nodes[].citationCount");
181
+ if (report.summary.findings !== findings.length)
182
+ issues.push("summary.findings must equal the number of findings");
183
+ if (report.summary.unchecked !== unchecked.length)
184
+ issues.push("summary.unchecked must equal the number of unchecked citations");
185
+ for (const kind of DOCS_ANCHOR_FINDING_KINDS) {
186
+ if (report.summary.byKind[kind] !== findings.filter((finding) => finding.kind === kind).length)
187
+ issues.push(`summary.byKind.${kind} must equal the number of ${kind} findings`);
188
+ }
189
+ for (const [index, node] of report.nodes.entries()) {
190
+ if (node.citationCount === 0)
191
+ issues.push(`nodes[${index}] must have at least one citation`);
192
+ for (const [findingIndex, finding] of node.findings.entries()) {
193
+ issues.push(...docsAnchorFindingIssues(finding, `nodes[${index}].findings[${findingIndex}]`));
194
+ }
195
+ }
196
+ if (report.codeGraph.availability === "ready" && unchecked.some((entry) => entry.reasonCode === "codegraph-unavailable")) {
197
+ issues.push("codegraph-unavailable is reported only when codeGraph.availability is unavailable");
198
+ }
199
+ return issues;
200
+ }
201
+ function docsAnchorFindingIssues(finding, prefix) {
202
+ const issues = [];
203
+ const { citation } = finding;
204
+ if (finding.kind === "missing-path" && citation.path === undefined)
205
+ issues.push(`${prefix} missing-path must name citation.path`);
206
+ if (finding.kind === "missing-symbol" && citation.symbol === undefined)
207
+ issues.push(`${prefix} missing-symbol must name citation.symbol`);
208
+ if (finding.kind === "line-out-of-range" && (citation.line === undefined || finding.lineCount === undefined)) {
209
+ issues.push(`${prefix} line-out-of-range must name citation.line and lineCount`);
210
+ }
211
+ if (finding.lineCount !== undefined !== (finding.kind === "line-out-of-range"))
212
+ issues.push(`${prefix}.lineCount is present only on line-out-of-range`);
213
+ if (finding.kind === "line-moved") {
214
+ const lines = finding.currentLines ?? [];
215
+ if (citation.line === undefined || citation.symbol === undefined)
216
+ issues.push(`${prefix} line-moved must name citation.line and citation.symbol`);
217
+ if (lines.length === 0 || lines.some((line, index) => index > 0 && line <= lines[index - 1]))
218
+ issues.push(`${prefix}.currentLines must be non-empty, sorted and unique`);
219
+ } else if (finding.currentLines !== undefined) {
220
+ issues.push(`${prefix}.currentLines is present only on line-moved`);
221
+ }
222
+ return issues;
223
+ }
224
+ var DOCS_ANCHORS_REPORT_SCHEMA_VERSION = "archcontext.docs-anchors/v1", DOCS_ANCHOR_FINDING_KINDS;
225
+ var init_docs_anchors = __esm(() => {
226
+ DOCS_ANCHOR_FINDING_KINDS = ["line-moved", "line-out-of-range", "missing-path", "missing-symbol"];
227
+ });
228
+
170
229
  // packages/contracts/src/external-docs.ts
171
230
  var EXTERNAL_DOCUMENTATION_RESOURCE_SCHEMA_VERSION = "archcontext.external-document/v1", CONTEXT7_LOCKFILE_SCHEMA_VERSION = "archcontext.context7-lock/v1";
172
231
 
@@ -674,6 +733,9 @@ var init_ledger = __esm(() => {
674
733
  ];
675
734
  });
676
735
 
736
+ // packages/contracts/src/ownership-dependencies.ts
737
+ var CHANGESET_OWNERSHIP_DELTA_SCHEMA_VERSION = "archcontext.changeset-ownership-delta/v1", CHANGESET_OWNERSHIP_DELTA_PATH_LIMIT = 50, CAPABILITY_DEPENDENCIES_SCHEMA_VERSION = "archcontext.capability-dependencies/v1", CAPABILITY_DEPENDENCIES_EDGE_LIMIT = 200, CAPABILITY_DEPENDENCIES_CALLS_UNAVAILABLE_REASON = "call-edges-not-exposed";
738
+
677
739
  // packages/contracts/src/ports.ts
678
740
  function baseModelBlockingErrors(result) {
679
741
  if (result.valid)
@@ -1407,7 +1469,7 @@ function sortedUniqueIssues(label, values) {
1407
1469
  const expected = [...new Set(values)].sort();
1408
1470
  return expected.length === values.length && expected.every((value, index) => value === values[index]) ? [] : [`${label} must be sorted and unique`];
1409
1471
  }
1410
- var PROJECTION_REQUEST_SCHEMA_VERSION = "archcontext.projection-request/v1", PROJECTION_RESULT_SCHEMA_VERSION = "archcontext.projection-result/v2", PROJECTION_APPLY_IDENTITY_SCHEMA_VERSION = "archcontext.projection-apply-identity/v1", PROJECTION_APPLY_RECEIPT_SCHEMA_VERSION = "archcontext.projection-apply-receipt/v1", PROJECTION_APPLY_RECOVERY_BINDING_SCHEMA_VERSION = "archcontext.projection-apply-recovery-binding/v1", PROJECTION_APPLY_RECOVERY_INTENT_SCHEMA_VERSION = "archcontext.projection-apply-recovery-intent/v1", PROJECTION_APPLY_RECOVERY_PROOF_SCHEMA_VERSION = "archcontext.projection-apply-recovery-proof/v1", PROJECTION_APPLY_RECOVERY_RESULT_SCHEMA_VERSION = "archcontext.projection-apply-recovery-result/v1", PROJECTION_APPLY_READBACK_RESULT_SCHEMA_VERSION = "archcontext.projection-apply-readback-result/v1", ARCHITECTURE_REFRESH_SIGNAL_SCHEMA_VERSION = "archcontext.architecture-refresh-signal/v1", ARCHCTX_CAPABILITIES_SCHEMA_VERSION = "archcontext.capabilities/v1", ARCHITECTURE_DOCS_RENDERER_VERSION = "archcontext.docs-renderer/v5", AGENT_CONTEXT_RENDERER_VERSION = "archcontext.agent-context-renderer/v1", PROJECTION_MODES, PROJECTION_PRIOR_COMMITTED_APPLY_OPERATIONS, PROJECTION_TARGETS, PROJECTION_HUMAN_ACTION_REASON_CODES, ARCHITECTURE_MAJOR_CHANGE_REASON_CODES, ARCHCTX_FEATURES, ARCHITECTURE_SEMANTIC_FACETS, ARCHITECTURE_PROOF_P1_STATUSES, ARCHITECTURE_PROOF_P2_STATUSES, PROJECTION_FILE_PREVIEW_MAX_BYTES = 65536, PROJECTION_PREVIEW_TOTAL_MAX_BYTES = 1048576, PROJECTION_FRESHNESS_REASON_CODES, UTF8, SHA256_DIGEST, ISO_INSTANT;
1472
+ var PROJECTION_REQUEST_SCHEMA_VERSION = "archcontext.projection-request/v1", PROJECTION_RESULT_SCHEMA_VERSION = "archcontext.projection-result/v2", PROJECTION_APPLY_IDENTITY_SCHEMA_VERSION = "archcontext.projection-apply-identity/v1", PROJECTION_APPLY_RECEIPT_SCHEMA_VERSION = "archcontext.projection-apply-receipt/v1", PROJECTION_APPLY_RECOVERY_BINDING_SCHEMA_VERSION = "archcontext.projection-apply-recovery-binding/v1", PROJECTION_APPLY_RECOVERY_INTENT_SCHEMA_VERSION = "archcontext.projection-apply-recovery-intent/v1", PROJECTION_APPLY_RECOVERY_PROOF_SCHEMA_VERSION = "archcontext.projection-apply-recovery-proof/v1", PROJECTION_APPLY_RECOVERY_RESULT_SCHEMA_VERSION = "archcontext.projection-apply-recovery-result/v1", PROJECTION_APPLY_READBACK_RESULT_SCHEMA_VERSION = "archcontext.projection-apply-readback-result/v1", ARCHITECTURE_REFRESH_SIGNAL_SCHEMA_VERSION = "archcontext.architecture-refresh-signal/v1", ARCHCTX_CAPABILITIES_SCHEMA_VERSION = "archcontext.capabilities/v1", ARCHITECTURE_DOCS_RENDERER_VERSION = "archcontext.docs-renderer/v5", AGENT_CONTEXT_RENDERER_VERSION = "archcontext.agent-context-renderer/v1", PROJECTION_MODES, PROJECTION_PRIOR_COMMITTED_APPLY_OPERATIONS, PROJECTION_TARGETS, PROJECTION_HUMAN_ACTION_REASON_CODES, ARCHITECTURE_MAJOR_CHANGE_REASON_CODES, ARCHCTX_FEATURES, ARCHITECTURE_SEMANTIC_FACETS, ARCHITECTURE_PROOF_P1_STATUSES, ARCHITECTURE_PROOF_P2_STATUSES, ARCHITECTURE_FLOW_SELECTOR_CHECK_SCHEMA_VERSION = "archcontext.flow-selector-check/v1", PROJECTION_FILE_PREVIEW_MAX_BYTES = 65536, PROJECTION_PREVIEW_TOTAL_MAX_BYTES = 1048576, PROJECTION_FRESHNESS_REASON_CODES, UTF8, SHA256_DIGEST, ISO_INSTANT;
1411
1473
  var init_projection = __esm(() => {
1412
1474
  init_schema();
1413
1475
  PROJECTION_MODES = ["check", "plan", "apply", "adopt"];
@@ -1440,8 +1502,12 @@ var init_projection = __esm(() => {
1440
1502
  "architecture-change-capabilities-v1",
1441
1503
  "architecture-docs-renderer-v2",
1442
1504
  "architecture-refresh-signal-v1",
1505
+ "capability-dependencies-v1",
1443
1506
  "changeset-entity-operations-v1",
1507
+ "changeset-ownership-delta-v1",
1444
1508
  "code-facts-unavailable-error-v1",
1509
+ "docs-anchors-v1",
1510
+ "flow-selector-check-v1",
1445
1511
  "module-statistics-v1",
1446
1512
  "projection-apply-readback-v1",
1447
1513
  "projection-apply-receipt-v1",
@@ -1459,7 +1525,8 @@ var init_projection = __esm(() => {
1459
1525
  "refactor-assessment-v1",
1460
1526
  "refactor-observation-evidence-v1",
1461
1527
  "refactor-resolution-v1",
1462
- "refactor-scan-limits-v1"
1528
+ "refactor-scan-limits-v1",
1529
+ "refactor-scan-untracked-count-v1"
1463
1530
  ];
1464
1531
  ARCHITECTURE_SEMANTIC_FACETS = [
1465
1532
  "constraints",
@@ -1802,7 +1869,7 @@ function productVersionManifest() {
1802
1869
  }
1803
1870
  };
1804
1871
  }
1805
- var ARCHCONTEXT_PRODUCT_NAME = "archctx", ARCHCONTEXT_PRODUCT_VERSION = "0.7.0", ARCHCONTEXT_PACKAGE_MANAGER = "bun@1.4.3", 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";
1872
+ var ARCHCONTEXT_PRODUCT_NAME = "archctx", ARCHCONTEXT_PRODUCT_VERSION = "0.7.1", ARCHCONTEXT_PACKAGE_MANAGER = "bun@1.4.3", 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";
1806
1873
 
1807
1874
  // packages/contracts/src/refactor.ts
1808
1875
  function refactorProposalDigest(proposal) {
@@ -1919,6 +1986,9 @@ function refactorRequestInvariantIssues(request, prefix = "request") {
1919
1986
  }
1920
1987
  if (request.proposal)
1921
1988
  issues.push(...refactorProposalInvariantIssues(request.proposal, `${prefix}.proposal`));
1989
+ if (request.importNodeLimit !== undefined && !(Number.isInteger(request.importNodeLimit) && request.importNodeLimit >= 1)) {
1990
+ issues.push(`${prefix}.importNodeLimit must be a positive integer`);
1991
+ }
1922
1992
  return issues;
1923
1993
  }
1924
1994
  function refactorVerificationRequestInvariantIssues(request, prefix = "request") {
@@ -1937,6 +2007,9 @@ function refactorVerificationRequestInvariantIssues(request, prefix = "request")
1937
2007
  if (request.expectedWorktreeDigest !== undefined) {
1938
2008
  issues.push(...digestIssues(`${prefix}.expectedWorktreeDigest`, request.expectedWorktreeDigest));
1939
2009
  }
2010
+ if (request.importNodeLimit !== undefined && !(Number.isInteger(request.importNodeLimit) && request.importNodeLimit >= 1)) {
2011
+ issues.push(`${prefix}.importNodeLimit must be a positive integer`);
2012
+ }
1940
2013
  if (request.executionEvidenceRefs !== undefined) {
1941
2014
  if (!Array.isArray(request.executionEvidenceRefs)) {
1942
2015
  issues.push(`${prefix}.executionEvidenceRefs must be an array`);
@@ -2013,6 +2086,16 @@ function moduleStatisticsSnapshotInvariantIssues(snapshot, prefix = "snapshot")
2013
2086
  }
2014
2087
  }
2015
2088
  issues.push(...nonNegativeIntegerIssues(`${prefix}.codeFacts.edgeLimit`, snapshot.codeFacts.edgeLimit));
2089
+ const importNodeCount = snapshot.codeFacts.importNodeCount ?? null;
2090
+ issues.push(...nonNegativeIntegerIssues(`${prefix}.codeFacts.importNodeCount`, importNodeCount));
2091
+ if (snapshot.codeFacts.reasonCodes.includes("code-facts-import-limit")) {
2092
+ const { edgeLimit } = snapshot.codeFacts;
2093
+ if (snapshot.codeFacts.coverage !== "partial")
2094
+ issues.push(`${prefix}.codeFacts.coverage must be partial when the import limit was reached`);
2095
+ if (edgeLimit === null || importNodeCount !== null && importNodeCount <= edgeLimit) {
2096
+ issues.push(`${prefix}.codeFacts.importNodeCount must exceed edgeLimit when the import limit was reached`);
2097
+ }
2098
+ }
2016
2099
  for (const [field, value] of Object.entries(snapshot.repositorySummary)) {
2017
2100
  issues.push(...nonNegativeIntegerIssues(`${prefix}.repositorySummary.${field}`, value));
2018
2101
  }
@@ -2467,6 +2550,7 @@ var init_refactor = __esm(() => {
2467
2550
  "executionEvidenceRefs",
2468
2551
  "expectedHeadSha",
2469
2552
  "expectedWorktreeDigest",
2553
+ "importNodeLimit",
2470
2554
  "recommendationId",
2471
2555
  "schemaVersion"
2472
2556
  ];
@@ -2479,6 +2563,7 @@ var init_refactor = __esm(() => {
2479
2563
  ];
2480
2564
  REFACTOR_SCALE_REASON_CODES = [
2481
2565
  "caller-coverage-unknown",
2566
+ "code-facts-import-limit",
2482
2567
  "code-facts-missing",
2483
2568
  "code-facts-truncated",
2484
2569
  "major-change-detected",
@@ -2712,6 +2797,7 @@ var init_validator = __esm(() => {
2712
2797
  var init_src = __esm(() => {
2713
2798
  init_control_plane_routes();
2714
2799
  init_architecture();
2800
+ init_docs_anchors();
2715
2801
  init_github_governance();
2716
2802
  init_ledger();
2717
2803
  init_ports();
@@ -13366,14 +13452,18 @@ function sanitizeGithubDeveloperReviewState(state, statePath) {
13366
13452
  assertNoReviewSecrets(data, []);
13367
13453
  return data;
13368
13454
  }
13455
+ function isStandardBase64Value(token) {
13456
+ return token.length % 4 === 0 && /^[A-Za-z0-9+/]+={0,2}$/.test(token) && /[A-Za-z0-9+]{20}/.test(token);
13457
+ }
13369
13458
  function assertNoCliSecretMaterial(value) {
13370
13459
  const serialized = typeof value === "string" ? value : JSON.stringify(value);
13371
13460
  if (!serialized)
13372
13461
  return;
13373
13462
  if (/-----BEGIN [A-Z ]*PRIVATE KEY-----/.test(serialized))
13374
13463
  throw new Error("device-private-key-material-forbidden");
13375
- if (/(^|["'\s])(?:file:\/\/|\/|\.\/|\.\.\/|~\/)[^"'\s]*(?:private|device|key)[^"'\s]*/i.test(serialized)) {
13376
- throw new Error("device-private-key-file-ref-forbidden");
13464
+ for (const [, token] of serialized.matchAll(/(?:^|["'\s])((?:file:\/\/|\.{0,2}\/|~\/)[^"'\s]*)/gi)) {
13465
+ if (/private|device|key/i.test(token) && !isStandardBase64Value(token))
13466
+ throw new Error("device-private-key-file-ref-forbidden");
13377
13467
  }
13378
13468
  if (/(access|refresh|token)_[A-Za-z0-9_-]+/.test(serialized))
13379
13469
  throw new Error("github-token-material-forbidden");
@@ -15017,7 +15107,7 @@ function parseScalar(value) {
15017
15107
 
15018
15108
  // packages/local-runtime/git-adapter/src/index.ts
15019
15109
  import { execFileSync as execFileSync2, spawnSync } from "node:child_process";
15020
- import { existsSync as existsSync6, lstatSync as lstatSync3, mkdirSync as mkdirSync4, mkdtempSync, readFileSync as readFileSync7, rmSync as rmSync4 } from "node:fs";
15110
+ import { existsSync as existsSync6, lstatSync as lstatSync3, mkdirSync as mkdirSync4, mkdtempSync, readdirSync as readdirSync4, readFileSync as readFileSync7, rmSync as rmSync4 } from "node:fs";
15021
15111
  import { dirname as dirname4, join as join4, resolve as resolve8 } from "node:path";
15022
15112
  import { tmpdir } from "node:os";
15023
15113
 
@@ -16363,29 +16453,86 @@ function validateStep(input) {
16363
16453
  input.diagnostics.push({ code: "selector-declaration-ambiguous", flowId: input.flowId, stepId: input.step.id, detail: selectorKey });
16364
16454
  return;
16365
16455
  }
16366
- const evidence = input.evidence.filter((entry) => evidenceSelectorKey(entry) === selectorKey && entry.nodeId === declarations[0].nodeId);
16367
- if (evidence.length === 0) {
16368
- input.diagnostics.push({ code: "selector-evidence-missing", flowId: input.flowId, stepId: input.step.id, detail: selectorKey });
16369
- return;
16370
- }
16371
- if (evidence.length > 1) {
16372
- input.diagnostics.push({ code: "selector-evidence-ambiguous", flowId: input.flowId, stepId: input.step.id, detail: selectorKey });
16373
- return;
16374
- }
16375
- if (evidence[0].ambiguous) {
16376
- input.diagnostics.push({ code: "selector-evidence-ambiguous", flowId: input.flowId, stepId: input.step.id, detail: selectorKey });
16377
- return;
16378
- }
16379
- if (evidence[0].truncated) {
16380
- input.diagnostics.push({ code: "selector-evidence-truncated", flowId: input.flowId, stepId: input.step.id, detail: selectorKey });
16381
- return;
16382
- }
16383
- if (!evidence[0].matched || evidence[0].callSites.length === 0) {
16384
- input.diagnostics.push({ code: "selector-evidence-unmatched", flowId: input.flowId, stepId: input.step.id, detail: selectorKey });
16456
+ const status = architectureSelectorProofStatus(input.evidence.filter((entry) => evidenceSelectorKey(entry) === selectorKey && entry.nodeId === declarations[0].nodeId));
16457
+ if (status !== "proven") {
16458
+ input.diagnostics.push({ code: SELECTOR_STATUS_DIAGNOSTICS[status], flowId: input.flowId, stepId: input.step.id, detail: selectorKey });
16385
16459
  return;
16386
16460
  }
16387
16461
  input.provenSelectorKeys.add(selectorKey);
16388
16462
  }
16463
+ var SELECTOR_STATUS_DIAGNOSTICS = {
16464
+ "ambiguous-symbol": "selector-evidence-ambiguous",
16465
+ "evidence-missing": "selector-evidence-missing",
16466
+ "evidence-truncated": "selector-evidence-truncated",
16467
+ "false-builtin-call": "selector-evidence-unmatched",
16468
+ "no-direct-call": "selector-evidence-unmatched",
16469
+ "only-references": "selector-evidence-unmatched"
16470
+ };
16471
+ function architectureSelectorProofStatus(evidence) {
16472
+ if (evidence.length === 0)
16473
+ return "evidence-missing";
16474
+ if (evidence.length > 1 || evidence[0].ambiguous)
16475
+ return "ambiguous-symbol";
16476
+ if (evidence[0].truncated)
16477
+ return "evidence-truncated";
16478
+ if (evidence[0].matched && evidence[0].callSites.length > 0)
16479
+ return "proven";
16480
+ if ((evidence[0].rejectedBuiltinCallSites ?? []).length > 0)
16481
+ return "false-builtin-call";
16482
+ if ((evidence[0].referenceSites ?? []).length > 0)
16483
+ return "only-references";
16484
+ return "no-direct-call";
16485
+ }
16486
+ function checkSemanticFlowSelectors(input) {
16487
+ const stepsByKey = new Map;
16488
+ for (const flow of [...input.flows].sort((left, right) => left.id.localeCompare(right.id))) {
16489
+ if (flow.applicability !== "required")
16490
+ continue;
16491
+ for (const step of [...flow.steps, ...flow.outcomes.flatMap((outcome) => outcome.steps)]) {
16492
+ const key = evidenceSelectorKey(step.evidence);
16493
+ stepsByKey.set(key, [...stepsByKey.get(key) ?? [], { flowId: flow.id, stepId: step.id }]);
16494
+ }
16495
+ }
16496
+ const selectors = input.nodes.flatMap((node) => (node.source?.entrypoints ?? []).flatMap((entrypoint) => entrypoint.symbols.flatMap((source) => source.sinks.map((sink) => {
16497
+ const key = evidenceSelectorKey({ entrypointId: entrypoint.id, sourceSymbol: source.name, sinkId: sink.id });
16498
+ const evidence = input.evidence.filter((entry) => evidenceSelectorKey(entry) === key && entry.nodeId === node.id);
16499
+ const single = evidence.length === 1 ? evidence[0] : undefined;
16500
+ return {
16501
+ nodeId: node.id,
16502
+ entrypointId: entrypoint.id,
16503
+ sourcePath: entrypoint.path,
16504
+ sourceSymbol: source.name,
16505
+ sinkId: sink.id,
16506
+ sinkPath: sink.path,
16507
+ sinkSymbol: sink.symbol,
16508
+ status: architectureSelectorProofStatus(evidence),
16509
+ callSites: single?.callSites ?? [],
16510
+ referenceSites: single?.referenceSites ?? [],
16511
+ rejectedBuiltinCallSites: single?.rejectedBuiltinCallSites ?? [],
16512
+ flowSteps: stepsByKey.get(key) ?? []
16513
+ };
16514
+ })))).sort((left, right) => compareStrings(left.nodeId, right.nodeId) || compareStrings(left.entrypointId, right.entrypointId) || compareStrings(left.sourceSymbol, right.sourceSymbol) || compareStrings(left.sinkId, right.sinkId));
16515
+ const capabilityIds = [...new Set(input.flows.map((flow) => flow.capabilityId))].sort(compareStrings);
16516
+ const capabilities = capabilityIds.map((capabilityId) => {
16517
+ const { p2 } = compileSemanticCapabilityDiagrams({ capabilityId, nodes: input.nodes, relations: input.relations, flows: input.flows, evidence: input.evidence });
16518
+ return { capabilityId, p2Status: p2.status, diagnostics: p2.diagnostics };
16519
+ });
16520
+ const unproven = selectors.filter((selector) => selector.status !== "proven");
16521
+ return {
16522
+ ok: capabilities.every((capability) => capability.p2Status !== "unprovable"),
16523
+ summary: {
16524
+ selectors: selectors.length,
16525
+ proven: selectors.length - unproven.length,
16526
+ unproven: unproven.length,
16527
+ usedUnproven: unproven.filter((selector) => selector.flowSteps.length > 0).length
16528
+ },
16529
+ selectors,
16530
+ capabilities
16531
+ };
16532
+ }
16533
+ function compareStrings(left, right) {
16534
+ return left < right ? -1 : left > right ? 1 : 0;
16535
+ }
16389
16536
  function proofRelevantSelectorEvidence(evidence) {
16390
16537
  return evidence.map((entry) => ({
16391
16538
  nodeId: entry.nodeId,
@@ -16486,7 +16633,7 @@ function mermaidStableId(prefix, value) {
16486
16633
  return `${prefix}_${slug}_${digestJson(value).slice(7, 15)}`;
16487
16634
  }
16488
16635
  function escapeMermaidLabel(value) {
16489
- return value.replace(/["#;,]/g, (character) => ({ '"': "'", "#": "#", ";": ";", ",": "," })[character]);
16636
+ return value.replace(/["#;,]/g, (character) => ({ '"': "#quot;", "#": "#35;", ";": "#59;", ",": "#44;" })[character]);
16490
16637
  }
16491
16638
 
16492
16639
  // packages/core/projection-engine/src/major-change.ts
@@ -17142,6 +17289,374 @@ function diffHunks(ops) {
17142
17289
  }
17143
17290
  return hunks;
17144
17291
  }
17292
+ // packages/core/projection-engine/src/prose-anchors.ts
17293
+ init_src();
17294
+ var IDENTIFIER = "[A-Za-z_][A-Za-z0-9_$]*";
17295
+ var QUALIFIED_IDENTIFIER = `${IDENTIFIER}(?:\\.${IDENTIFIER})*`;
17296
+ var IDENTIFIER_PATTERN = new RegExp(`^${IDENTIFIER}$`);
17297
+ var QUALIFIED_PATTERN = new RegExp(`^${QUALIFIED_IDENTIFIER}$`);
17298
+ var CALL_PATTERN = new RegExp(`^(${QUALIFIED_IDENTIFIER})\\(\\)$`);
17299
+ var PATH_PATTERN = /^[A-Za-z0-9_.@+\-/]+$/;
17300
+ var LINE_FRAGMENT = /^L(\d+)(?:-L?(\d+))?$/;
17301
+ var LINE_SUFFIX = /^(.*?):(\d+)(?:-(\d+))?$/;
17302
+ var FILE_EXTENSION = /\.[A-Za-z0-9]+$/;
17303
+ var BINDING_GAP = /^\s*[([,:–—-]?\s*$/;
17304
+ var GENERATED_BEGIN = /<!-- BEGIN ARCHCONTEXT:generated target="([^"]+)"[^>]*-->/;
17305
+ function checkProseAnchors(input) {
17306
+ const nodes = [];
17307
+ let documents = 0;
17308
+ for (const node of [...input.nodes].sort((left, right) => left.nodeId < right.nodeId ? -1 : left.nodeId > right.nodeId ? 1 : 0)) {
17309
+ if (node.document)
17310
+ documents += 1;
17311
+ const result = checkNode(node, input);
17312
+ if (result.citationCount > 0)
17313
+ nodes.push(result);
17314
+ }
17315
+ const findings = nodes.flatMap((node) => node.findings);
17316
+ const byKind = Object.fromEntries(DOCS_ANCHOR_FINDING_KINDS.map((kind) => [kind, findings.filter((finding) => finding.kind === kind).length]));
17317
+ return {
17318
+ schemaVersion: DOCS_ANCHORS_REPORT_SCHEMA_VERSION,
17319
+ profile: input.profile,
17320
+ codeGraph: input.codeGraph.availability === "ready" ? { availability: "ready" } : { availability: "unavailable", reasonCode: input.codeGraph.reasonCode },
17321
+ summary: {
17322
+ nodes: input.nodes.length,
17323
+ documents,
17324
+ citations: nodes.reduce((total, node) => total + node.citationCount, 0),
17325
+ findings: findings.length,
17326
+ unchecked: nodes.reduce((total, node) => total + node.unchecked.length, 0),
17327
+ byKind
17328
+ },
17329
+ nodes
17330
+ };
17331
+ }
17332
+ function extractNodeProseCitations(node) {
17333
+ const located = [];
17334
+ if (typeof node.summary === "string") {
17335
+ for (const line of node.summary.split(/\r?\n/)) {
17336
+ located.push(...parseLineCitations(line).map((parsed) => ({ origin: { kind: "node-summary" }, parsed })));
17337
+ }
17338
+ }
17339
+ for (const [index, responsibility] of (node.responsibilities ?? []).entries()) {
17340
+ if (typeof responsibility !== "string")
17341
+ continue;
17342
+ for (const line of responsibility.split(/\r?\n/)) {
17343
+ located.push(...parseLineCitations(line).map((parsed) => ({ origin: { kind: "node-responsibility", index }, parsed })));
17344
+ }
17345
+ }
17346
+ if (node.document) {
17347
+ for (const { line, text } of humanOwnedMarkdownLines(node.document.body)) {
17348
+ located.push(...parseLineCitations(text).map((parsed) => ({ origin: { kind: "document", path: node.document.path, line }, parsed })));
17349
+ }
17350
+ }
17351
+ return located;
17352
+ }
17353
+ function humanOwnedMarkdownLines(body) {
17354
+ const lines = [];
17355
+ let generatedEnd;
17356
+ let fence;
17357
+ let inComment = false;
17358
+ for (const [index, raw] of body.split(/\r?\n/).entries()) {
17359
+ if (generatedEnd !== undefined) {
17360
+ if (raw.includes(generatedEnd))
17361
+ generatedEnd = undefined;
17362
+ continue;
17363
+ }
17364
+ const begin = GENERATED_BEGIN.exec(raw);
17365
+ if (begin) {
17366
+ const end = `<!-- END ARCHCONTEXT:generated target="${begin[1]}" -->`;
17367
+ if (!raw.slice(begin.index + begin[0].length).includes(end))
17368
+ generatedEnd = end;
17369
+ continue;
17370
+ }
17371
+ if (fence) {
17372
+ const close = /^ {0,3}(`{3,}|~{3,})\s*$/.exec(raw);
17373
+ if (close && close[1][0] === fence.char && close[1].length >= fence.length)
17374
+ fence = undefined;
17375
+ continue;
17376
+ }
17377
+ const open = /^ {0,3}(`{3,}|~{3,})/.exec(raw);
17378
+ if (open && !inComment) {
17379
+ fence = { char: open[1][0], length: open[1].length };
17380
+ continue;
17381
+ }
17382
+ const stripped = stripHtmlComments(raw, inComment);
17383
+ inComment = stripped.inComment;
17384
+ if (stripped.text.trim() !== "")
17385
+ lines.push({ line: index + 1, text: stripped.text });
17386
+ }
17387
+ return lines;
17388
+ }
17389
+ function stripHtmlComments(raw, inComment) {
17390
+ let text = "";
17391
+ let rest = raw;
17392
+ let open = inComment;
17393
+ while (rest.length > 0) {
17394
+ if (open) {
17395
+ const close = rest.indexOf("-->");
17396
+ if (close < 0)
17397
+ return { text, inComment: true };
17398
+ rest = rest.slice(close + 3);
17399
+ open = false;
17400
+ continue;
17401
+ }
17402
+ const start = rest.indexOf("<!--");
17403
+ if (start < 0)
17404
+ return { text: text + rest, inComment: false };
17405
+ text += rest.slice(0, start);
17406
+ rest = rest.slice(start + 4);
17407
+ open = true;
17408
+ }
17409
+ return { text, inComment: open };
17410
+ }
17411
+ function inlineCodeSpans(text) {
17412
+ const spans = [];
17413
+ let index = 0;
17414
+ while (index < text.length) {
17415
+ if (text[index] !== "`") {
17416
+ index += 1;
17417
+ continue;
17418
+ }
17419
+ if (precedingBackslashes(text, index) % 2 === 1) {
17420
+ index += 1;
17421
+ continue;
17422
+ }
17423
+ let runEnd = index;
17424
+ while (text[runEnd] === "`")
17425
+ runEnd += 1;
17426
+ const ticks = runEnd - index;
17427
+ let cursor = runEnd;
17428
+ let close = -1;
17429
+ while (cursor < text.length) {
17430
+ if (text[cursor] !== "`") {
17431
+ cursor += 1;
17432
+ continue;
17433
+ }
17434
+ let closeEnd = cursor;
17435
+ while (text[closeEnd] === "`")
17436
+ closeEnd += 1;
17437
+ if (closeEnd - cursor === ticks) {
17438
+ close = cursor;
17439
+ break;
17440
+ }
17441
+ cursor = closeEnd;
17442
+ }
17443
+ if (close < 0) {
17444
+ index = runEnd;
17445
+ continue;
17446
+ }
17447
+ let content = text.slice(runEnd, close);
17448
+ if (content.length > 2 && content.startsWith(" ") && content.endsWith(" "))
17449
+ content = content.slice(1, -1);
17450
+ spans.push({ content, start: index, end: close + ticks });
17451
+ index = close + ticks;
17452
+ }
17453
+ return spans;
17454
+ }
17455
+ function precedingBackslashes(text, index) {
17456
+ let count = 0;
17457
+ while (index - count - 1 >= 0 && text[index - count - 1] === "\\")
17458
+ count += 1;
17459
+ return count;
17460
+ }
17461
+ function parseLineCitations(text) {
17462
+ const spans = inlineCodeSpans(text);
17463
+ const parsed = spans.map((span) => parseCitationText(span.content));
17464
+ const consumed = new Set;
17465
+ for (const [index, citation] of parsed.entries()) {
17466
+ if (citation?.form !== "path" || citation.line === undefined || citation.symbol !== undefined)
17467
+ continue;
17468
+ const bind = (other, gap) => {
17469
+ const candidate = parsed[other];
17470
+ if (consumed.has(other) || !candidate || candidate.form === "path" || !BINDING_GAP.test(gap))
17471
+ return false;
17472
+ citation.symbol = candidate.symbol;
17473
+ consumed.add(other);
17474
+ return true;
17475
+ };
17476
+ if (index + 1 < spans.length && bind(index + 1, text.slice(spans[index].end, spans[index + 1].start)))
17477
+ continue;
17478
+ if (index > 0)
17479
+ bind(index - 1, text.slice(spans[index - 1].end, spans[index].start));
17480
+ }
17481
+ return parsed.flatMap((citation, index) => citation && !consumed.has(index) ? [citation] : []);
17482
+ }
17483
+ function parseCitationText(raw) {
17484
+ const text = raw.trim();
17485
+ if (text === "" || /\s/.test(text))
17486
+ return;
17487
+ const call = CALL_PATTERN.exec(text);
17488
+ if (call)
17489
+ return { form: "call", text, symbol: call[1] };
17490
+ if (IDENTIFIER_PATTERN.test(text))
17491
+ return { form: "identifier", text, symbol: text };
17492
+ let rest = text;
17493
+ let line;
17494
+ let endLine;
17495
+ let symbol;
17496
+ let explicit = false;
17497
+ const hash = rest.indexOf("#");
17498
+ if (hash >= 0) {
17499
+ const fragment = rest.slice(hash + 1);
17500
+ rest = rest.slice(0, hash);
17501
+ const lineFragment = LINE_FRAGMENT.exec(fragment);
17502
+ if (lineFragment) {
17503
+ line = Number(lineFragment[1]);
17504
+ endLine = lineFragment[2] === undefined ? undefined : Number(lineFragment[2]);
17505
+ } else if (QUALIFIED_PATTERN.test(fragment)) {
17506
+ symbol = fragment;
17507
+ } else {
17508
+ return;
17509
+ }
17510
+ explicit = true;
17511
+ }
17512
+ const lineSuffix = LINE_SUFFIX.exec(rest);
17513
+ if (lineSuffix) {
17514
+ if (line !== undefined)
17515
+ return;
17516
+ rest = lineSuffix[1];
17517
+ line = Number(lineSuffix[2]);
17518
+ endLine = lineSuffix[3] === undefined ? undefined : Number(lineSuffix[3]);
17519
+ explicit = true;
17520
+ }
17521
+ if (line !== undefined && (line < 1 || endLine !== undefined && endLine < line))
17522
+ return;
17523
+ const written = rest.replace(/^\.\//, "");
17524
+ if (!PATH_PATTERN.test(written) || written.startsWith("/"))
17525
+ return;
17526
+ const directory = written.endsWith("/");
17527
+ const path = directory ? written.slice(0, -1) : written;
17528
+ const segments = path.split("/");
17529
+ if (path === "" || segments.some((segment) => segment === "" || /^\.+$/.test(segment)))
17530
+ return;
17531
+ if (!explicit && segments.length < 2)
17532
+ return;
17533
+ if (explicit && segments.length < 2 && !FILE_EXTENSION.test(path))
17534
+ return;
17535
+ return {
17536
+ form: "path",
17537
+ text,
17538
+ path,
17539
+ directory,
17540
+ explicit,
17541
+ ...line === undefined ? {} : { line },
17542
+ ...endLine === undefined ? {} : { endLine },
17543
+ ...symbol === undefined ? {} : { symbol }
17544
+ };
17545
+ }
17546
+ function checkNode(node, input) {
17547
+ const findings = [];
17548
+ const unchecked = [];
17549
+ let citationCount = 0;
17550
+ for (const { origin, parsed } of extractNodeProseCitations(node)) {
17551
+ const outcome = parsed.form === "path" ? checkPathCitation(parsed, node, input) : checkSymbolCitation(parsed, input.codeGraph);
17552
+ if (!outcome.counted)
17553
+ continue;
17554
+ citationCount += 1;
17555
+ for (const finding of outcome.findings)
17556
+ findings.push({ ...finding, origin, citation: outcome.citation });
17557
+ for (const reasonCode of outcome.unchecked)
17558
+ unchecked.push({ reasonCode, origin, citation: outcome.citation });
17559
+ }
17560
+ return {
17561
+ nodeId: node.nodeId,
17562
+ ...node.document ? { documentPath: node.document.path } : {},
17563
+ citationCount,
17564
+ findings,
17565
+ unchecked
17566
+ };
17567
+ }
17568
+ function checkSymbolCitation(parsed, codeGraph) {
17569
+ const citation = { text: parsed.text, symbol: parsed.symbol };
17570
+ if (codeGraph.availability !== "ready") {
17571
+ return parsed.form === "call" ? { counted: true, citation, findings: [], unchecked: ["codegraph-unavailable"] } : { counted: false, citation, findings: [], unchecked: [] };
17572
+ }
17573
+ const { name, container } = splitSymbol(parsed.symbol);
17574
+ const declared = matchingDeclarations(codeGraph.symbols.declarationsNamed(name), container).length > 0;
17575
+ if (parsed.form === "identifier")
17576
+ return { counted: declared, citation, findings: [], unchecked: [] };
17577
+ const missing = !codeGraph.symbols.appearsInSource(name);
17578
+ return { counted: true, citation, findings: missing ? [{ kind: "missing-symbol" }] : [], unchecked: [] };
17579
+ }
17580
+ function checkPathCitation(parsed, node, input) {
17581
+ const citation = {
17582
+ text: parsed.text,
17583
+ path: parsed.path,
17584
+ ...parsed.line === undefined ? {} : { line: parsed.line },
17585
+ ...parsed.endLine === undefined ? {} : { endLine: parsed.endLine },
17586
+ ...parsed.symbol === undefined ? {} : { symbol: parsed.symbol }
17587
+ };
17588
+ const resolution = resolveCitedPath(parsed, node, input.tree);
17589
+ if (resolution.status === "ignored")
17590
+ return { counted: false, citation, findings: [], unchecked: [] };
17591
+ if (resolution.status === "ambiguous")
17592
+ return { counted: true, citation, findings: [], unchecked: ["path-ambiguous"] };
17593
+ const needsFile = parsed.line !== undefined || parsed.symbol !== undefined;
17594
+ if (resolution.status === "missing" || needsFile && resolution.kind !== "file") {
17595
+ return { counted: true, citation, findings: [{ kind: "missing-path" }], unchecked: [] };
17596
+ }
17597
+ const resolved = resolution;
17598
+ if (resolved.path !== parsed.path)
17599
+ citation.resolvedPath = resolved.path;
17600
+ if (!needsFile)
17601
+ return { counted: true, citation, findings: [], unchecked: [] };
17602
+ const lineFinding = () => {
17603
+ if (parsed.line === undefined)
17604
+ return [];
17605
+ const lineCount = input.tree.lineCount(resolved.path);
17606
+ return (parsed.endLine ?? parsed.line) > lineCount ? [{ kind: "line-out-of-range", lineCount }] : [];
17607
+ };
17608
+ if (parsed.symbol === undefined)
17609
+ return { counted: true, citation, findings: lineFinding(), unchecked: [] };
17610
+ if (input.codeGraph.availability !== "ready") {
17611
+ return { counted: true, citation, findings: lineFinding(), unchecked: ["codegraph-unavailable"] };
17612
+ }
17613
+ const symbols = input.codeGraph.symbols;
17614
+ if (!symbols.supportsPath(resolved.path)) {
17615
+ return { counted: true, citation, findings: lineFinding(), unchecked: ["language-unsupported"] };
17616
+ }
17617
+ const { name, container } = splitSymbol(parsed.symbol);
17618
+ const declarations = matchingDeclarations(symbols.declarationsInFile(resolved.path, name), container);
17619
+ if (declarations.length === 0)
17620
+ return { counted: true, citation, findings: [{ kind: "missing-symbol" }], unchecked: [] };
17621
+ const line = parsed.line;
17622
+ if (line === undefined || declarations.some((declaration) => declaration.startLine <= line && line <= declaration.endLine)) {
17623
+ return { counted: true, citation, findings: [], unchecked: [] };
17624
+ }
17625
+ const currentLines = [...new Set(declarations.map((declaration) => declaration.startLine))].sort((left, right) => left - right);
17626
+ return { counted: true, citation, findings: [{ kind: "line-moved", currentLines }], unchecked: [] };
17627
+ }
17628
+ function resolveCitedPath(parsed, node, tree) {
17629
+ const direct = tree.kind(parsed.path);
17630
+ if (direct && !(parsed.directory && direct === "file"))
17631
+ return { status: "resolved", path: parsed.path, kind: direct };
17632
+ const missing = () => tree.isIgnored(parsed.path) ? { status: "ignored" } : { status: "missing" };
17633
+ const anchored = parsed.path.includes("/") && tree.hasTopLevelEntry(parsed.path.split("/")[0]);
17634
+ if (anchored)
17635
+ return missing();
17636
+ if (!parsed.explicit)
17637
+ return { status: "ignored" };
17638
+ let matches = tree.filesEndingWith(parsed.path);
17639
+ if (matches.length > 1 && node.footprint) {
17640
+ const footprint = new Set(node.footprint);
17641
+ const inFootprint = matches.filter((path) => footprint.has(path));
17642
+ if (inFootprint.length === 1)
17643
+ matches = inFootprint;
17644
+ }
17645
+ if (matches.length === 0)
17646
+ return missing();
17647
+ if (matches.length > 1)
17648
+ return { status: "ambiguous" };
17649
+ return { status: "resolved", path: matches[0], kind: "file" };
17650
+ }
17651
+ function splitSymbol(symbol) {
17652
+ const segments = symbol.split(".");
17653
+ return segments.length > 1 ? { name: segments[segments.length - 1], container: segments[segments.length - 2] } : { name: symbol };
17654
+ }
17655
+ function matchingDeclarations(declarations, container) {
17656
+ if (container === undefined)
17657
+ return declarations;
17658
+ return declarations.filter((declaration) => declaration.qualifiedName.split(/::|\.|#|\//).includes(container));
17659
+ }
17145
17660
 
17146
17661
  // packages/core/projection-engine/src/index.ts
17147
17662
  function nativeNodeSource(node) {
@@ -17164,15 +17679,10 @@ function renderArchitectureDocumentationProjection(input) {
17164
17679
  const generatedAt = input.generatedAt ?? "1970-01-01T00:00:00.000Z";
17165
17680
  const scaleSignalsByNodeId = new Map((input.sourceScaleSignals ?? []).map((signal) => [signal.nodeId, signal]));
17166
17681
  const scaleByNodeId = new Map([...scaleSignalsByNodeId].map(([nodeId, signal]) => [nodeId, architectureCapabilityScale(signal)]));
17682
+ const semanticNodes = semanticArchitectureNodes(model);
17167
17683
  const semanticCompilationsByNodeId = new Map(model.nodes.map((node) => [node.id, compileSemanticCapabilityDiagrams({
17168
17684
  capabilityId: node.id,
17169
- nodes: model.nodes.map((entry) => ({
17170
- id: entry.id,
17171
- kind: entry.kind,
17172
- name: entry.name,
17173
- ...entry.parent ? { parent: entry.parent } : {},
17174
- ...nativeNodeSource(entry) ? { source: nativeNodeSource(entry) } : {}
17175
- })),
17685
+ nodes: semanticNodes,
17176
17686
  relations: model.relations,
17177
17687
  flows: model.flows ?? [],
17178
17688
  evidence: input.selectorEvidence
@@ -17187,7 +17697,8 @@ function renderArchitectureDocumentationProjection(input) {
17187
17697
  resulting: semanticState,
17188
17698
  acceptedChange: input.refreshContext?.acceptedChange
17189
17699
  });
17190
- const targetDrafts = architectureDocumentationTargetDrafts(model, layout2);
17700
+ const docsLanguage = input.docsLanguage === undefined ? undefined : assertArchitectureDocsLanguage(input.docsLanguage);
17701
+ const targetDrafts = architectureDocumentationTargetDrafts(model, layout2, docsLanguage);
17191
17702
  const stampedNodeIds = new Set(architectureProjectionStampedNodeIds(model, layout2.profile));
17192
17703
  const footprintDigestByNodeId = new Map(input.sourceFootprints.map((entry) => [entry.nodeId, entry.digest]));
17193
17704
  const rendered = targetDrafts.map((draft) => {
@@ -17426,7 +17937,8 @@ function loadArchitectureDocumentationInputs(root, profile = "default") {
17426
17937
  return {
17427
17938
  model,
17428
17939
  decisions: loadArchitectureDecisionRecords(root),
17429
- existingFiles: loadArchitectureFilesForLayout(root, layout2)
17940
+ existingFiles: loadArchitectureFilesForLayout(root, layout2),
17941
+ docsLanguage: loadArchitectureDocumentationLanguage(root)
17430
17942
  };
17431
17943
  }
17432
17944
  function loadCapabilitySourceScaleSignals(root, model, sourceFiles) {
@@ -17514,6 +18026,28 @@ function loadArchitectureDocumentationProfile(root) {
17514
18026
  }
17515
18027
  return manifest.profile;
17516
18028
  }
18029
+ var ARCHITECTURE_DOCS_LANGUAGES = ["zh-Hant", "zh-Hans", "en"];
18030
+ var ARCHCONTEXT_MANIFEST_PATH = ".archcontext/manifest.yaml";
18031
+ function loadArchitectureDocumentationLanguage(root) {
18032
+ const path = resolve7(root, ARCHCONTEXT_MANIFEST_PATH);
18033
+ if (!existsSync5(path))
18034
+ return;
18035
+ let manifest;
18036
+ try {
18037
+ manifest = parseJsonOrStableYaml(readFileSync6(path, "utf8"), ARCHCONTEXT_MANIFEST_PATH);
18038
+ } catch {
18039
+ return;
18040
+ }
18041
+ const docs = manifest && typeof manifest === "object" && !Array.isArray(manifest) ? manifest.docs : undefined;
18042
+ const language = docs && typeof docs === "object" && !Array.isArray(docs) ? docs.language : undefined;
18043
+ return language === undefined ? undefined : assertArchitectureDocsLanguage(language);
18044
+ }
18045
+ function assertArchitectureDocsLanguage(value) {
18046
+ if (!ARCHITECTURE_DOCS_LANGUAGES.includes(value)) {
18047
+ throw new Error(`architecture-docs-language-invalid: docs.language must be one of ${ARCHITECTURE_DOCS_LANGUAGES.join(", ")} (got ${JSON.stringify(value)})`);
18048
+ }
18049
+ return value;
18050
+ }
17517
18051
  function loadArchitectureDecisionRecords(root) {
17518
18052
  const dir = resolve7(root, "docs/adr");
17519
18053
  if (!existsSync5(dir))
@@ -17663,6 +18197,28 @@ function loadArchitectureProjectionManifestStamps(root) {
17663
18197
  ...isV3Provenance ? { provenance } : {}
17664
18198
  };
17665
18199
  }
18200
+ function semanticArchitectureNodes(model) {
18201
+ return model.nodes.map((entry) => ({
18202
+ id: entry.id,
18203
+ kind: entry.kind,
18204
+ name: entry.name,
18205
+ ...entry.parent ? { parent: entry.parent } : {},
18206
+ ...nativeNodeSource(entry) ? { source: nativeNodeSource(entry) } : {}
18207
+ }));
18208
+ }
18209
+ function checkArchitectureFlowSelectors(input) {
18210
+ const model = normalizeNativeModel(input.model);
18211
+ return {
18212
+ schemaVersion: ARCHITECTURE_FLOW_SELECTOR_CHECK_SCHEMA_VERSION,
18213
+ codeGraph: input.codeGraph,
18214
+ ...checkSemanticFlowSelectors({
18215
+ nodes: semanticArchitectureNodes(model),
18216
+ relations: model.relations,
18217
+ flows: model.flows ?? [],
18218
+ evidence: input.selectorEvidence
18219
+ })
18220
+ };
18221
+ }
17666
18222
  function normalizeNativeModel(model) {
17667
18223
  return {
17668
18224
  nodes: [...model.nodes].sort((a, b) => a.id.localeCompare(b.id)),
@@ -17903,8 +18459,8 @@ function readYamlObjects(dir) {
17903
18459
  function escapeMermaid(value) {
17904
18460
  return value.replace(/"/g, "'");
17905
18461
  }
17906
- function architectureDocumentationTargetDrafts(model, layout2) {
17907
- return layout2.targets.map((target) => target.type === "entity-summary" ? { ...target, skeleton: entitySummarySkeleton(model.nodes.find((node) => node.id === target.scope.id)) } : target);
18462
+ function architectureDocumentationTargetDrafts(model, layout2, docsLanguage) {
18463
+ return layout2.targets.map((target) => target.type === "entity-summary" ? { ...target, skeleton: entitySummarySkeleton(model.nodes.find((node) => node.id === target.scope.id), docsLanguage) } : target);
17908
18464
  }
17909
18465
  function projectionTarget(input) {
17910
18466
  return {
@@ -17932,6 +18488,7 @@ function renderTargetGeneratedBody(target, model, input) {
17932
18488
  return renderEntitySummary(node, model, {
17933
18489
  scaleSignal: input.scaleSignalsByNodeId.get(node.id),
17934
18490
  scale: input.scaleByNodeId.get(node.id),
18491
+ scaleByNodeId: input.scaleByNodeId,
17935
18492
  semanticCompilation: input.semanticCompilationsByNodeId.get(node.id)
17936
18493
  });
17937
18494
  }
@@ -17992,6 +18549,7 @@ function renderEntitySummary(node, model, input) {
17992
18549
  const incoming = model.relations.filter((relation) => relation.target === node.id);
17993
18550
  const outgoing = model.relations.filter((relation) => relation.source === node.id);
17994
18551
  const localContracts = contractFilesForNode(node);
18552
+ const components = model.nodes.filter((entry) => entry.parent === node.id && entry.kind === "component");
17995
18553
  const lines = [
17996
18554
  `> **狀態**:${node.status ? `\`${node.status}\`` : "未宣告(`status` 缺失)"}`,
17997
18555
  `> **Capability ID**:\`${node.id}\`(kind \`${node.kind}\`)`,
@@ -18013,6 +18571,7 @@ function renderEntitySummary(node, model, input) {
18013
18571
  "| --- | --- | --- |",
18014
18572
  ...entrypoints.flatMap((entry) => entry.symbols.map((symbol) => `| \`${entry.id}\` | \`${entry.path}#${symbol.name}\` | ${symbol.sinks.map((sink) => `\`${sink.id}\` → \`${sink.path}#${sink.symbol}\``).join("、")} |`))
18015
18573
  ] : ["- 未宣告 `source.entrypoints`,入口清單無法從架構模型推導。"],
18574
+ ...renderComponentEntrypoints(components),
18016
18575
  "",
18017
18576
  "### 1.3 規模信號",
18018
18577
  "",
@@ -18032,6 +18591,8 @@ function renderEntitySummary(node, model, input) {
18032
18591
  "入向關係:",
18033
18592
  "",
18034
18593
  ...incoming.length === 0 ? ["- 無。"] : incoming.map((relation) => `- \`${relation.kind}\` ← \`${relation.source}\` — ${relation.intent}`),
18594
+ ...renderComponentOutboundRelations(components, model.relations),
18595
+ ...renderComponentTable(components, input.scaleByNodeId),
18035
18596
  "",
18036
18597
  "## 2. P2:端到端數據流",
18037
18598
  "",
@@ -18041,6 +18602,56 @@ function renderEntitySummary(node, model, input) {
18041
18602
  `)}
18042
18603
  `;
18043
18604
  }
18605
+ function renderComponentEntrypoints(components) {
18606
+ if (components.length === 0)
18607
+ return [];
18608
+ const rows = components.flatMap((component) => (nativeNodeSource(component)?.entrypoints ?? []).flatMap((entry) => entry.symbols.map((symbol) => `| \`${component.id}\` | \`${entry.id}\` | \`${entry.path}#${symbol.name}\` | ${symbol.sinks.map((sink) => `\`${sink.id}\` → \`${sink.path}#${sink.symbol}\``).join("、")} |`)));
18609
+ return [
18610
+ "",
18611
+ "組件入口:",
18612
+ "",
18613
+ ...rows.length > 0 ? ["| 組件 | 宣告入口 | 錨點 | 職責 |", "| --- | --- | --- | --- |", ...rows] : ["- 組件均未宣告 `source.entrypoints`。"]
18614
+ ];
18615
+ }
18616
+ function renderComponentOutboundRelations(components, relations) {
18617
+ if (components.length === 0)
18618
+ return [];
18619
+ return [
18620
+ "",
18621
+ "組件出向關係:",
18622
+ "",
18623
+ ...components.flatMap((component) => {
18624
+ const outgoing = relations.filter((relation) => relation.source === component.id);
18625
+ return [
18626
+ `- \`${component.id}\`:`,
18627
+ ...outgoing.length === 0 ? [" - 無。"] : outgoing.map((relation) => ` - \`${relation.kind}\` → \`${relation.target}\` — ${relation.intent}`)
18628
+ ];
18629
+ })
18630
+ ];
18631
+ }
18632
+ function renderComponentTable(components, scaleByNodeId) {
18633
+ if (components.length === 0)
18634
+ return [];
18635
+ return [
18636
+ "",
18637
+ "### 1.5 組件結構",
18638
+ "",
18639
+ "| 組件 | 名稱 | 規模量級 | 宣告入口 |",
18640
+ "| --- | --- | --- | --- |",
18641
+ ...components.map((component) => {
18642
+ const source = nativeNodeSource(component);
18643
+ let scaleCell = "未宣告 `source.include`";
18644
+ if ((source?.include ?? []).length > 0) {
18645
+ const scale = scaleByNodeId.get(component.id);
18646
+ if (!scale)
18647
+ throw new Error(`architecture-docs-projection-scale-signal-missing: ${component.id}`);
18648
+ scaleCell = `\`${scaleMagnitudeBucketLabel(scale.fileCountBucket)}\` 個文件 / \`${scaleMagnitudeBucketLabel(scale.lineCountBucket)}\` 行`;
18649
+ }
18650
+ const entrypointIds = (source?.entrypoints ?? []).map((entry) => `\`${entry.id}\``);
18651
+ return `| \`${component.id}\` | ${component.name.replaceAll("|", "\\|")} | ${scaleCell} | ${entrypointIds.length > 0 ? entrypointIds.join("、") : "未宣告"} |`;
18652
+ })
18653
+ ];
18654
+ }
18044
18655
  function renderSemanticP1Section(compilation) {
18045
18656
  if (compilation.p1.status === "proven") {
18046
18657
  return [
@@ -18081,16 +18692,23 @@ function entitySummaryTitle(node) {
18081
18692
  const segments = node.id.replace(/^capability\./, "").split(".").filter((segment) => segment !== "");
18082
18693
  return segments.length > 0 ? segments.join("/") : node.id;
18083
18694
  }
18084
- function entitySummarySkeleton(node) {
18695
+ var ENTITY_SUMMARY_SKELETON_TEXT = {
18696
+ legacy: { titleSuffix: " 架構文檔", p3: "## 3. P3:設計決策與不變量", history: "## 4. 歷史決策記錄(append-only)" },
18697
+ "zh-Hant": { titleSuffix: " 架構文檔", p3: "## 3. P3:設計決策與不變量", history: "## 4. 歷史決策記錄(append-only)" },
18698
+ "zh-Hans": { titleSuffix: " 架构文档", p3: "## 3. P3:设计决策与不变量", history: "## 4. 历史决策记录(append-only)" },
18699
+ en: { titleSuffix: " Architecture", p3: "## 3. P3: Design Decisions and Invariants", history: "## 4. Decision History (append-only)" }
18700
+ };
18701
+ function entitySummarySkeleton(node, docsLanguage) {
18702
+ const text = ENTITY_SUMMARY_SKELETON_TEXT[docsLanguage ?? "legacy"];
18085
18703
  return {
18086
- prefix: `# ${entitySummaryTitle(node)} 架構文檔
18704
+ prefix: `# ${entitySummaryTitle(node)}${text.titleSuffix}
18087
18705
 
18088
18706
  `,
18089
18707
  suffix: [
18090
18708
  "",
18091
- "## 3. P3:設計決策與不變量",
18709
+ text.p3,
18092
18710
  "",
18093
- "## 4. 歷史決策記錄(append-only)",
18711
+ text.history,
18094
18712
  "",
18095
18713
  "## Optimization Backlog",
18096
18714
  ""
@@ -18098,6 +18716,9 @@ function entitySummarySkeleton(node) {
18098
18716
  `)
18099
18717
  };
18100
18718
  }
18719
+ function entitySummarySkeletonVariants(node) {
18720
+ return [undefined, ...ARCHITECTURE_DOCS_LANGUAGES].map((language) => entitySummarySkeleton(node, language));
18721
+ }
18101
18722
  function renderRelationSummary(relation, model) {
18102
18723
  const source = model.nodes.find((node) => node.id === relation.source);
18103
18724
  const target = model.nodes.find((node) => node.id === relation.target);
@@ -18263,7 +18884,7 @@ function architectureDocumentationProjectionDrift(input) {
18263
18884
  path: existing.path,
18264
18885
  targetId: region.targetId,
18265
18886
  actualDigest,
18266
- disposition: orphanHoldsOnlyIntactGeneratedRegion(existing.body, region.targetId, orphanSkeleton(committedEntityTargets, existing.path, region.targetId)) ? "delete" : "human-review"
18887
+ disposition: orphanHoldsOnlyIntactGeneratedRegion(existing.body, region.targetId, orphanSkeletons(committedEntityTargets, existing.path, region.targetId)) ? "delete" : "human-review"
18267
18888
  });
18268
18889
  }
18269
18890
  }
@@ -18299,17 +18920,17 @@ function committedEntitySummaryTargets(manifestBody) {
18299
18920
  }
18300
18921
  return targets;
18301
18922
  }
18302
- function orphanSkeleton(committedEntityTargets, path, targetId) {
18923
+ function orphanSkeletons(committedEntityTargets, path, targetId) {
18303
18924
  const committed = committedEntityTargets.get(targetId);
18304
- return committed && committed.path === path ? entitySummarySkeleton({ id: committed.nodeId }) : undefined;
18925
+ return committed && committed.path === path ? entitySummarySkeletonVariants({ id: committed.nodeId }) : [];
18305
18926
  }
18306
- function orphanHoldsOnlyIntactGeneratedRegion(body, targetId, skeleton) {
18927
+ function orphanHoldsOnlyIntactGeneratedRegion(body, targetId, skeletons) {
18307
18928
  const region = findGeneratedRegion(body, targetId);
18308
18929
  if (!region)
18309
18930
  return false;
18310
18931
  const before = body.slice(0, region.start);
18311
18932
  const after = body.slice(region.end);
18312
- const generatedOnly = `${before}${after}`.trim() === "" || skeleton !== undefined && before === skeleton.prefix && after === skeleton.suffix;
18933
+ const generatedOnly = `${before}${after}`.trim() === "" || skeletons.some((skeleton) => before === skeleton.prefix && after === skeleton.suffix);
18313
18934
  if (!generatedOnly)
18314
18935
  return false;
18315
18936
  const metadata = parseGeneratedRegionMetadata(region.startMarker);
@@ -18545,26 +19166,78 @@ function findAgentContextRegion(body, nodeId) {
18545
19166
  };
18546
19167
  }
18547
19168
  function resolveArchitectureOwnerForPath(nodes, path) {
18548
- const candidates = [];
18549
- for (const node of nodes) {
19169
+ return resolveArchitectureOwnersForPaths(nodes, [path])[0];
19170
+ }
19171
+ function resolveArchitectureOwnersForPaths(nodes, paths) {
19172
+ const declaring = nodes.flatMap((node) => {
18550
19173
  const source = nativeNodeSource(node);
18551
19174
  const include = source?.include ?? [];
18552
19175
  if (include.length === 0)
18553
- continue;
18554
- const excluded = (source?.exclude ?? []).some((pattern) => matchesGlob(path, pattern));
18555
- if (excluded)
18556
- continue;
18557
- const specificity = include.filter((pattern) => matchesGlob(path, pattern)).reduce((max, pattern) => Math.max(max, globLiteralPrefixLength(pattern)), -1);
18558
- if (specificity >= 0)
18559
- candidates.push({ node, specificity });
19176
+ return [];
19177
+ return [{
19178
+ node,
19179
+ include: include.map((pattern) => ({ regex: globToRegExp(pattern), specificity: globLiteralPrefixLength(pattern) })),
19180
+ exclude: (source?.exclude ?? []).map(globToRegExp)
19181
+ }];
19182
+ });
19183
+ const parents = new Map(nodes.map((node) => [node.id, node.parent]));
19184
+ const kinds = new Map(nodes.map((node) => [node.id, node.kind]));
19185
+ const owningCapabilityOf = (id) => {
19186
+ for (const ancestor of ancestorChain(id, parents))
19187
+ if (kinds.get(ancestor) === "capability")
19188
+ return ancestor;
19189
+ return null;
19190
+ };
19191
+ return paths.map((path) => {
19192
+ const candidates = [];
19193
+ for (const entry of declaring) {
19194
+ if (entry.exclude.some((regex) => regex.test(path)))
19195
+ continue;
19196
+ const specificity = entry.include.filter((pattern) => pattern.regex.test(path)).reduce((max, pattern) => Math.max(max, pattern.specificity), -1);
19197
+ if (specificity >= 0)
19198
+ candidates.push({ node: entry.node, specificity });
19199
+ }
19200
+ if (candidates.length === 0)
19201
+ return { status: "no-match", owningCapabilityId: null };
19202
+ const chainOwner = deepestOnOneParentChain(candidates.map((candidate) => candidate.node.id), parents);
19203
+ if (chainOwner !== undefined) {
19204
+ const node = candidates.find((candidate) => candidate.node.id === chainOwner).node;
19205
+ return { status: "matched", node, owningCapabilityId: owningCapabilityOf(node.id) };
19206
+ }
19207
+ const chains = new Map(candidates.map((candidate) => [candidate.node.id, ancestorChain(candidate.node.id, parents)]));
19208
+ const isStrictAncestorOf = (ancestor, descendant) => ancestor !== descendant && chains.get(descendant).has(ancestor) && !chains.get(ancestor).has(descendant);
19209
+ const leaves = candidates.filter((candidate) => !candidates.some((other) => isStrictAncestorOf(candidate.node.id, other.node.id)));
19210
+ const contenders = leaves.length > 0 ? leaves : candidates;
19211
+ const maxSpecificity = contenders.reduce((max, candidate) => Math.max(max, candidate.specificity), -1);
19212
+ const winners = contenders.filter((candidate) => candidate.specificity === maxSpecificity).map((candidate) => candidate.node);
19213
+ if (winners.length === 1)
19214
+ return { status: "matched", node: winners[0], owningCapabilityId: owningCapabilityOf(winners[0].id) };
19215
+ const capabilities = new Set(winners.map((node) => owningCapabilityOf(node.id)));
19216
+ return { status: "ambiguous", candidates: winners, owningCapabilityId: capabilities.size === 1 ? [...capabilities][0] : null };
19217
+ });
19218
+ }
19219
+ function deepestOnOneParentChain(candidateIds, parents) {
19220
+ if (candidateIds.length === 0)
19221
+ return;
19222
+ const depthOf = (id) => ancestorChain(id, parents).size - 1;
19223
+ const deepest = candidateIds.reduce((best, id) => {
19224
+ const bestDepth = depthOf(best);
19225
+ const depth = depthOf(id);
19226
+ if (depth !== bestDepth)
19227
+ return depth > bestDepth ? id : best;
19228
+ return id < best ? id : best;
19229
+ });
19230
+ const chain = ancestorChain(deepest, parents);
19231
+ return candidateIds.every((id) => chain.has(id)) ? deepest : undefined;
19232
+ }
19233
+ function ancestorChain(id, parents) {
19234
+ const chain = new Set;
19235
+ let current = id;
19236
+ while (current !== undefined && parents.has(current) && !chain.has(current)) {
19237
+ chain.add(current);
19238
+ current = parents.get(current);
18560
19239
  }
18561
- if (candidates.length === 0)
18562
- return { status: "no-match" };
18563
- const maxSpecificity = candidates.reduce((max, candidate) => Math.max(max, candidate.specificity), -1);
18564
- const winners = candidates.filter((candidate) => candidate.specificity === maxSpecificity).map((candidate) => candidate.node);
18565
- if (winners.length > 1)
18566
- return { status: "ambiguous", candidates: winners };
18567
- return { status: "matched", node: winners[0] };
19240
+ return chain;
18568
19241
  }
18569
19242
  function matchesGlob(path, pattern) {
18570
19243
  return globToRegExp(pattern).test(path);
@@ -18752,12 +19425,23 @@ function readTrackedSourceFiles(root, options = {}) {
18752
19425
  }
18753
19426
  function readWorkspacePackages(root) {
18754
19427
  const manifest = JSON.parse(readFileSync7(join4(root, "package.json"), "utf8"));
18755
- return (manifest.workspaces ?? []).map((directory) => {
19428
+ return (manifest.workspaces ?? []).flatMap((entry) => expandWorkspaceEntry(root, entry)).map((directory) => {
18756
19429
  const path = join4(root, ...directory.split("/"), "package.json");
18757
19430
  const packageManifest = JSON.parse(readFileSync7(path, "utf8"));
18758
19431
  return { name: packageManifest.name, root: directory, exports: packageManifest.exports ?? {} };
18759
19432
  }).sort((left, right) => left.name < right.name ? -1 : left.name > right.name ? 1 : 0);
18760
19433
  }
19434
+ function expandWorkspaceEntry(root, entry) {
19435
+ if (!entry.includes("*"))
19436
+ return [entry];
19437
+ const base = entry.slice(0, -2);
19438
+ if (!entry.endsWith("/*") || base.includes("*"))
19439
+ throw new Error(`workspace-glob-unsupported: ${entry}`);
19440
+ const directory = join4(root, ...base.split("/"));
19441
+ if (!existsSync6(directory))
19442
+ return [];
19443
+ return readdirSync4(directory, { withFileTypes: true }).filter((child) => child.isDirectory() && existsSync6(join4(directory, child.name, "package.json"))).map((child) => `${base}/${child.name}`).sort();
19444
+ }
18761
19445
  function isDetachedHead(root) {
18762
19446
  return runGit(root, ["rev-parse", "--abbrev-ref", "HEAD"]).trim() === "HEAD";
18763
19447
  }
@@ -18991,7 +19675,8 @@ var CODEGRAPH_TELEMETRY_ENV = "DO_NOT_TRACK";
18991
19675
  var CODEGRAPH_TELEMETRY_DISABLED_VALUE = "1";
18992
19676
  var DEFAULT_CODEGRAPH_BINARY = "codegraph";
18993
19677
  var requireFromAdapter = createRequire2(import.meta.url);
18994
- var { CodeGraph } = requireFromAdapter(REQUIRED_CODEGRAPH_PACKAGE);
19678
+ var codeGraphSdk = requireFromAdapter(REQUIRED_CODEGRAPH_PACKAGE);
19679
+ var { CodeGraph } = codeGraphSdk;
18995
19680
  var CODEGRAPH_OUTPUT_MAX_BYTES = 32 * 1024 * 1024;
18996
19681
  var CODEGRAPH_QUERY_TIMEOUT_MS = 30000;
18997
19682
  function disableCodeGraphTelemetryByDefault(env = process.env) {
@@ -19502,7 +20187,8 @@ function architectureDeltaTask(git) {
19502
20187
  ...git.paths.flatMap((change) => [change.path, change.previousPath ?? ""])
19503
20188
  ].filter(Boolean)).join(" ");
19504
20189
  }
19505
- var CODEGRAPH_IMPORT_NODE_QUERY_LIMIT = 5000;
20190
+ var CODEGRAPH_IMPORT_NODE_READ_LIMIT = 50000;
20191
+ var CODEGRAPH_IMPORT_NODE_OUTPUT_BYTES = 4 * 1024;
19506
20192
  function prepareArchitectureDocumentationProjectionSnapshot(root, model, options = {}) {
19507
20193
  const baseHeadSha = readProjectionHeadSha(root);
19508
20194
  const sourceFiles = listProjectionSourceFiles(root);
@@ -19567,7 +20253,7 @@ function assertProjectionCodeFactsAvailable(root, prepared) {
19567
20253
  function prepareProjectionCodeFacts(root, model, options) {
19568
20254
  const invocation = options.binary ? codeGraphCliInvocation(options.binary, root, "") : packagedCodeGraphCliInvocation();
19569
20255
  const binaryPath = invocation.argsPrefix[0] ?? invocation.command;
19570
- const actualVersion = runProjectionCodeGraph(invocation, root, ["--version"], options.timeouts?.versionMs ?? 5000).trim();
20256
+ const actualVersion = runProjectionCodeGraph(invocation, root, ["--version"], options.timeouts?.versionMs ?? 1e4).trim();
19571
20257
  if (actualVersion !== REQUIRED_CODEGRAPH_VERSION) {
19572
20258
  throw new Error(`CodeGraph ${REQUIRED_CODEGRAPH_VERSION} required, got actual binary ${actualVersion || "unknown"}`);
19573
20259
  }
@@ -19608,7 +20294,9 @@ function prepareProjectionCodeFacts(root, model, options) {
19608
20294
  const inputs = loadCapabilityCodeGraphProjectionInputs(root, model, {
19609
20295
  ...options.sourceFiles ? { sourceFiles: options.sourceFiles } : {},
19610
20296
  binary: binaryPath,
19611
- importNodeLimit: options.importNodeLimit
20297
+ importNodeLimit: options.importNodeLimit,
20298
+ importNodeCount: statusImportNodeCount(postStatus),
20299
+ ...options.importGraphs === false ? { importGraphs: false } : {}
19612
20300
  });
19613
20301
  const finalStatus = readProjectionCodeGraphStatus(invocation, root, statusTimeout);
19614
20302
  assertProjectionCodeGraphStatus(finalStatus, root, "post-query");
@@ -19743,8 +20431,36 @@ function digestFile(path) {
19743
20431
  function codeGraphIndexAvailable(workspaceRoot) {
19744
20432
  return existsSync7(join5(workspaceRoot, ".codegraph"));
19745
20433
  }
20434
+ var NON_DECLARATION_KINDS = new Set(["file", "import", "export", "parameter"]);
20435
+ function openCodeGraphSymbolReader(workspaceRoot, openIndex = (root, options) => CodeGraph.openSync(root, options)) {
20436
+ if (!codeGraphIndexAvailable(workspaceRoot))
20437
+ return { availability: "unavailable", reasonCode: "index-missing" };
20438
+ disableCodeGraphTelemetryByDefault();
20439
+ let graph;
20440
+ try {
20441
+ graph = openIndex(workspaceRoot, { readOnly: true });
20442
+ } catch {
20443
+ return { availability: "unavailable", reasonCode: "index-open-failed" };
20444
+ }
20445
+ const declarations = (nodes) => nodes.filter((node) => !NON_DECLARATION_KINDS.has(node.kind)).map((node) => ({ name: node.name, qualifiedName: node.qualifiedName, startLine: node.startLine, endLine: node.endLine })).sort((left, right) => left.startLine - right.startLine || left.qualifiedName.localeCompare(right.qualifiedName));
20446
+ return {
20447
+ availability: "ready",
20448
+ reader: {
20449
+ supportsPath: (path) => codeGraphSdk.isLanguageSupported(codeGraphSdk.detectLanguage(path)),
20450
+ declarationsInSource: (path, content) => {
20451
+ try {
20452
+ return declarations(graph.extractFromSource(path, content).nodes);
20453
+ } catch {
20454
+ return;
20455
+ }
20456
+ },
20457
+ declarationsNamed: (name) => declarations(graph.getNodesByName(name).filter((node) => node.name === name)),
20458
+ close: () => graph.close()
20459
+ }
20460
+ };
20461
+ }
19746
20462
  function loadCapabilityCodeGraphProjectionInputs(root, model, options = {}) {
19747
- const declaresFootprint = model.nodes.some((node) => (nativeNodeSource(node)?.include ?? []).length > 0);
20463
+ const declaresFootprint = options.importGraphs !== false && model.nodes.some((node) => (nativeNodeSource(node)?.include ?? []).length > 0);
19748
20464
  const footprints = declaresFootprint ? loadCapabilitySourceFootprints(root, model, options.sourceFiles ?? listProjectionSourceFiles(root)) : [];
19749
20465
  const entrypointNodes = model.nodes.map((node) => ({ nodeId: node.id, entrypoints: nativeNodeSource(node)?.entrypoints ?? [] })).filter((entry) => entry.entrypoints.length > 0).sort((left, right) => left.nodeId.localeCompare(right.nodeId));
19750
20466
  if (footprints.length === 0 && entrypointNodes.length === 0)
@@ -19753,11 +20469,11 @@ function loadCapabilityCodeGraphProjectionInputs(root, model, options = {}) {
19753
20469
  return { importGraphs: [], selectorEvidence: [] };
19754
20470
  disableCodeGraphTelemetryByDefault();
19755
20471
  const binary = options.binary ?? DEFAULT_CODEGRAPH_BINARY;
19756
- const importGraphs = footprints.length > 0 ? capabilityImportGraphs(root, binary, footprints, options.importNodeLimit ?? CODEGRAPH_IMPORT_NODE_QUERY_LIMIT) : [];
20472
+ const importGraphs = footprints.length > 0 ? capabilityImportGraphs(root, binary, footprints, options.importNodeLimit ?? CODEGRAPH_IMPORT_NODE_READ_LIMIT, options.importNodeCount ?? null) : [];
19757
20473
  const selectorIndex = entrypointNodes.length > 0 ? options.selectorIndexFactory?.(root) ?? CodeGraph.openSync(root) : undefined;
19758
20474
  let selectorEvidence;
19759
20475
  try {
19760
- selectorEvidence = entrypointNodes.flatMap((entry) => entry.entrypoints.flatMap((entrypoint) => entrypoint.symbols.flatMap((source) => source.sinks.map((sink) => exactSelectorEvidence(selectorIndex, {
20476
+ selectorEvidence = entrypointNodes.flatMap((entry) => entry.entrypoints.flatMap((entrypoint) => entrypoint.symbols.flatMap((source) => source.sinks.map((sink) => exactSelectorEvidence(root, selectorIndex, {
19761
20477
  nodeId: entry.nodeId,
19762
20478
  entrypointId: entrypoint.id,
19763
20479
  sourcePath: entrypoint.path,
@@ -19772,10 +20488,21 @@ function loadCapabilityCodeGraphProjectionInputs(root, model, options = {}) {
19772
20488
  return { importGraphs, selectorEvidence };
19773
20489
  }
19774
20490
  function repositoryImportPairs(root, binary, limit, worktreeDigest) {
20491
+ if (!Number.isInteger(limit) || limit < 1)
20492
+ throw new Error(`import node limit must be a positive integer, got ${limit}`);
19775
20493
  if (!codeGraphIndexAvailable(root)) {
19776
- return { pairs: [], truncated: true, availability: "unavailable", indexedWorktreeDigest: null };
20494
+ return {
20495
+ pairs: [],
20496
+ truncated: true,
20497
+ availability: "unavailable",
20498
+ indexedWorktreeDigest: null,
20499
+ importNodeCount: null,
20500
+ importNodeLimit: limit,
20501
+ importNodeLimitExceeded: false
20502
+ };
19777
20503
  }
19778
- const nodes = codeGraphImportNodes(root, binary, limit);
20504
+ const status = readProjectionCodeGraphStatus(codeGraphCliInvocation(binary, root, ""), root, CODEGRAPH_QUERY_TIMEOUT_MS);
20505
+ const nodes = codeGraphImportPopulation(root, binary, limit, statusImportNodeCount(status));
19779
20506
  const pairs = new Map;
19780
20507
  for (const node of nodes.imports) {
19781
20508
  if (node.kind !== "import")
@@ -19787,18 +20514,43 @@ function repositoryImportPairs(root, binary, limit, worktreeDigest) {
19787
20514
  const firstLine = line === undefined ? previousLine : previousLine === undefined ? line : Math.min(line, previousLine);
19788
20515
  pairs.set(key, { from: node.filePath, specifier: node.name, to, ...firstLine === undefined ? {} : { line: firstLine } });
19789
20516
  }
19790
- const status = readProjectionCodeGraphStatus(codeGraphCliInvocation(binary, root, ""), root, CODEGRAPH_QUERY_TIMEOUT_MS);
19791
20517
  const pending = status.pendingChanges;
19792
20518
  const clean = status.initialized && pending !== undefined && pending.added === 0 && pending.modified === 0 && pending.removed === 0;
19793
20519
  return {
19794
20520
  pairs: [...pairs.keys()].sort().map((key) => pairs.get(key)),
19795
20521
  truncated: nodes.truncated,
19796
20522
  availability: clean ? "ready" : "unavailable",
19797
- indexedWorktreeDigest: clean ? worktreeDigest : null
20523
+ indexedWorktreeDigest: clean ? worktreeDigest : null,
20524
+ importNodeCount: nodes.count,
20525
+ importNodeLimit: limit,
20526
+ importNodeLimitExceeded: nodes.limitExceeded
19798
20527
  };
19799
20528
  }
19800
- function capabilityImportGraphs(root, binary, footprints, limit) {
19801
- const nodes = codeGraphImportNodes(root, binary, limit);
20529
+ function statusImportNodeCount(status) {
20530
+ if (!status.nodesByKind || typeof status.nodesByKind !== "object")
20531
+ return null;
20532
+ const count = status.nodesByKind.import;
20533
+ return Number.isInteger(count) && count >= 0 ? count : null;
20534
+ }
20535
+ function codeGraphImportPopulation(root, binary, limit, reportedCount) {
20536
+ const queryLimit = Math.min(reportedCount ?? limit, limit) + 1;
20537
+ const output = runCodeGraphCli(binary, root, ["query", "-p", root, "-j", "-l", String(queryLimit), "-k", "import", "import"], Math.max(CODEGRAPH_OUTPUT_MAX_BYTES, queryLimit * CODEGRAPH_IMPORT_NODE_OUTPUT_BYTES));
20538
+ const imports = JSON.parse(output).map(({ node }) => node);
20539
+ const limitExceeded = (reportedCount ?? imports.length) > limit;
20540
+ if (limitExceeded) {
20541
+ return { imports: imports.slice(0, limit), count: reportedCount, truncated: imports.length < limit, limitExceeded };
20542
+ }
20543
+ return {
20544
+ imports,
20545
+ count: reportedCount ?? imports.length,
20546
+ truncated: reportedCount !== null && imports.length !== reportedCount,
20547
+ limitExceeded
20548
+ };
20549
+ }
20550
+ function capabilityImportGraphs(root, binary, footprints, limit, reportedCount) {
20551
+ if (!Number.isInteger(limit) || limit < 1)
20552
+ throw new Error(`import node limit must be a positive integer, got ${limit}`);
20553
+ const nodes = codeGraphImportPopulation(root, binary, limit, reportedCount);
19802
20554
  const pairs = resolvedImportPairs(root, nodes.imports);
19803
20555
  return footprints.map((footprint) => {
19804
20556
  const scope = new Set(footprint.files);
@@ -19812,39 +20564,147 @@ function capabilityImportGraphs(root, binary, footprints, limit) {
19812
20564
  nodeId: footprint.nodeId,
19813
20565
  files: footprint.files,
19814
20566
  edges: [...edges.values()].sort((left, right) => left.from.localeCompare(right.from) || left.to.localeCompare(right.to)),
19815
- truncated: nodes.truncated
20567
+ truncated: nodes.truncated,
20568
+ ...nodes.limitExceeded ? { importNodeLimitExceeded: true } : {}
19816
20569
  };
19817
20570
  });
19818
20571
  }
19819
- function codeGraphImportNodes(root, binary, limit) {
19820
- const output = runCodeGraphCli(binary, root, ["query", "-p", root, "-j", "-l", String(limit), "-k", "import", "import"]);
19821
- const parsed = JSON.parse(output);
19822
- return { imports: parsed.map(({ node }) => node), truncated: parsed.length >= limit };
19823
- }
19824
- function exactSelectorEvidence(index, selector) {
19825
- const sources = index.getNodesByName(selector.sourceSymbol).filter((node) => normalizedIndexPath(node.filePath) === normalizedIndexPath(selector.sourcePath));
19826
- const exactCalls = sources.flatMap((source) => index.getOutgoingEdges(source.id).filter((edge) => edge.kind === "calls").flatMap((edge) => {
20572
+ var RUNTIME_BUILTIN_CALL_NAMES = new Set([
20573
+ "basename",
20574
+ "dirname",
20575
+ "extname",
20576
+ "format",
20577
+ "isAbsolute",
20578
+ "join",
20579
+ "normalize",
20580
+ "parse",
20581
+ "relative",
20582
+ "resolve",
20583
+ "toNamespacedPath",
20584
+ "access",
20585
+ "accessSync",
20586
+ "appendFile",
20587
+ "appendFileSync",
20588
+ "chmod",
20589
+ "chmodSync",
20590
+ "close",
20591
+ "closeSync",
20592
+ "copyFile",
20593
+ "copyFileSync",
20594
+ "cp",
20595
+ "cpSync",
20596
+ "createReadStream",
20597
+ "createWriteStream",
20598
+ "exists",
20599
+ "existsSync",
20600
+ "fsync",
20601
+ "fsyncSync",
20602
+ "lstat",
20603
+ "lstatSync",
20604
+ "mkdir",
20605
+ "mkdirSync",
20606
+ "mkdtemp",
20607
+ "mkdtempSync",
20608
+ "open",
20609
+ "openSync",
20610
+ "opendir",
20611
+ "opendirSync",
20612
+ "read",
20613
+ "readFile",
20614
+ "readFileSync",
20615
+ "readSync",
20616
+ "readdir",
20617
+ "readdirSync",
20618
+ "readlink",
20619
+ "readlinkSync",
20620
+ "realpath",
20621
+ "realpathSync",
20622
+ "rename",
20623
+ "renameSync",
20624
+ "rm",
20625
+ "rmSync",
20626
+ "rmdir",
20627
+ "rmdirSync",
20628
+ "stat",
20629
+ "statSync",
20630
+ "symlink",
20631
+ "symlinkSync",
20632
+ "unlink",
20633
+ "unlinkSync",
20634
+ "watch",
20635
+ "write",
20636
+ "writeFile",
20637
+ "writeFileSync",
20638
+ "writeSync",
20639
+ "exec",
20640
+ "execFile",
20641
+ "execFileSync",
20642
+ "execSync",
20643
+ "fork",
20644
+ "spawn",
20645
+ "spawnSync",
20646
+ "homedir",
20647
+ "hostname",
20648
+ "platform",
20649
+ "tmpdir",
20650
+ "fileURLToPath",
20651
+ "pathToFileURL",
20652
+ "createHash",
20653
+ "randomBytes",
20654
+ "randomUUID",
20655
+ "clearInterval",
20656
+ "clearTimeout",
20657
+ "fetch",
20658
+ "queueMicrotask",
20659
+ "setInterval",
20660
+ "setTimeout",
20661
+ "structuredClone"
20662
+ ]);
20663
+ function exactSelectorEvidence(root, index, selector) {
20664
+ const sources = indexedSymbolsAt(index, selector.sourceSymbol, selector.sourcePath);
20665
+ const sinkDeclarations = indexedSymbolsAt(index, selector.sinkSymbol, selector.sinkPath);
20666
+ const exactEdges = sources.flatMap((source) => index.getOutgoingEdges(source.id).filter((edge) => edge.kind === "calls" || edge.kind === "references").flatMap((edge) => {
19827
20667
  const target = index.getNode(edge.target);
19828
20668
  return target && target.name === selector.sinkSymbol && normalizedIndexPath(target.filePath) === normalizedIndexPath(selector.sinkPath) ? [{ edge, target }] : [];
19829
20669
  }));
20670
+ const calls = exactEdges.filter(({ edge }) => edge.kind === "calls");
20671
+ const falseBuiltinCalls = calls.length > 0 && isFalseBuiltinCall(root, index, sources[0].filePath, selector);
20672
+ const exactCalls = falseBuiltinCalls ? [] : calls;
20673
+ const references = exactCalls.length === 0 ? exactEdges.filter(({ edge }) => edge.kind === "references") : [];
19830
20674
  const sinkIdentities = new Set(exactCalls.map(({ target }) => target.id));
19831
- const ambiguous = sources.length > 1 || sinkIdentities.size > 1;
19832
- const callSites = [...new Map(exactCalls.map(({ edge }) => {
19833
- const callSite = { path: selector.sourcePath, ...edge.line === undefined ? {} : { line: edge.line } };
19834
- return [`${callSite.path}\x00${callSite.line ?? ""}`, callSite];
19835
- })).values()].sort((left, right) => left.path.localeCompare(right.path) || (left.line ?? 0) - (right.line ?? 0));
20675
+ const ambiguous = sources.length > 1 || sinkDeclarations.length > 1 || sinkIdentities.size > 1;
19836
20676
  return {
19837
20677
  ...selector,
19838
20678
  matched: !ambiguous && sinkIdentities.size === 1,
19839
20679
  ambiguous,
19840
20680
  truncated: false,
19841
- callSites
20681
+ callSites: edgeSites(selector.sourcePath, exactCalls),
20682
+ ...falseBuiltinCalls ? { rejectedBuiltinCallSites: edgeSites(selector.sourcePath, calls) } : {},
20683
+ ...references.length > 0 ? { referenceSites: edgeSites(selector.sourcePath, references) } : {}
19842
20684
  };
19843
20685
  }
20686
+ function indexedSymbolsAt(index, name, path) {
20687
+ return index.getNodesByName(name).filter((node) => normalizedIndexPath(node.filePath) === normalizedIndexPath(path));
20688
+ }
20689
+ function edgeSites(sourcePath, edges) {
20690
+ return [...new Map(edges.map(({ edge }) => {
20691
+ const site = { path: sourcePath, ...edge.line === undefined ? {} : { line: edge.line } };
20692
+ return [`${site.path}\x00${site.line ?? ""}`, site];
20693
+ })).values()].sort((left, right) => left.path.localeCompare(right.path) || (left.line ?? 0) - (right.line ?? 0));
20694
+ }
20695
+ function isFalseBuiltinCall(root, index, indexedSourcePath, selector) {
20696
+ if (!RUNTIME_BUILTIN_CALL_NAMES.has(selector.sinkSymbol))
20697
+ return false;
20698
+ const sourcePath = normalizedIndexPath(selector.sourcePath);
20699
+ const sinkPath = normalizedIndexPath(selector.sinkPath);
20700
+ if (sourcePath === sinkPath)
20701
+ return false;
20702
+ return !index.getNodesInFile(indexedSourcePath).some((node) => node.kind === "import" && [node.name, node.name.replace(/\.[cm]?js$/, "")].some((specifier) => resolveImportTarget(root, sourcePath, specifier) === sinkPath));
20703
+ }
19844
20704
  function normalizedIndexPath(path) {
19845
20705
  return path.replaceAll("\\", "/").replace(/^\.\//, "");
19846
20706
  }
19847
- function runCodeGraphCli(binary, workspaceRoot, args) {
20707
+ function runCodeGraphCli(binary, workspaceRoot, args, maxBuffer = CODEGRAPH_OUTPUT_MAX_BYTES) {
19848
20708
  disableCodeGraphTelemetryByDefault();
19849
20709
  const invocation = codeGraphCliInvocation(binary, workspaceRoot);
19850
20710
  try {
@@ -19852,7 +20712,7 @@ function runCodeGraphCli(binary, workspaceRoot, args) {
19852
20712
  cwd: workspaceRoot,
19853
20713
  encoding: "utf8",
19854
20714
  stdio: ["ignore", "pipe", "pipe"],
19855
- maxBuffer: CODEGRAPH_OUTPUT_MAX_BYTES,
20715
+ maxBuffer,
19856
20716
  timeout: CODEGRAPH_QUERY_TIMEOUT_MS
19857
20717
  });
19858
20718
  } catch (error) {
@@ -20464,14 +21324,15 @@ function localEgressStatus(env = process.env, live = {}) {
20464
21324
  }
20465
21325
 
20466
21326
  // packages/surfaces/cli/src/main.ts
20467
- import { spawn as spawn2, spawnSync as spawnSync3 } from "child_process";
20468
- import { accessSync as accessSync2, closeSync as closeSync10, constants as constants2, existsSync as existsSync27, mkdirSync as mkdirSync13, openSync as openSync10, readFileSync as readFileSync27, rmSync as rmSync16, statSync as statSync10 } from "fs";
20469
- import { dirname as dirname15, join as join18, resolve as resolve28 } from "path";
21327
+ import { spawn as spawn2, spawnSync as spawnSync4 } from "child_process";
21328
+ import { accessSync as accessSync2, closeSync as closeSync10, constants as constants2, existsSync as existsSync28, mkdirSync as mkdirSync13, openSync as openSync10, readFileSync as readFileSync28, rmSync as rmSync15, statSync as statSync10 } from "fs";
21329
+ import { dirname as dirname15, join as join18, resolve as resolve29 } from "path";
20470
21330
  import { fileURLToPath as fileURLToPath2 } from "url";
20471
21331
 
20472
21332
  // packages/contracts/src/index.ts
20473
21333
  init_control_plane_routes();
20474
21334
  init_architecture();
21335
+ init_docs_anchors();
20475
21336
  init_github_governance();
20476
21337
  init_ledger();
20477
21338
  init_ports();
@@ -20597,7 +21458,7 @@ function assertAllowedArchContextPath2(root, relativePath, scope) {
20597
21458
  // packages/core/architecture-domain/src/index.ts
20598
21459
  init_src();
20599
21460
  import { createHash as createHash6 } from "node:crypto";
20600
- import { readdirSync as readdirSync4, readFileSync as readFileSync10, realpathSync as realpathSync6, statSync as statSync4 } from "node:fs";
21461
+ import { readdirSync as readdirSync5, readFileSync as readFileSync10, realpathSync as realpathSync6, statSync as statSync4 } from "node:fs";
20601
21462
  import { relative as relative7, resolve as resolve12, sep as sep6 } from "node:path";
20602
21463
  var DEFAULT_IGNORES2 = new Set([
20603
21464
  ".git",
@@ -20658,7 +21519,7 @@ function listRepoFiles2(root, ignore = DEFAULT_IGNORES2) {
20658
21519
  walk(resolve12(root));
20659
21520
  return out.sort();
20660
21521
  function walk(dir) {
20661
- for (const entry of readdirSync4(dir, { withFileTypes: true })) {
21522
+ for (const entry of readdirSync5(dir, { withFileTypes: true })) {
20662
21523
  const absolute = resolve12(dir, entry.name);
20663
21524
  const rel = relative7(root, absolute).split(sep6).join("/");
20664
21525
  if (!rel || shouldIgnorePath2(entry.name, rel, ignore))
@@ -22792,7 +23653,7 @@ var UNTRUSTED_TOOL_ESCAPE_KEYS = new Set([
22792
23653
  ]);
22793
23654
 
22794
23655
  // packages/cloud/hardening/src/index.ts
22795
- import { readdirSync as readdirSync5, readFileSync as readFileSync11, statSync as statSync5 } from "node:fs";
23656
+ import { readdirSync as readdirSync6, readFileSync as readFileSync11, statSync as statSync5 } from "node:fs";
22796
23657
  import { resolve as resolve13 } from "node:path";
22797
23658
  init_src2();
22798
23659
  init_src();
@@ -22888,7 +23749,7 @@ function listFiles(root) {
22888
23749
  walk(".");
22889
23750
  return out;
22890
23751
  function walk(relativeDir) {
22891
- for (const entry of readdirSync5(resolve13(root, relativeDir), { withFileTypes: true })) {
23752
+ for (const entry of readdirSync6(resolve13(root, relativeDir), { withFileTypes: true })) {
22892
23753
  if ([".git", "node_modules", "coverage"].includes(entry.name))
22893
23754
  continue;
22894
23755
  const child = relativeDir === "." ? entry.name : `${relativeDir}/${entry.name}`;
@@ -22905,6 +23766,11 @@ function listFiles(root) {
22905
23766
  import { execFileSync as execFileSync4 } from "node:child_process";
22906
23767
  import { closeSync as closeSync5, constants, existsSync as existsSync10, fstatSync as fstatSync2, openSync as openSync5, readFileSync as readFileSync12, rmSync as rmSync6, writeFileSync as writeFileSync5 } from "node:fs";
22907
23768
  import { join as join6 } from "node:path";
23769
+ import { setTimeout as sleep } from "node:timers/promises";
23770
+ var WINDOWS_CREATE_TIMEOUT_MS = 60000;
23771
+ var WINDOWS_READ_TIMEOUT_MS = 1e4;
23772
+ var REMOVE_HANDLE_RELEASE_MARGIN_MS = 2000;
23773
+ var REMOVE_RETRY_DELAY_MS = 25;
22908
23774
  var WINDOWS_CONTROL_FILE_SCRIPT = String.raw`
22909
23775
  $ErrorActionPreference = 'Stop'
22910
23776
  # A pwsh -> Node/Bun -> powershell.exe launch inherits incompatible PS7 module paths.
@@ -22982,7 +23848,7 @@ function windowsControlFile(operation, path, body) {
22982
23848
  encoding: "utf8",
22983
23849
  windowsHide: true,
22984
23850
  stdio: ["pipe", "pipe", "pipe"],
22985
- timeout: operation === "create" ? 30000 : 1e4,
23851
+ timeout: operation === "create" ? WINDOWS_CREATE_TIMEOUT_MS : WINDOWS_READ_TIMEOUT_MS,
22986
23852
  maxBuffer: 1024 * 1024
22987
23853
  });
22988
23854
  } catch (error) {
@@ -23041,6 +23907,21 @@ function readPrivateControlFile(path) {
23041
23907
  return;
23042
23908
  }
23043
23909
  }
23910
+ async function removePrivateControlFile(path) {
23911
+ const deadline = Date.now() + WINDOWS_READ_TIMEOUT_MS + REMOVE_HANDLE_RELEASE_MARGIN_MS;
23912
+ for (;; ) {
23913
+ try {
23914
+ rmSync6(path, { force: true });
23915
+ return;
23916
+ } catch (error) {
23917
+ const code = error.code;
23918
+ const sharingFailure = process.platform === "win32" && (code === "EBUSY" || code === "EPERM" || code === "EACCES");
23919
+ if (!sharingFailure || Date.now() >= deadline)
23920
+ throw error;
23921
+ }
23922
+ await sleep(REMOVE_RETRY_DELAY_MS);
23923
+ }
23924
+ }
23044
23925
 
23045
23926
  // packages/local-runtime/process-liveness/src/index.ts
23046
23927
  function isProcessAlive(pid) {
@@ -25871,7 +26752,7 @@ function isIgnorableDirectoryFsyncError2(error) {
25871
26752
 
25872
26753
  // packages/local-runtime/git-adapter/src/index.ts
25873
26754
  import { execFileSync as execFileSync5, spawnSync as spawnSync2 } from "node:child_process";
25874
- import { existsSync as existsSync12, lstatSync as lstatSync6, mkdirSync as mkdirSync7, mkdtempSync as mkdtempSync2, readFileSync as readFileSync14, rmSync as rmSync8 } from "node:fs";
26755
+ import { existsSync as existsSync12, lstatSync as lstatSync6, mkdirSync as mkdirSync7, mkdtempSync as mkdtempSync2, readdirSync as readdirSync7, readFileSync as readFileSync14, rmSync as rmSync8 } from "node:fs";
25875
26756
  import { dirname as dirname8, join as join8, resolve as resolve15 } from "node:path";
25876
26757
  init_src();
25877
26758
  var GIT_CAT_FILE_MAX_BYTES2 = 512 * 1024 * 1024;
@@ -27427,7 +28308,7 @@ function unresolvedChangeSetJournalSummary(journals) {
27427
28308
  }
27428
28309
 
27429
28310
  // packages/local-runtime/runtime-daemon/src/external-documentation.ts
27430
- import { existsSync as existsSync15, readdirSync as readdirSync8, readFileSync as readFileSync17, statSync as statSync7 } from "node:fs";
28311
+ import { existsSync as existsSync15, readdirSync as readdirSync10, readFileSync as readFileSync17, statSync as statSync7 } from "node:fs";
27431
28312
  import { resolve as resolve18 } from "node:path";
27432
28313
 
27433
28314
  // packages/core/context-compiler/src/index.ts
@@ -27435,7 +28316,7 @@ init_src();
27435
28316
 
27436
28317
  // packages/core/practice-catalog/src/index.ts
27437
28318
  init_src();
27438
- import { existsSync as existsSync13, lstatSync as lstatSync7, readdirSync as readdirSync6, readFileSync as readFileSync15, realpathSync as realpathSync8 } from "node:fs";
28319
+ import { existsSync as existsSync13, lstatSync as lstatSync7, readdirSync as readdirSync8, readFileSync as readFileSync15, realpathSync as realpathSync8 } from "node:fs";
27439
28320
  import { dirname as dirname9, relative as relative9, resolve as resolve16, sep as sep7 } from "node:path";
27440
28321
  import { fileURLToPath } from "node:url";
27441
28322
  var PRACTICE_CATALOG_VERSION = "2026.06.0";
@@ -28046,7 +28927,7 @@ function listDataFiles(dir, allowedRoot, errors) {
28046
28927
  return;
28047
28928
  }
28048
28929
  if (stat.isDirectory()) {
28049
- for (const entry of readdirSync6(current, { withFileTypes: true }))
28930
+ for (const entry of readdirSync8(current, { withFileTypes: true }))
28050
28931
  walk(resolve16(current, entry.name));
28051
28932
  return;
28052
28933
  }
@@ -28660,7 +29541,7 @@ function checkResult(input, result) {
28660
29541
  }
28661
29542
  // packages/core/practice-engine/src/enforcement.ts
28662
29543
  init_src();
28663
- import { existsSync as existsSync14, lstatSync as lstatSync8, readdirSync as readdirSync7, readFileSync as readFileSync16 } from "node:fs";
29544
+ import { existsSync as existsSync14, lstatSync as lstatSync8, readdirSync as readdirSync9, readFileSync as readFileSync16 } from "node:fs";
28664
29545
  import { basename as basename4, join as join9, relative as relative10, resolve as resolve17, sep as sep8 } from "node:path";
28665
29546
  var ENFORCEMENT_RANK = { advisory: 0, checkpoint: 1, complete: 2 };
28666
29547
  var POLICY_MODES = new Set(["advisory", "active", "fail-open", "fail-closed"]);
@@ -28702,7 +29583,7 @@ function loadPracticeWaivers(root) {
28702
29583
  throw new Error("practice-waiver-path-not-directory");
28703
29584
  const waivers = [];
28704
29585
  const ownerRegistry = loadPracticeWaiverOwnerRegistry(root);
28705
- for (const entry of readdirSync7(dir, { withFileTypes: true })) {
29586
+ for (const entry of readdirSync9(dir, { withFileTypes: true })) {
28706
29587
  if (!entry.isFile() || !/\.(?:ya?ml|json)$/.test(entry.name))
28707
29588
  continue;
28708
29589
  const path = join9(dir, entry.name);
@@ -29005,7 +29886,7 @@ function parseJsonYamlFile(path) {
29005
29886
  function collectFiles(root, relativeDir) {
29006
29887
  const dir = resolve17(root, relativeDir);
29007
29888
  const out = [];
29008
- for (const entry of readdirSync7(dir, { withFileTypes: true })) {
29889
+ for (const entry of readdirSync9(dir, { withFileTypes: true })) {
29009
29890
  const child = `${relativeDir}/${entry.name}`;
29010
29891
  if (entry.isDirectory())
29011
29892
  out.push(...collectFiles(root, child));
@@ -30571,7 +31452,7 @@ function expandWorkspacePackageJson(root, pattern) {
30571
31452
  const base = resolve18(root, pattern.slice(0, -2));
30572
31453
  if (!existsSync15(base) || !statSync7(base).isDirectory())
30573
31454
  return [];
30574
- return readdirSync8(base, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => resolve18(base, entry.name, "package.json")).filter((path) => existsSync15(path));
31455
+ return readdirSync10(base, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => resolve18(base, entry.name, "package.json")).filter((path) => existsSync15(path));
30575
31456
  }
30576
31457
  function exactVersionFromManifest(manifest, packageName) {
30577
31458
  if (!manifest || typeof manifest !== "object" || Array.isArray(manifest))
@@ -31349,7 +32230,7 @@ function parsePracticeCheckpointBaselineState(state, repositoryId, taskSessionId
31349
32230
  // packages/local-runtime/runtime-daemon/src/developer-review-run.ts
31350
32231
  init_src();
31351
32232
  import { randomBytes as randomBytes2 } from "node:crypto";
31352
- import { chmodSync as chmodSync6, existsSync as existsSync18, lstatSync as lstatSync9, mkdirSync as mkdirSync8, mkdtempSync as mkdtempSync3, readdirSync as readdirSync9, readFileSync as readFileSync18, rmSync as rmSync10, writeFileSync as writeFileSync7 } from "node:fs";
32233
+ import { chmodSync as chmodSync6, existsSync as existsSync18, lstatSync as lstatSync9, mkdirSync as mkdirSync8, mkdtempSync as mkdtempSync3, readdirSync as readdirSync11, readFileSync as readFileSync18, rmSync as rmSync10, writeFileSync as writeFileSync7 } from "node:fs";
31353
32234
  import { tmpdir as tmpdir2 } from "node:os";
31354
32235
  import { basename as basename5, dirname as dirname10, join as join11, resolve as resolve20, sep as sep9 } from "node:path";
31355
32236
  // packages/local-runtime/runtime-daemon/src/rpc-protocol.ts
@@ -32120,7 +33001,7 @@ class DeveloperReviewRunService {
32120
33001
  };
32121
33002
  if (!existsSync18(stateDir))
32122
33003
  return recovery;
32123
- for (const entry of readdirSync9(stateDir).sort()) {
33004
+ for (const entry of readdirSync11(stateDir).sort()) {
32124
33005
  if (!entry.endsWith(".json"))
32125
33006
  continue;
32126
33007
  const manifestPath = join11(stateDir, entry);
@@ -32148,7 +33029,7 @@ class DeveloperReviewRunService {
32148
33029
  recovery.rejected.push(`${entry}: ${error instanceof Error ? error.message : String(error)}`);
32149
33030
  }
32150
33031
  }
32151
- for (const entry of readdirSync9(stateDir).sort()) {
33032
+ for (const entry of readdirSync11(stateDir).sort()) {
32152
33033
  if (!entry.endsWith(".lock"))
32153
33034
  continue;
32154
33035
  const lockPath = join11(stateDir, entry);
@@ -33344,7 +34225,7 @@ init_src();
33344
34225
 
33345
34226
  // packages/local-runtime/model-store-yaml/src/index.ts
33346
34227
  init_src();
33347
- import { existsSync as existsSync19, lstatSync as lstatSync10, mkdirSync as mkdirSync9, readdirSync as readdirSync10, readFileSync as readFileSync19, rmSync as rmSync11 } from "node:fs";
34228
+ import { existsSync as existsSync19, lstatSync as lstatSync10, mkdirSync as mkdirSync9, readdirSync as readdirSync12, readFileSync as readFileSync19, rmSync as rmSync11 } from "node:fs";
33348
34229
  import { dirname as dirname11, resolve as resolve21 } from "node:path";
33349
34230
  var GENERATED_ARCHITECTURE_PATH = ".archcontext/generated/ARCHITECTURE.md";
33350
34231
  var GENERATED_ARCHITECTURE_BODY = [
@@ -33569,7 +34450,7 @@ function collectGeneratedProjectionFiles(root) {
33569
34450
  const generatedRoot = ".archcontext/generated";
33570
34451
  if (!existsSync19(resolve21(root, generatedRoot)))
33571
34452
  return [];
33572
- const walk = (relativeDir) => readdirSync10(resolve21(root, relativeDir), { withFileTypes: true }).flatMap((entry) => {
34453
+ const walk = (relativeDir) => readdirSync12(resolve21(root, relativeDir), { withFileTypes: true }).flatMap((entry) => {
33573
34454
  const child = `${relativeDir}/${entry.name}`;
33574
34455
  if (entry.isDirectory())
33575
34456
  return walk(child);
@@ -33664,7 +34545,7 @@ function collectArchContextFiles(root, entries) {
33664
34545
  function walk(relativeDir) {
33665
34546
  const out = [];
33666
34547
  const dir = resolve21(root, relativeDir);
33667
- for (const entry of readdirSync10(dir, { withFileTypes: true })) {
34548
+ for (const entry of readdirSync12(dir, { withFileTypes: true })) {
33668
34549
  const child = `${relativeDir}/${entry.name}`;
33669
34550
  if (entry.isDirectory())
33670
34551
  out.push(...walk(child));
@@ -33681,7 +34562,7 @@ function isCollectableModelFile(path) {
33681
34562
  }
33682
34563
  function readdirOrFile(path) {
33683
34564
  try {
33684
- readdirSync10(path);
34565
+ readdirSync12(path);
33685
34566
  return "directory";
33686
34567
  } catch {
33687
34568
  return "file";
@@ -42854,6 +43735,7 @@ function buildArchitectureDocsProjection(_host, root, generatedAt, profile = "de
42854
43735
  profile,
42855
43736
  decisions: loaded.decisions,
42856
43737
  existingFiles: loaded.existingFiles,
43738
+ docsLanguage: loaded.docsLanguage,
42857
43739
  sourceFootprints,
42858
43740
  sourceScaleSignals: loadCapabilitySourceScaleSignals(root, loaded.model, codeGraphInputs.sourceFiles),
42859
43741
  importGraphs: codeGraphInputs.importGraphs,
@@ -43954,6 +44836,7 @@ function buildRuntimeProjectionRecoveryFixedPoint(root) {
43954
44836
  profile: REPO_HARNESS_PROJECTION_PROFILE,
43955
44837
  decisions: loaded.decisions,
43956
44838
  existingFiles: loaded.existingFiles,
44839
+ docsLanguage: loaded.docsLanguage,
43957
44840
  sourceFootprints: loadCapabilitySourceFootprintDigests(root, loaded.model, codeGraphInputs.sourceFiles),
43958
44841
  sourceScaleSignals: loadCapabilitySourceScaleSignals(root, loaded.model, codeGraphInputs.sourceFiles),
43959
44842
  importGraphs: codeGraphInputs.importGraphs,
@@ -44068,12 +44951,123 @@ function runtimeProjectionOwnedOutputDigest(projection) {
44068
44951
  files: projection.files.map((file) => ({ path: file.path, body: file.body })).sort((left, right) => left.path.localeCompare(right.path))
44069
44952
  });
44070
44953
  }
44071
- // packages/core/recommendation-engine/src/index.ts
44954
+ // packages/local-runtime/runtime-daemon/src/docs-anchors.ts
44072
44955
  init_src();
44073
-
44074
- // packages/core/refactor-assessment/src/index.ts
44075
- init_src();
44076
-
44956
+ import { spawnSync as spawnSync3 } from "node:child_process";
44957
+ import { readFileSync as readFileSync24 } from "node:fs";
44958
+ import { resolve as resolve25 } from "node:path";
44959
+ function runDocsAnchorsCommand(cwd, options = {}) {
44960
+ let opened;
44961
+ try {
44962
+ const root = findRepositoryRoot(cwd);
44963
+ const profile = options.profile ?? committedArchitectureDocsProfile(root);
44964
+ const model = loadNativeModelFromArchContext(root);
44965
+ const layout = resolveArchitectureDocumentationLayout({ nodes: model.nodes, relations: model.relations, profile });
44966
+ const documents = new Map(loadArchitectureFilesForLayout(root, layout).map((file) => [file.path, file.body]));
44967
+ const files = listProjectionSourceFiles(root);
44968
+ const footprints = new Map(loadCapabilitySourceFootprints(root, model, files).map((footprint) => [footprint.nodeId, footprint.files]));
44969
+ const contents = fileContentReader(root);
44970
+ const tree = gitVisibleTree(root, files, contents);
44971
+ opened = openCodeGraphSymbolReader(root);
44972
+ const codeGraph = opened.availability === "ready" ? { availability: "ready", symbols: proseAnchorSymbols(opened.reader, files, contents) } : { availability: "unavailable", reasonCode: opened.reasonCode };
44973
+ const report = checkProseAnchors({
44974
+ profile,
44975
+ codeGraph,
44976
+ tree,
44977
+ nodes: model.nodes.map((node) => {
44978
+ const documentPath = layout.entityPathByNodeId.get(node.id);
44979
+ const body = documentPath === undefined ? undefined : documents.get(documentPath);
44980
+ const responsibilities = Array.isArray(node.responsibilities) ? node.responsibilities.filter((entry) => typeof entry === "string") : undefined;
44981
+ return {
44982
+ nodeId: node.id,
44983
+ ...typeof node.summary === "string" ? { summary: node.summary } : {},
44984
+ ...responsibilities ? { responsibilities } : {},
44985
+ ...footprints.has(node.id) ? { footprint: footprints.get(node.id) } : {},
44986
+ ...documentPath !== undefined && body !== undefined ? { document: { path: documentPath, body } } : {}
44987
+ };
44988
+ })
44989
+ });
44990
+ const issues = docsAnchorsReportIssues(report);
44991
+ if (issues.length > 0)
44992
+ throw new Error(`docs-anchors-report-invalid: ${issues.join("; ")}`);
44993
+ return okEnvelope("docs.anchors", report);
44994
+ } catch (error) {
44995
+ return projectionFailureEnvelope("docs.anchors", error);
44996
+ } finally {
44997
+ if (opened?.availability === "ready")
44998
+ opened.reader.close();
44999
+ }
45000
+ }
45001
+ function fileContentReader(root) {
45002
+ const cache = new Map;
45003
+ return (path) => {
45004
+ let content = cache.get(path);
45005
+ if (content === undefined) {
45006
+ content = readFileSync24(resolve25(root, path), "utf8").replace(/\r\n/g, `
45007
+ `);
45008
+ cache.set(path, content);
45009
+ }
45010
+ return content;
45011
+ };
45012
+ }
45013
+ function gitVisibleTree(root, files, contents) {
45014
+ const fileSet = new Set(files);
45015
+ const directories = new Set;
45016
+ const topLevel = new Set;
45017
+ const ignored = new Map;
45018
+ for (const file of files) {
45019
+ const segments = file.split("/");
45020
+ topLevel.add(segments[0]);
45021
+ for (let index = 1;index < segments.length; index += 1)
45022
+ directories.add(segments.slice(0, index).join("/"));
45023
+ }
45024
+ return {
45025
+ kind: (path) => fileSet.has(path) ? "file" : directories.has(path) ? "directory" : undefined,
45026
+ hasTopLevelEntry: (segment) => topLevel.has(segment),
45027
+ filesEndingWith: (suffix) => files.filter((file) => file === suffix || file.endsWith(`/${suffix}`)),
45028
+ lineCount: (path) => {
45029
+ const content = contents(path);
45030
+ if (content === "")
45031
+ return 0;
45032
+ return content.split(`
45033
+ `).length - (content.endsWith(`
45034
+ `) ? 1 : 0);
45035
+ },
45036
+ isIgnored: (path) => {
45037
+ let found = ignored.get(path);
45038
+ if (found === undefined) {
45039
+ found = spawnSync3("git", ["check-ignore", "--", path, `${path}/`], { cwd: root, stdio: "ignore" }).status === 0;
45040
+ ignored.set(path, found);
45041
+ }
45042
+ return found;
45043
+ }
45044
+ };
45045
+ }
45046
+ var SOURCE_SEARCH_EXCLUDED_PREFIXES = [".archcontext/"];
45047
+ function proseAnchorSymbols(reader, files, contents) {
45048
+ const sourceFiles = files.filter((file) => !SOURCE_SEARCH_EXCLUDED_PREFIXES.some((prefix) => file.startsWith(prefix)) && reader.supportsPath(file));
45049
+ const appears = new Map;
45050
+ const parsed = new Map;
45051
+ const declarationsIn = (path) => {
45052
+ if (!parsed.has(path))
45053
+ parsed.set(path, reader.supportsPath(path) ? reader.declarationsInSource(path, contents(path)) : undefined);
45054
+ return parsed.get(path);
45055
+ };
45056
+ return {
45057
+ supportsPath: (path) => declarationsIn(path) !== undefined,
45058
+ declarationsInFile: (path, name) => (declarationsIn(path) ?? []).filter((declaration) => declaration.name === name),
45059
+ declarationsNamed: (name) => reader.declarationsNamed(name),
45060
+ appearsInSource: (name) => {
45061
+ let found = appears.get(name);
45062
+ if (found === undefined) {
45063
+ const word = new RegExp(`(?<![A-Za-z0-9_$])${name.replace(/[$]/g, "\\$")}(?![A-Za-z0-9_$])`);
45064
+ found = sourceFiles.some((file) => word.test(contents(file)));
45065
+ appears.set(name, found);
45066
+ }
45067
+ return found;
45068
+ }
45069
+ };
45070
+ }
44077
45071
  // packages/core/module-statistics/src/index.ts
44078
45072
  init_src();
44079
45073
 
@@ -44221,10 +45215,10 @@ function tarjanComponents(nodeIds, successors) {
44221
45215
  }
44222
45216
 
44223
45217
  // packages/core/module-statistics/src/ownership.ts
45218
+ init_src();
44224
45219
  function resolveOwnership(nodes, paths) {
44225
45220
  const declaring = nodes.filter((node) => (nativeNodeSource(node)?.include ?? []).length > 0);
44226
45221
  const parents = new Map(nodes.map((node) => [node.id, node.parent]));
44227
- const depths = new Map(nodes.map((node) => [node.id, depthOf(node.id, parents)]));
44228
45222
  const roots = declaredSourceRoots(declaring);
44229
45223
  const excludes = declaring.flatMap((node) => nativeNodeSource(node)?.exclude ?? []);
44230
45224
  const byPath = new Map;
@@ -44243,7 +45237,7 @@ function resolveOwnership(nodes, paths) {
44243
45237
  byPath.set(path, { owners: [], ambiguous: false });
44244
45238
  continue;
44245
45239
  }
44246
- const resolution = resolveCandidates(candidates, parents, depths);
45240
+ const resolution = resolveCandidates(candidates, parents);
44247
45241
  ownedFileCount += 1;
44248
45242
  if (resolution.ambiguous)
44249
45243
  multiplyOwnedFileCount += 1;
@@ -44290,38 +45284,90 @@ function claims(node, path) {
44290
45284
  return false;
44291
45285
  return include.some((pattern) => matchesGlob(path, pattern));
44292
45286
  }
44293
- function resolveCandidates(candidates, parents, depths) {
44294
- if (candidates.length === 1)
44295
- return { owners: [candidates[0].id], ambiguous: false };
44296
- const deepest = candidates.reduce((best, node) => {
44297
- const bestDepth = depths.get(best.id) ?? 0;
44298
- const nodeDepth = depths.get(node.id) ?? 0;
44299
- if (nodeDepth !== bestDepth)
44300
- return nodeDepth > bestDepth ? node : best;
44301
- return node.id < best.id ? node : best;
44302
- });
44303
- const chain = ancestorChain(deepest.id, parents);
44304
- if (candidates.every((node) => chain.has(node.id)))
44305
- return { owners: [deepest.id], ambiguous: false };
44306
- return { owners: candidates.map((node) => node.id).sort(), ambiguous: true };
44307
- }
44308
- function ancestorChain(id, parents) {
44309
- const chain = new Set;
44310
- let current = id;
44311
- while (current !== undefined && parents.has(current) && !chain.has(current)) {
44312
- chain.add(current);
44313
- current = parents.get(current);
44314
- }
44315
- return chain;
45287
+ function resolveCandidates(candidates, parents) {
45288
+ const ids = candidates.map((node) => node.id);
45289
+ const owner = deepestOnOneParentChain(ids, parents);
45290
+ if (owner !== undefined)
45291
+ return { owners: [owner], ambiguous: false };
45292
+ return { owners: ids.sort(), ambiguous: true };
45293
+ }
45294
+ function ownershipDelta(before, after, paths, pathLimit = CHANGESET_OWNERSHIP_DELTA_PATH_LIMIT) {
45295
+ const changedNodeIds = ownershipRelevantChanges(before, after);
45296
+ const beforeIndex = resolveOwnership(before, paths);
45297
+ const afterIndex = changedNodeIds.length === 0 ? beforeIndex : resolveOwnership(after, paths);
45298
+ const categories = {
45299
+ becameOwned: [],
45300
+ becameContested: [],
45301
+ ownerChanged: [],
45302
+ becameUnowned: []
45303
+ };
45304
+ for (const path of [...paths].sort()) {
45305
+ const was = beforeIndex.byPath.get(path) ?? { owners: [], ambiguous: false };
45306
+ const now = afterIndex.byPath.get(path) ?? { owners: [], ambiguous: false };
45307
+ if (sameOwners(was.owners, now.owners))
45308
+ continue;
45309
+ const file = { path, before: [...was.owners].sort(), after: [...now.owners].sort() };
45310
+ if (now.ambiguous && !was.ambiguous)
45311
+ categories.becameContested.push(file);
45312
+ else if (was.owners.length === 0)
45313
+ categories.becameOwned.push(file);
45314
+ else if (now.owners.length === 0)
45315
+ categories.becameUnowned.push(file);
45316
+ else
45317
+ categories.ownerChanged.push(file);
45318
+ }
45319
+ const summary = (index) => ({
45320
+ ownedFileCount: index.ownedFileCount,
45321
+ multiplyOwnedFileCount: index.multiplyOwnedFileCount,
45322
+ unownedFileCount: index.unownedFileCount
45323
+ });
45324
+ const bounded = (files) => {
45325
+ const listed = files.slice(0, pathLimit);
45326
+ return { count: files.length, files: listed, omittedCount: files.length - listed.length };
45327
+ };
45328
+ return {
45329
+ schemaVersion: CHANGESET_OWNERSHIP_DELTA_SCHEMA_VERSION,
45330
+ status: "computed",
45331
+ ownershipRule: "parent-chain",
45332
+ fileUniverse: "head-tracked-files",
45333
+ trackedFileCount: paths.length,
45334
+ changedNodeIds,
45335
+ pathLimit,
45336
+ before: summary(beforeIndex),
45337
+ after: summary(afterIndex),
45338
+ becameOwned: bounded(categories.becameOwned),
45339
+ becameContested: bounded(categories.becameContested),
45340
+ ownerChanged: bounded(categories.ownerChanged),
45341
+ becameUnowned: bounded(categories.becameUnowned)
45342
+ };
45343
+ }
45344
+ function ownershipRelevantChanges(before, after) {
45345
+ const key = (node) => {
45346
+ if (node === undefined)
45347
+ return "absent";
45348
+ const source = nativeNodeSource(node);
45349
+ return digestJson({
45350
+ include: source?.include ?? [],
45351
+ exclude: source?.exclude ?? [],
45352
+ parent: node.parent ?? null
45353
+ });
45354
+ };
45355
+ const beforeById = new Map(before.map((node) => [node.id, node]));
45356
+ const afterById = new Map(after.map((node) => [node.id, node]));
45357
+ const ids = new Set([...beforeById.keys(), ...afterById.keys()]);
45358
+ return [...ids].filter((id) => key(beforeById.get(id)) !== key(afterById.get(id))).sort();
44316
45359
  }
44317
- function depthOf(id, parents) {
44318
- return ancestorChain(id, parents).size - 1;
45360
+ function sameOwners(left, right) {
45361
+ if (left.length !== right.length)
45362
+ return false;
45363
+ const sortedRight = [...right].sort();
45364
+ return [...left].sort().every((owner, index) => owner === sortedRight[index]);
44319
45365
  }
44320
45366
 
44321
45367
  // packages/core/module-statistics/src/index.ts
44322
45368
  function evaluateDependencyConstraints(input) {
44323
45369
  const constraints = [...input.constraints].sort((left, right) => compare(left.id, right.id));
44324
- const certification = certifyCodeFacts(input.codeFacts, input.worktreeDigest, input.truncated);
45370
+ const certification = certifyCodeFacts(input.codeFacts, input.worktreeDigest, input.truncated, input.importNodeLimitExceeded ?? false);
44325
45371
  if (constraints.length === 0) {
44326
45372
  return {
44327
45373
  schemaVersion: "archcontext.dependency-constraint-evaluation/v1",
@@ -44389,7 +45435,87 @@ function evaluateDependencyConstraints(input) {
44389
45435
  violations: sortedViolations
44390
45436
  };
44391
45437
  }
44392
- function certifyCodeFacts(codeFacts, worktreeDigest, truncated) {
45438
+ function measureCapabilityDependencies(input) {
45439
+ const certification = certifyCodeFacts(input.codeFacts, input.worktreeDigest, input.truncated, input.importNodeLimitExceeded);
45440
+ const paths = new Set(input.files);
45441
+ const edges = (certification.measurable ? resolveEdges(input.importEdges, input.workspacePackages, paths) : []).filter((edge) => paths.has(edge.from) && (edge.to === null || paths.has(edge.to)));
45442
+ const ownership = resolveOwnership(input.nodes, [...paths].sort());
45443
+ const parents = new Map(input.nodes.map((node) => [node.id, node.parent]));
45444
+ const kinds = new Map(input.nodes.map((node) => [node.id, node.kind]));
45445
+ const capabilityOf = (owner) => {
45446
+ for (const ancestor of ancestorChain(owner, parents))
45447
+ if (kinds.get(ancestor) === "capability")
45448
+ return ancestor;
45449
+ return null;
45450
+ };
45451
+ const ownersOf = (path) => ownership.byPath.get(path)?.owners ?? [];
45452
+ const within = (path, nodeId) => nodeId === undefined || ownersOf(path).some((owner) => ancestorChain(owner, parents).has(nodeId));
45453
+ const attribute = (path) => {
45454
+ const owners = ownersOf(path);
45455
+ if (owners.length === 0)
45456
+ return { gap: "unownedEndpoint" };
45457
+ const capabilities = new Set(owners.map(capabilityOf));
45458
+ if (capabilities.size > 1)
45459
+ return { gap: "contestedEndpoint" };
45460
+ const [capability] = capabilities;
45461
+ return capability === null ? { gap: "noCapabilityEndpoint" } : { capability };
45462
+ };
45463
+ const unattributed = { unownedEndpoint: 0, contestedEndpoint: 0, noCapabilityEndpoint: 0 };
45464
+ const cells = new Map;
45465
+ const attributedEdges = [];
45466
+ const seen = new Set;
45467
+ let unresolvedCount = 0;
45468
+ for (const edge of edges) {
45469
+ if (!within(edge.from, input.from))
45470
+ continue;
45471
+ if (edge.to === null) {
45472
+ if (input.into === undefined && isRepositoryLocalSpecifier(edge.specifier, input.workspacePackages))
45473
+ unresolvedCount += 1;
45474
+ continue;
45475
+ }
45476
+ if (edge.to === edge.from || !within(edge.to, input.into))
45477
+ continue;
45478
+ const key = JSON.stringify([edge.from, edge.to]);
45479
+ if (seen.has(key))
45480
+ continue;
45481
+ seen.add(key);
45482
+ const from = attribute(edge.from);
45483
+ const to = attribute(edge.to);
45484
+ if ("gap" in from || "gap" in to) {
45485
+ unattributed["gap" in from ? from.gap : to.gap] += 1;
45486
+ continue;
45487
+ }
45488
+ const cellKey = JSON.stringify([from.capability, to.capability]);
45489
+ const cell = cells.get(cellKey) ?? { from: from.capability, to: to.capability, imports: 0, calls: null };
45490
+ cell.imports += 1;
45491
+ cells.set(cellKey, cell);
45492
+ attributedEdges.push({ fromPath: edge.from, toPath: edge.to, fromCapability: from.capability, toCapability: to.capability });
45493
+ }
45494
+ const edgeLimit = input.edgeLimit ?? CAPABILITY_DEPENDENCIES_EDGE_LIMIT;
45495
+ attributedEdges.sort((left, right) => compare(left.fromPath, right.fromPath) || compare(left.toPath, right.toPath));
45496
+ const listed = attributedEdges.slice(0, edgeLimit);
45497
+ return {
45498
+ schemaVersion: CAPABILITY_DEPENDENCIES_SCHEMA_VERSION,
45499
+ ownershipRule: "parent-chain",
45500
+ filter: { from: input.from ?? null, into: input.into ?? null },
45501
+ capabilities: input.nodes.filter((node) => node.kind === "capability").map((node) => node.id).sort(compare),
45502
+ matrix: [...cells.values()].sort((left, right) => compare(left.from, right.from) || compare(left.to, right.to)),
45503
+ imports: {
45504
+ coverage: certification.coverage,
45505
+ reasonCodes: [...certification.reasonCodes].sort(),
45506
+ truncated: input.truncated,
45507
+ importNodeCount: input.importNodeCount,
45508
+ importNodeLimit: input.importNodeLimit,
45509
+ importNodeLimitExceeded: input.importNodeLimitExceeded,
45510
+ edgeCount: seen.size,
45511
+ unresolvedCount,
45512
+ unattributed
45513
+ },
45514
+ calls: { coverage: "unknown", reasonCodes: [CAPABILITY_DEPENDENCIES_CALLS_UNAVAILABLE_REASON] },
45515
+ edges: { limit: edgeLimit, count: attributedEdges.length, omittedCount: attributedEdges.length - listed.length, items: listed }
45516
+ };
45517
+ }
45518
+ function certifyCodeFacts(codeFacts, worktreeDigest, truncated, importNodeLimitExceeded) {
44393
45519
  const attested = codeFacts.indexedWorktreeDigest !== null && codeFacts.indexedWorktreeDigest === worktreeDigest;
44394
45520
  const measurable = codeFacts.availability === "ready" && attested;
44395
45521
  const reasonCodes = [];
@@ -44397,9 +45523,12 @@ function certifyCodeFacts(codeFacts, worktreeDigest, truncated) {
44397
45523
  reasonCodes.push("code-facts-unavailable");
44398
45524
  else if (!attested)
44399
45525
  reasonCodes.push("code-facts-stale");
45526
+ if (measurable && importNodeLimitExceeded)
45527
+ reasonCodes.push("code-facts-import-limit");
44400
45528
  if (measurable && truncated)
44401
45529
  reasonCodes.push("code-facts-truncated");
44402
- return { measurable, coverage: !measurable ? "unknown" : truncated ? "partial" : "complete", reasonCodes };
45530
+ const partial = truncated || importNodeLimitExceeded;
45531
+ return { measurable, coverage: !measurable ? "unknown" : partial ? "partial" : "complete", reasonCodes };
44403
45532
  }
44404
45533
  function isRepositoryLocalSpecifier(specifier, workspacePackages) {
44405
45534
  if (specifier === "." || specifier === ".." || specifier.startsWith("./") || specifier.startsWith("../"))
@@ -44414,7 +45543,8 @@ function measureModuleStatistics(input) {
44414
45543
  const trackedFiles = [...input.trackedFiles].sort((left, right) => left.path < right.path ? -1 : left.path > right.path ? 1 : 0);
44415
45544
  const linesByPath = new Map(trackedFiles.map((file) => [file.path, file.lineCount]));
44416
45545
  const ownership = resolveOwnership(nodes, trackedFiles.map((file) => file.path));
44417
- const { measurable, coverage } = certifyCodeFacts(input.codeFacts, input.worktree.worktreeDigest, input.truncated);
45546
+ const importNodeLimitExceeded = input.importNodeLimitExceeded ?? false;
45547
+ const { measurable, coverage } = certifyCodeFacts(input.codeFacts, input.worktree.worktreeDigest, input.truncated, importNodeLimitExceeded);
44418
45548
  const edges = measurable ? resolveEdges(input.importEdges, input.workspacePackages, new Set(linesByPath.keys())) : [];
44419
45549
  const constraints = input.constraints ?? [];
44420
45550
  const directionEvaluation = constraints.length === 0 ? undefined : evaluateDependencyConstraints({
@@ -44425,6 +45555,7 @@ function measureModuleStatistics(input) {
44425
45555
  importEdges: input.importEdges,
44426
45556
  workspacePackages: input.workspacePackages,
44427
45557
  truncated: input.truncated,
45558
+ importNodeLimitExceeded,
44428
45559
  codeFacts: input.codeFacts
44429
45560
  });
44430
45561
  const directionViolations = directionEvaluation === undefined ? undefined : countViolationsByNode(directionEvaluation);
@@ -44447,7 +45578,8 @@ function measureModuleStatistics(input) {
44447
45578
  coverage,
44448
45579
  truncated: coverage === "unknown" ? true : input.truncated,
44449
45580
  edgeLimit: input.edgeLimit,
44450
- reasonCodes: reasonCodes({ coverage, truncated: input.truncated, measurable, modules, ownership })
45581
+ ...input.importNodeCount === undefined ? {} : { importNodeCount: input.importNodeCount },
45582
+ reasonCodes: reasonCodes({ coverage, truncated: input.truncated, importNodeLimitExceeded, measurable, modules, ownership })
44451
45583
  };
44452
45584
  const draft = {
44453
45585
  schemaVersion: MODULE_STATISTICS_SCHEMA_VERSION,
@@ -44557,6 +45689,8 @@ function reasonCodes(context) {
44557
45689
  codes.add("code-facts-missing");
44558
45690
  if (context.truncated || context.coverage === "unknown")
44559
45691
  codes.add("code-facts-truncated");
45692
+ if (context.measurable && context.importNodeLimitExceeded)
45693
+ codes.add("code-facts-import-limit");
44560
45694
  if (context.modules.some((module) => !module.footprintDeclared))
44561
45695
  codes.add("node-footprint-undeclared");
44562
45696
  if (context.ownership.multiplyOwnedFileCount > 0)
@@ -44566,6 +45700,16 @@ function reasonCodes(context) {
44566
45700
  return [...codes].sort();
44567
45701
  }
44568
45702
 
45703
+ // packages/local-runtime/runtime-daemon/src/refactor-scan.ts
45704
+ init_src();
45705
+ import { execFileSync as execFileSync9 } from "node:child_process";
45706
+ import { createHash as createHash10 } from "node:crypto";
45707
+ import { existsSync as existsSync24, readdirSync as readdirSync13, readFileSync as readFileSync25 } from "node:fs";
45708
+ import { join as join16 } from "node:path";
45709
+
45710
+ // packages/core/refactor-assessment/src/index.ts
45711
+ init_src();
45712
+
44569
45713
  // packages/core/refactor-assessment/src/evidence.ts
44570
45714
  init_src();
44571
45715
  class ObservationEvidenceBuilder {
@@ -45320,7 +46464,8 @@ function buildObservations(snapshot, structure, model) {
45320
46464
  subjectSelectorId: repositorySubject,
45321
46465
  metrics: {
45322
46466
  unresolvedImportCount: snapshot.repositorySummary.unresolvedImportCount,
45323
- edgeLimit: snapshot.codeFacts.edgeLimit
46467
+ edgeLimit: snapshot.codeFacts.edgeLimit,
46468
+ ...snapshot.codeFacts.importNodeCount === undefined ? {} : { importNodeCount: snapshot.codeFacts.importNodeCount }
45324
46469
  },
45325
46470
  evidence: evidence.evidenceGap()
45326
46471
  });
@@ -45359,8 +46504,12 @@ function classifyProposal(context) {
45359
46504
  codes.add("node-footprint-undeclared");
45360
46505
  if (coverage === "unknown")
45361
46506
  codes.add("code-facts-missing");
45362
- if (coverage === "partial")
45363
- codes.add("code-facts-truncated");
46507
+ if (coverage === "partial") {
46508
+ const limited = context.snapshot.codeFacts.reasonCodes.includes("code-facts-import-limit");
46509
+ codes.add(limited ? "code-facts-import-limit" : "code-facts-truncated");
46510
+ if (limited && context.snapshot.codeFacts.truncated)
46511
+ codes.add("code-facts-truncated");
46512
+ }
45364
46513
  if (scopeOwnership.contested.length > 0)
45365
46514
  codes.add("ownership-ambiguous");
45366
46515
  if (derivation.unresolvedTargets.length > 0)
@@ -45502,7 +46651,11 @@ function isPresent2(value) {
45502
46651
  return value !== undefined;
45503
46652
  }
45504
46653
 
46654
+ // packages/local-runtime/runtime-daemon/src/refactor-recording.ts
46655
+ init_src();
46656
+
45505
46657
  // packages/core/recommendation-engine/src/index.ts
46658
+ init_src();
45506
46659
  var RECOMMENDATION_SCHEDULER_ENGINE_VERSION = "archcontext.recommendation-scheduler/v1";
45507
46660
  var ACTIVE_RECOMMENDATION_STATUSES = new Set(["open", "acknowledged", "deferred"]);
45508
46661
  var REFACTOR_ACTIVE_RECOMMENDATION_STATUSES = new Set([
@@ -46176,11 +47329,16 @@ function observationExplanation(observation, affectedNodeIds) {
46176
47329
  const examples = evidence.unresolvedImports.slice(0, EXPLANATION_EXAMPLE_LIMIT);
46177
47330
  return [
46178
47331
  `Code facts coverage is ${evidence.coverage} (${evidence.reasonCodes.join(", ")}), so structural metrics${affectedNodeIds.length > 0 ? ` for ${affectedNodeIds.join(", ")}` : ""} are incomplete.`,
47332
+ ...evidence.reasonCodes.includes("code-facts-import-limit") ? [importLimitText(observation.metrics)] : [],
46179
47333
  ...evidence.totalCount === 0 ? [] : [`${evidence.totalCount} import specifier(s) did not resolve to a tracked file.`, ...examples.map((entry) => `${locationText(entry.fromPath, entry.fromLine)} imports ${entry.specifier}.`), ...more(examples.length)]
46180
47334
  ];
46181
47335
  }
46182
47336
  }
46183
47337
  }
47338
+ function importLimitText(metrics) {
47339
+ const count = typeof metrics.importNodeCount === "number" ? `${metrics.importNodeCount} import nodes` : "more import nodes";
47340
+ return `The index holds ${count}, more than the scan import node limit of ${metrics.edgeLimit ?? "unknown"}. Run archctx refactor scan --import-node-limit <n> with a larger limit; a new CodeGraph index does not change this.`;
47341
+ }
46184
47342
  function evidenceLength(evidence) {
46185
47343
  switch (evidence.kind) {
46186
47344
  case "cycle":
@@ -46365,7 +47523,6 @@ function latestRecommendationV3ByFingerprint(previous) {
46365
47523
  }
46366
47524
 
46367
47525
  // packages/local-runtime/runtime-daemon/src/refactor-recording.ts
46368
- init_src();
46369
47526
  var REFACTOR_ASSESSMENT_REGISTRY_CAPACITY = 8;
46370
47527
  var REFACTOR_CLASSIFIER_RULESET_SCHEMA_VERSION = "archcontext.refactor-classifier-ruleset/v1";
46371
47528
  var REFACTOR_SCAN_EVENT_TYPE = "architecture.refactor.scan";
@@ -46606,17 +47763,15 @@ function digestSuffix4(digest) {
46606
47763
  }
46607
47764
 
46608
47765
  // packages/local-runtime/runtime-daemon/src/refactor-scan.ts
46609
- init_src();
46610
- import { execFileSync as execFileSync9 } from "node:child_process";
46611
- import { createHash as createHash10 } from "node:crypto";
46612
- import { existsSync as existsSync24, readdirSync as readdirSync11, readFileSync as readFileSync24 } from "node:fs";
46613
- import { join as join16 } from "node:path";
46614
47766
  var CODEGRAPH_BINARY = "codegraph";
46615
47767
  var CODEGRAPH_VERSION_TIMEOUT_MS = 15000;
46616
47768
  var REPOSITORY_REFACTOR_REQUEST = {
46617
47769
  schemaVersion: REFACTOR_REQUEST_SCHEMA_VERSION,
46618
47770
  scope: { kind: "repository" }
46619
47771
  };
47772
+ function repositoryRefactorRequest(importNodeLimit) {
47773
+ return importNodeLimit === undefined ? REPOSITORY_REFACTOR_REQUEST : { ...REPOSITORY_REFACTOR_REQUEST, importNodeLimit };
47774
+ }
46620
47775
 
46621
47776
  class RefactorScanError extends Error {
46622
47777
  code;
@@ -46640,7 +47795,7 @@ function runRefactorScan(input) {
46640
47795
  const constraints = readDependencyConstraints(listModelFiles(input.root)).constraints;
46641
47796
  const createdAt = readHeadCommitterDate(input.root);
46642
47797
  const requestId = refactorRequestId(input.request);
46643
- const edgeLimit = input.edgeLimit ?? CODEGRAPH_IMPORT_NODE_QUERY_LIMIT;
47798
+ const edgeLimit = input.request.importNodeLimit ?? CODEGRAPH_IMPORT_NODE_READ_LIMIT;
46644
47799
  const trackedFiles = readTrackedSourceFiles(input.root);
46645
47800
  const codeFacts = readCodeFacts(input.root, input.worktree.worktreeDigest, edgeLimit);
46646
47801
  const { snapshot, structure } = measureModuleStatistics({
@@ -46652,6 +47807,8 @@ function runRefactorScan(input) {
46652
47807
  workspacePackages: declaredWorkspacePackages(input.root),
46653
47808
  truncated: codeFacts.truncated,
46654
47809
  edgeLimit,
47810
+ importNodeCount: codeFacts.importNodeCount,
47811
+ importNodeLimitExceeded: codeFacts.importNodeLimitExceeded,
46655
47812
  codeFacts: {
46656
47813
  version: codeFacts.version,
46657
47814
  binaryDigest: codeFacts.binaryDigest,
@@ -46707,7 +47864,8 @@ function runRefactorScan(input) {
46707
47864
  maxEvidenceSampleLimit: REFACTOR_OBSERVATION_EVIDENCE_LIMIT,
46708
47865
  ...plan.candidateBudget
46709
47866
  },
46710
- trackedFileCount: trackedFiles.length
47867
+ trackedFileCount: trackedFiles.length,
47868
+ untrackedExcludedCount: countFilesOutsideHead(input.root)
46711
47869
  };
46712
47870
  }
46713
47871
  function evaluateReviewDependencyConstraints(input) {
@@ -46723,11 +47881,20 @@ function evaluateReviewDependencyConstraints(input) {
46723
47881
  codeFacts: { availability: "unavailable", indexedWorktreeDigest: null }
46724
47882
  });
46725
47883
  }
47884
+ const edgeLimit = input.edgeLimit ?? CODEGRAPH_IMPORT_NODE_READ_LIMIT;
46726
47885
  let imports;
46727
47886
  try {
46728
- imports = repositoryImportPairs(input.root, CODEGRAPH_BINARY, input.edgeLimit ?? CODEGRAPH_IMPORT_NODE_QUERY_LIMIT, input.worktreeDigest);
47887
+ imports = repositoryImportPairs(input.root, CODEGRAPH_BINARY, edgeLimit, input.worktreeDigest);
46729
47888
  } catch {
46730
- imports = { pairs: [], truncated: true, availability: "unavailable", indexedWorktreeDigest: null };
47889
+ imports = {
47890
+ pairs: [],
47891
+ truncated: true,
47892
+ availability: "unavailable",
47893
+ indexedWorktreeDigest: null,
47894
+ importNodeCount: null,
47895
+ importNodeLimit: edgeLimit,
47896
+ importNodeLimitExceeded: false
47897
+ };
46731
47898
  }
46732
47899
  const workspaces = reviewWorkspacePackages(input.root);
46733
47900
  return evaluateDependencyConstraints({
@@ -46737,6 +47904,7 @@ function evaluateReviewDependencyConstraints(input) {
46737
47904
  workspacePackages: workspaces.packages,
46738
47905
  workspacePackagesResolved: workspaces.resolved,
46739
47906
  truncated: imports.truncated,
47907
+ importNodeLimitExceeded: imports.importNodeLimitExceeded,
46740
47908
  codeFacts: { availability: imports.availability, indexedWorktreeDigest: imports.indexedWorktreeDigest }
46741
47909
  });
46742
47910
  }
@@ -46744,7 +47912,7 @@ function reviewWorkspacePackages(root) {
46744
47912
  if (!existsSync24(join16(root, "package.json")))
46745
47913
  return { packages: [], resolved: true };
46746
47914
  try {
46747
- const manifest = JSON.parse(readFileSync24(join16(root, "package.json"), "utf8"));
47915
+ const manifest = JSON.parse(readFileSync25(join16(root, "package.json"), "utf8"));
46748
47916
  const declared = Array.isArray(manifest.workspaces) ? manifest.workspaces : manifest.workspaces?.packages ?? [];
46749
47917
  if (!Array.isArray(declared))
46750
47918
  throw new Error("workspaces must be a list");
@@ -46757,10 +47925,10 @@ function reviewWorkspacePackages(root) {
46757
47925
  const parent = /^([^*?[\]{}!]+)\/\*$/.exec(pattern)?.[1];
46758
47926
  if (parent === undefined)
46759
47927
  throw new Error(`unsupported workspace pattern: ${entry}`);
46760
- return readdirSync11(join16(root, parent), { withFileTypes: true }).filter((child) => child.isDirectory() && existsSync24(join16(root, parent, child.name, "package.json"))).map((child) => `${parent}/${child.name}`);
47928
+ return readdirSync13(join16(root, parent), { withFileTypes: true }).filter((child) => child.isDirectory() && existsSync24(join16(root, parent, child.name, "package.json"))).map((child) => `${parent}/${child.name}`);
46761
47929
  });
46762
47930
  const packages = directories.flatMap((directory) => {
46763
- const packageManifest = JSON.parse(readFileSync24(join16(root, ...directory.split("/"), "package.json"), "utf8"));
47931
+ const packageManifest = JSON.parse(readFileSync25(join16(root, ...directory.split("/"), "package.json"), "utf8"));
46764
47932
  if (typeof packageManifest.name !== "string")
46765
47933
  return [];
46766
47934
  const exports = typeof packageManifest.exports === "string" ? { ".": packageManifest.exports } : Object.fromEntries(Object.entries(packageManifest.exports ?? {}).filter((entry) => typeof entry[1] === "string"));
@@ -46797,6 +47965,10 @@ function readWorktreeFiles(root) {
46797
47965
  }
46798
47966
  return [...new Set(output.split("\x00").filter(Boolean))].sort();
46799
47967
  }
47968
+ function countFilesOutsideHead(root) {
47969
+ const head = new Set(readTrackedTreeEntries(root).map((entry) => entry.path));
47970
+ return readWorktreeFiles(root).filter((path) => !head.has(path)).length;
47971
+ }
46800
47972
  function declaredWorkspacePackages(root) {
46801
47973
  return existsSync24(join16(root, "package.json")) ? readWorkspacePackages(root) : [];
46802
47974
  }
@@ -46827,6 +47999,8 @@ function readCodeFacts(root, worktreeDigest, edgeLimit) {
46827
47999
  return {
46828
48000
  pairs: imports.pairs,
46829
48001
  truncated: imports.truncated,
48002
+ importNodeCount: imports.importNodeCount,
48003
+ importNodeLimitExceeded: imports.importNodeLimitExceeded,
46830
48004
  availability: imports.availability,
46831
48005
  indexedWorktreeDigest: imports.indexedWorktreeDigest,
46832
48006
  ...attestation
@@ -46843,10 +48017,108 @@ function attestCodeGraphBinary(root) {
46843
48017
  const binaryPath = invocation.argsPrefix[0] ?? invocation.command;
46844
48018
  return {
46845
48019
  version,
46846
- binaryDigest: `sha256:${createHash10("sha256").update(readFileSync24(binaryPath)).digest("hex")}`
48020
+ binaryDigest: `sha256:${createHash10("sha256").update(readFileSync25(binaryPath)).digest("hex")}`
46847
48021
  };
46848
48022
  }
46849
48023
 
48024
+ // packages/local-runtime/runtime-daemon/src/capability-dependencies.ts
48025
+ var CODEGRAPH_BINARY2 = "codegraph";
48026
+
48027
+ class CapabilityDependenciesError extends Error {
48028
+ code;
48029
+ constructor(code, message) {
48030
+ super(message);
48031
+ this.code = code;
48032
+ this.name = "CapabilityDependenciesError";
48033
+ }
48034
+ }
48035
+ function readCapabilityDependencies(root, request = {}) {
48036
+ const importNodeLimit = request.importNodeLimit ?? CODEGRAPH_IMPORT_NODE_READ_LIMIT;
48037
+ for (const [name, value] of [["importNodeLimit", request.importNodeLimit], ["edgeLimit", request.edgeLimit]]) {
48038
+ if (value !== undefined && (!Number.isInteger(value) || value < (name === "edgeLimit" ? 0 : 1))) {
48039
+ throw new CapabilityDependenciesError("AC_SCHEMA_INVALID", `${name} must be a ${name === "edgeLimit" ? "non-negative" : "positive"} integer, got ${value}`);
48040
+ }
48041
+ }
48042
+ const model = loadModel2(root);
48043
+ const nodeIds = new Set(model.nodes.map((node) => node.id));
48044
+ for (const [flag, nodeId] of [["--from", request.from], ["--into", request.into]]) {
48045
+ if (nodeId !== undefined && !nodeIds.has(nodeId)) {
48046
+ throw new CapabilityDependenciesError("AC_SCHEMA_INVALID", `deps ${flag} names no node in the model: ${nodeId}`);
48047
+ }
48048
+ }
48049
+ const headSha = readHeadSha(root);
48050
+ const worktreeDigest = computeWorktreeDigest(root);
48051
+ const files = readTrackedTreeEntries(root).filter((entry) => entry.type === "blob").map((entry) => entry.path);
48052
+ const imports = readImports(root, importNodeLimit, worktreeDigest);
48053
+ const matrix = measureCapabilityDependencies({
48054
+ nodes: model.nodes,
48055
+ worktreeDigest,
48056
+ files,
48057
+ importEdges: imports.pairs,
48058
+ workspacePackages: readWorkspaces(root),
48059
+ truncated: imports.truncated,
48060
+ importNodeCount: imports.importNodeCount,
48061
+ importNodeLimit: imports.importNodeLimit,
48062
+ importNodeLimitExceeded: imports.importNodeLimitExceeded,
48063
+ codeFacts: { availability: imports.availability, indexedWorktreeDigest: imports.indexedWorktreeDigest },
48064
+ ...request.from === undefined ? {} : { from: request.from },
48065
+ ...request.into === undefined ? {} : { into: request.into },
48066
+ ...request.edgeLimit === undefined ? {} : { edgeLimit: request.edgeLimit }
48067
+ });
48068
+ return { ...matrix, worktree: { headSha, worktreeDigest }, privacy: { writes: "none" } };
48069
+ }
48070
+ function readWorkspaces(root) {
48071
+ try {
48072
+ return declaredWorkspacePackages(root);
48073
+ } catch (error) {
48074
+ throw new CapabilityDependenciesError("AC_SCHEMA_INVALID", `workspace packages are unreadable: ${error instanceof Error ? error.message : String(error)}`);
48075
+ }
48076
+ }
48077
+ function loadModel2(root) {
48078
+ try {
48079
+ return loadNativeModelFromArchContext(root);
48080
+ } catch (error) {
48081
+ throw new CapabilityDependenciesError("AC_MODEL_ADOPTION_REQUIRED", `architecture model is unreadable: ${error instanceof Error ? error.message : String(error)}`);
48082
+ }
48083
+ }
48084
+ function readImports(root, limit, worktreeDigest) {
48085
+ try {
48086
+ return repositoryImportPairs(root, CODEGRAPH_BINARY2, limit, worktreeDigest);
48087
+ } catch {
48088
+ return {
48089
+ pairs: [],
48090
+ truncated: true,
48091
+ availability: "unavailable",
48092
+ indexedWorktreeDigest: null,
48093
+ importNodeCount: null,
48094
+ importNodeLimit: limit,
48095
+ importNodeLimitExceeded: false
48096
+ };
48097
+ }
48098
+ }
48099
+ // packages/local-runtime/runtime-daemon/src/flow-selector-check.ts
48100
+ init_src();
48101
+ function runFlowSelectorCheckCommand(cwd, options = {}) {
48102
+ try {
48103
+ const root = findRepositoryRoot(cwd);
48104
+ const model = loadNativeModelFromArchContext(root);
48105
+ const sourceFiles = listProjectionSourceFiles(root);
48106
+ const prepared = prepareProjectionCodeFacts(root, model, {
48107
+ sourceTreeDigest: architectureDocumentationSourceTreeDigest(root, model, sourceFiles),
48108
+ sourceFiles,
48109
+ importGraphs: false,
48110
+ ...options.binary ? { binary: options.binary } : {}
48111
+ });
48112
+ assertProjectionCodeFactsAvailable(root, prepared);
48113
+ return okEnvelope("flows.check", checkArchitectureFlowSelectors({
48114
+ model,
48115
+ selectorEvidence: prepared.selectorEvidence,
48116
+ codeGraph: { availability: prepared.handshake.availability, version: prepared.handshake.actualVersion }
48117
+ }));
48118
+ } catch (error) {
48119
+ return projectionFailureEnvelope("flows.check", error);
48120
+ }
48121
+ }
46850
48122
  // packages/local-runtime/runtime-daemon/src/refactor-verify.ts
46851
48123
  init_src();
46852
48124
  var REFACTOR_RESOLUTION_EVENT_TYPE = "architecture.refactor.resolution";
@@ -46993,6 +48265,12 @@ function baselineSnapshotForRecommendation(evidenceState, recommendationId) {
46993
48265
  }
46994
48266
  return;
46995
48267
  }
48268
+ function baselineImportNodeLimit(snapshot) {
48269
+ if (snapshot === undefined || snapshot.codeFacts.importNodeCount === undefined)
48270
+ return;
48271
+ const { edgeLimit } = snapshot.codeFacts;
48272
+ return edgeLimit === null || edgeLimit === CODEGRAPH_IMPORT_NODE_READ_LIMIT ? undefined : edgeLimit;
48273
+ }
46996
48274
  function refactorVerifyIngressIssues(recommendation) {
46997
48275
  if (recommendation.category === "practice") {
46998
48276
  return [`recommendation ${recommendation.recommendationId} is a practice recommendation and carries no refactor outcome`];
@@ -47087,6 +48365,9 @@ class RecommendationsService {
47087
48365
  }
47088
48366
  });
47089
48367
  };
48368
+ if (input.importNodeLimit !== undefined && !(Number.isInteger(input.importNodeLimit) && input.importNodeLimit >= 1)) {
48369
+ return errorEnvelope(`recommendations.${command}`, "AC_SCHEMA_INVALID", `recommendations ${command} importNodeLimit must be a positive integer`);
48370
+ }
47090
48371
  if (command === "metrics")
47091
48372
  return readMetrics();
47092
48373
  if (command === "list")
@@ -47094,7 +48375,7 @@ class RecommendationsService {
47094
48375
  if (command === "show") {
47095
48376
  if (!input.recommendationId)
47096
48377
  return errorEnvelope("recommendations.show", "AC_SCHEMA_INVALID", "recommendations show requires --id");
47097
- return this.showRecommendation(repositoryRoot, input.recommendationId);
48378
+ return this.showRecommendation(repositoryRoot, input.recommendationId, input.importNodeLimit);
47098
48379
  }
47099
48380
  if (!isRecommendationLifecycleCliAction(command)) {
47100
48381
  return errorEnvelope("recommendations", "AC_SCHEMA_INVALID", "recommendations requires acknowledge|accept|reject|defer|waive|resolve|metrics|show|list");
@@ -47158,7 +48439,7 @@ class RecommendationsService {
47158
48439
  async decideScanCandidate(root, repositoryRoot, input, command, now) {
47159
48440
  const surface = `recommendations.${command}`;
47160
48441
  const recommendationId = input.recommendationId;
47161
- const measured = await this.measureRepository(repositoryRoot, REPOSITORY_REFACTOR_REQUEST, `recommendations ${command}`);
48442
+ const measured = await this.measureRepository(repositoryRoot, repositoryRefactorRequest(input.importNodeLimit), `recommendations ${command}`);
47162
48443
  if (!measured.ok) {
47163
48444
  return errorEnvelope(surface, measured.code, `recommendation ${recommendationId} is not recorded and the current refactor scan could not run: ${measured.message}`, measured.reasonCode);
47164
48445
  }
@@ -47239,7 +48520,7 @@ class RecommendationsService {
47239
48520
  }
47240
48521
  });
47241
48522
  }
47242
- async showRecommendation(repositoryRoot, recommendationId) {
48523
+ async showRecommendation(repositoryRoot, recommendationId, importNodeLimit) {
47243
48524
  const scope = await this.context.architectureLedgerScope(repositoryRoot);
47244
48525
  const replay = await this.context.localStore.replayArchitectureLedger({ ...scope, mode: "genesis" });
47245
48526
  const artifacts = recommendationArtifactsFromEvents(replay.events);
@@ -47257,7 +48538,7 @@ class RecommendationsService {
47257
48538
  worktree: null
47258
48539
  });
47259
48540
  }
47260
- const measured = await this.measureRepository(repositoryRoot, REPOSITORY_REFACTOR_REQUEST, "recommendations show");
48541
+ const measured = await this.measureRepository(repositoryRoot, repositoryRefactorRequest(importNodeLimit), "recommendations show");
47261
48542
  if (!measured.ok) {
47262
48543
  return errorEnvelope("recommendations.show", measured.code, `recommendation ${recommendationId} is not recorded and the current refactor scan could not run: ${measured.message}`, measured.reasonCode);
47263
48544
  }
@@ -47446,6 +48727,7 @@ class RecommendationsService {
47446
48727
  requestId: result.requestId,
47447
48728
  request,
47448
48729
  trackedFileCount: result.trackedFileCount,
48730
+ untrackedExcludedCount: result.untrackedExcludedCount,
47449
48731
  snapshot: result.snapshot,
47450
48732
  assessment: result.assessment,
47451
48733
  ...result.proposal ? { proposal: result.proposal } : {},
@@ -47612,11 +48894,13 @@ class RecommendationsService {
47612
48894
  graphDigest: replay.graphDigest
47613
48895
  });
47614
48896
  }
48897
+ const baseline = baselineSnapshotForRecommendation(replay.evidenceState, recommendation.recommendationId);
48898
+ const importNodeLimit = input.importNodeLimit ?? baselineImportNodeLimit(baseline?.snapshot);
47615
48899
  let scan;
47616
48900
  try {
47617
48901
  scan = runRefactorScan({
47618
48902
  root: repositoryRoot,
47619
- request: REPOSITORY_REFACTOR_REQUEST,
48903
+ request: repositoryRefactorRequest(importNodeLimit),
47620
48904
  repository: gitScope.repository,
47621
48905
  worktree: gitScope.worktree,
47622
48906
  previousRecommendations: artifacts.recommendations,
@@ -47627,7 +48911,6 @@ class RecommendationsService {
47627
48911
  return errorEnvelope("refactor.verify", error.code, error.message, error.reasonCode);
47628
48912
  return errorEnvelope("refactor.verify", "AC_SCHEMA_INVALID", error instanceof Error ? error.message : String(error));
47629
48913
  }
47630
- const baseline = baselineSnapshotForRecommendation(replay.evidenceState, recommendation.recommendationId);
47631
48914
  let plan;
47632
48915
  try {
47633
48916
  plan = runRefactorVerify({
@@ -47764,11 +49047,15 @@ function decodeRuntimeRefactorVerifyInput(value) {
47764
49047
  throw new RuntimeRefactorInputError(`refactor verify ${field} must be a non-empty string`);
47765
49048
  }
47766
49049
  }
49050
+ if (input.importNodeLimit !== undefined && typeof input.importNodeLimit !== "number") {
49051
+ throw new RuntimeRefactorInputError("refactor verify importNodeLimit must be a positive integer");
49052
+ }
47767
49053
  const request = {
47768
49054
  schemaVersion: REFACTOR_VERIFICATION_REQUEST_SCHEMA_VERSION,
47769
49055
  recommendationId: input.recommendationId,
47770
49056
  ...typeof input.expectedHeadSha === "string" ? { expectedHeadSha: input.expectedHeadSha } : {},
47771
49057
  ...typeof input.expectedWorktreeDigest === "string" ? { expectedWorktreeDigest: input.expectedWorktreeDigest } : {},
49058
+ ...typeof input.importNodeLimit === "number" ? { importNodeLimit: input.importNodeLimit } : {},
47772
49059
  ...input.executionEvidenceRefs === undefined ? {} : { executionEvidenceRefs: decodeRuntimeExecutionEvidenceRefs(input.executionEvidenceRefs) }
47773
49060
  };
47774
49061
  if (input.schemaVersion !== REFACTOR_VERIFICATION_REQUEST_SCHEMA_VERSION) {
@@ -47966,14 +49253,14 @@ function recommendationActorKind(input) {
47966
49253
  }
47967
49254
 
47968
49255
  // packages/local-runtime/runtime-daemon/src/changeset-authority.ts
47969
- import { existsSync as existsSync25, readFileSync as readFileSync26 } from "node:fs";
47970
- import { resolve as resolve26 } from "node:path";
49256
+ import { existsSync as existsSync25, readFileSync as readFileSync27 } from "node:fs";
49257
+ import { resolve as resolve27 } from "node:path";
47971
49258
  init_src();
47972
49259
 
47973
49260
  // packages/local-runtime/runtime-daemon/src/committed-change-acceptance.ts
47974
49261
  import { execFileSync as execFileSync10 } from "node:child_process";
47975
- import { closeSync as closeSync8, constants as fsConstants3, fstatSync as fstatSync3, lstatSync as lstatSync13, openSync as openSync9, readdirSync as readdirSync12, readFileSync as readFileSync25 } from "node:fs";
47976
- import { resolve as resolve25 } from "node:path";
49262
+ import { closeSync as closeSync8, constants as fsConstants3, fstatSync as fstatSync3, lstatSync as lstatSync13, openSync as openSync9, readdirSync as readdirSync14, readFileSync as readFileSync26 } from "node:fs";
49263
+ import { resolve as resolve26 } from "node:path";
47977
49264
  init_src();
47978
49265
  var SEMANTIC_MODEL_DIRECTORIES = ["nodes", "relations", "flows"];
47979
49266
  var SEMANTIC_MODEL_PATH = /^\.archcontext\/model\/(nodes|relations|flows)\/[^/]+\.ya?ml$/;
@@ -48030,7 +49317,7 @@ function readSemanticModelSnapshot(root, readEntry) {
48030
49317
  try {
48031
49318
  if (!lstatSync13(absolute).isDirectory())
48032
49319
  throw new Error(`semantic-model-directory-invalid: ${relativeDirectory}`);
48033
- entries = readdirSync12(absolute);
49320
+ entries = readdirSync14(absolute);
48034
49321
  } catch (error) {
48035
49322
  if (error.code === "ENOENT")
48036
49323
  continue;
@@ -48038,7 +49325,7 @@ function readSemanticModelSnapshot(root, readEntry) {
48038
49325
  }
48039
49326
  for (const entry of entries.filter((name) => /\.ya?ml$/.test(name)).sort()) {
48040
49327
  const path = `${relativeDirectory}/${entry}`;
48041
- bodies.set(path, strictUtf8(readEntry(resolve25(absolute, entry), path), path));
49328
+ bodies.set(path, strictUtf8(readEntry(resolve26(absolute, entry), path), path));
48042
49329
  }
48043
49330
  }
48044
49331
  const hashes = new Map([...bodies].map(([path, body]) => [path, digestJson({ body })]));
@@ -48051,7 +49338,7 @@ function readRegularFileBytes(absolute, path) {
48051
49338
  try {
48052
49339
  if (!fstatSync3(fd).isFile())
48053
49340
  throw new Error(`semantic-model-entry-not-regular: ${path}`);
48054
- return readFileSync25(fd);
49341
+ return readFileSync26(fd);
48055
49342
  } finally {
48056
49343
  closeSync8(fd);
48057
49344
  }
@@ -48401,6 +49688,40 @@ function sameStrings(left, right) {
48401
49688
  return left.length === right.length && left.every((value, index) => value === right[index]);
48402
49689
  }
48403
49690
 
49691
+ // packages/local-runtime/runtime-daemon/src/ownership-delta-preview.ts
49692
+ init_src();
49693
+ var NODE_DIRECTORY = ".archcontext/model/nodes/";
49694
+ var NODE_FILE_OPERATIONS = new Set(["create_entity", "update_entity_fields", "delete_entity"]);
49695
+ function planOwnershipDelta(root, draft) {
49696
+ const nodeOperations = draft.operations.filter((operation) => NODE_FILE_OPERATIONS.has(operation.op) && operation.path !== undefined && isNodeFile(operation.path));
49697
+ if (nodeOperations.length === 0)
49698
+ return;
49699
+ try {
49700
+ const current = new Map(listModelFiles(root).filter((file) => isNodeFile(file.path)).map((file) => [file.path, file.body]));
49701
+ const planned = new Map(current);
49702
+ for (const operation of nodeOperations) {
49703
+ if (operation.op === "delete_entity")
49704
+ planned.delete(operation.path);
49705
+ else
49706
+ planned.set(operation.path, operation.body ?? "");
49707
+ }
49708
+ const paths = readTrackedTreeEntries(root).filter((entry) => entry.type === "blob").map((entry) => entry.path);
49709
+ return ownershipDelta(loadNativeModelFromModelFiles(current).nodes, loadNativeModelFromModelFiles(planned).nodes, paths);
49710
+ } catch (error) {
49711
+ return {
49712
+ schemaVersion: CHANGESET_OWNERSHIP_DELTA_SCHEMA_VERSION,
49713
+ status: "unavailable",
49714
+ reason: error instanceof Error ? error.message : String(error)
49715
+ };
49716
+ }
49717
+ }
49718
+ function isNodeFile(path) {
49719
+ if (!path.startsWith(NODE_DIRECTORY))
49720
+ return false;
49721
+ const name = path.slice(NODE_DIRECTORY.length);
49722
+ return !name.includes("/") && /\.ya?ml$/.test(name);
49723
+ }
49724
+
48404
49725
  // packages/local-runtime/runtime-daemon/src/changeset-authority.ts
48405
49726
  class RuntimeUpdateInputError extends Error {
48406
49727
  }
@@ -48516,10 +49837,10 @@ class ChangeSetAuthorityService {
48516
49837
  return errorEnvelope("practices.waive", "AC_SCHEMA_INVALID", error instanceof Error ? error.message : String(error));
48517
49838
  }
48518
49839
  const path = `.archcontext/waivers/${waiverId}.json`;
48519
- const absolute = resolve26(session.workspace.root, path);
49840
+ const absolute = resolve27(session.workspace.root, path);
48520
49841
  const body = `${JSON.stringify(waiver, null, 2)}
48521
49842
  `;
48522
- const expectedHash = existsSync25(absolute) ? digestJson({ body: readFileSync26(absolute, "utf8") }) : "missing";
49843
+ const expectedHash = existsSync25(absolute) ? digestJson({ body: readFileSync27(absolute, "utf8") }) : "missing";
48523
49844
  const draft = this.changeSetEngine.plan({
48524
49845
  id: input.id ?? `changeset.practice-waiver-${waiverId.replace(/[^A-Za-z0-9_-]/g, "-")}`,
48525
49846
  base: {
@@ -48574,10 +49895,14 @@ class ChangeSetAuthorityService {
48574
49895
  this.changesets.set(draft.id, draft);
48575
49896
  this.changeSetRoots.set(draft.id, canonicalRepositoryRoot(root));
48576
49897
  this.changeSetWorktreeDigestProfiles.set(draft.id, worktreeDigestProfile);
49898
+ const ownershipDelta = planOwnershipDelta(root, draft);
48577
49899
  return okEnvelope("plan_update", {
48578
49900
  draft,
48579
49901
  changeSetDigest: digestJson(draft),
48580
- preview: this.changeSetEngine.preview(root, draft)
49902
+ preview: {
49903
+ ...this.changeSetEngine.preview(root, draft),
49904
+ ...ownershipDelta === undefined ? {} : { ownershipDelta }
49905
+ }
48581
49906
  });
48582
49907
  }
48583
49908
  async mcpProjection(root, input, approved) {
@@ -48876,7 +50201,7 @@ function architectureLedgerWriteAppendsEvents(mode) {
48876
50201
  // packages/local-runtime/runtime-daemon/src/rpc-server.ts
48877
50202
  init_src();
48878
50203
  import { createServer as createServer2 } from "node:http";
48879
- import { closeSync as closeSync9, mkdirSync as mkdirSync12, rmSync as rmSync15 } from "node:fs";
50204
+ import { closeSync as closeSync9, existsSync as existsSync26, mkdirSync as mkdirSync12 } from "node:fs";
48880
50205
  import { dirname as dirname14 } from "node:path";
48881
50206
  import { randomBytes as randomBytes5 } from "node:crypto";
48882
50207
  var DEFAULT_DAEMON_IDLE_TIMEOUT_MS = 30 * 60000;
@@ -48904,6 +50229,8 @@ class ArchctxRuntimeRpcServer {
48904
50229
  idleTimeoutMs;
48905
50230
  idleTimer;
48906
50231
  inFlightRpcRequests = 0;
50232
+ authorizedRequests = 0;
50233
+ idleExitPending = false;
48907
50234
  constructor(daemon, options = {}) {
48908
50235
  this.daemon = daemon;
48909
50236
  this.options = options;
@@ -48930,11 +50257,11 @@ class ArchctxRuntimeRpcServer {
48930
50257
  if (server)
48931
50258
  await new Promise((resolveClose) => server.close(() => resolveClose()));
48932
50259
  if (connection)
48933
- rmSync15(connection.connectionPath, { force: true });
50260
+ await removePrivateControlFile(connection.connectionPath);
48934
50261
  if (this.lockFd !== undefined) {
48935
50262
  closeSync9(this.lockFd);
48936
50263
  this.lockFd = undefined;
48937
- rmSync15(this.options.lockPath ?? defaultDaemonLockPath(this.options.root ?? process.cwd()), { force: true });
50264
+ await removePrivateControlFile(this.options.lockPath ?? defaultDaemonLockPath(this.options.root ?? process.cwd()));
48938
50265
  }
48939
50266
  if (startedDaemon)
48940
50267
  await this.daemon.stop().catch(() => {
@@ -48992,13 +50319,16 @@ class ArchctxRuntimeRpcServer {
48992
50319
  });
48993
50320
  }
48994
50321
  await this.daemon.stop();
50322
+ try {
50323
+ if (connection)
50324
+ await removePrivateControlFile(connection.connectionPath);
50325
+ } finally {
50326
+ if (this.lockFd !== undefined)
50327
+ closeSync9(this.lockFd);
50328
+ this.lockFd = undefined;
50329
+ }
48995
50330
  if (connection)
48996
- rmSync15(connection.connectionPath, { force: true });
48997
- if (this.lockFd !== undefined)
48998
- closeSync9(this.lockFd);
48999
- this.lockFd = undefined;
49000
- if (connection)
49001
- rmSync15(connection.lockPath, { force: true });
50331
+ await removePrivateControlFile(connection.lockPath);
49002
50332
  this.options.onStop?.();
49003
50333
  }
49004
50334
  armIdleTimer() {
@@ -49015,21 +50345,44 @@ class ArchctxRuntimeRpcServer {
49015
50345
  this.idleTimer = undefined;
49016
50346
  }
49017
50347
  async checkIdleAndMaybeExit() {
49018
- if (!this.server || !this.connection)
50348
+ if (!this.server || !this.connection || this.idleExitPending)
49019
50349
  return;
49020
50350
  if (this.inFlightRpcRequests > 0) {
49021
50351
  this.armIdleTimer();
49022
50352
  return;
49023
50353
  }
50354
+ this.idleExitPending = true;
49024
50355
  const hasActiveWork = await this.daemon.hasActiveBackgroundWork().catch(() => true);
49025
50356
  if (hasActiveWork || this.inFlightRpcRequests > 0 || !this.connection) {
50357
+ this.idleExitPending = false;
49026
50358
  this.armIdleTimer();
49027
50359
  return;
49028
50360
  }
49029
- rmSync15(this.connection.connectionPath, { force: true });
50361
+ const connection = this.connection;
50362
+ const requestsBeforeRemoval = this.authorizedRequests;
50363
+ let cleanupError;
50364
+ await removePrivateControlFile(connection.connectionPath).catch((error) => {
50365
+ cleanupError = error;
50366
+ });
50367
+ if (this.server && this.connection && (this.inFlightRpcRequests > 0 || this.authorizedRequests !== requestsBeforeRemoval)) {
50368
+ try {
50369
+ if (!existsSync26(connection.connectionPath))
50370
+ createPrivateControlFile(connection.connectionPath, JSON.stringify(connection, null, 2));
50371
+ this.idleExitPending = false;
50372
+ this.armIdleTimer();
50373
+ return;
50374
+ } catch (error) {
50375
+ cleanupError ??= error;
50376
+ }
50377
+ }
49030
50378
  try {
49031
50379
  await this.stop();
50380
+ } catch (error) {
50381
+ cleanupError ??= error;
49032
50382
  } finally {
50383
+ if (cleanupError)
50384
+ process.stderr.write(`archctxd idle exit cleanup failed: ${cleanupError.code ?? "unknown"}
50385
+ `);
49033
50386
  (this.options.exit ?? process.exit)(0);
49034
50387
  }
49035
50388
  }
@@ -49059,6 +50412,7 @@ class ArchctxRuntimeRpcServer {
49059
50412
  writeJson(response, 401, { schemaVersion: RUNTIME_RPC_VERSION, ok: false, error: "runtime RPC token required" });
49060
50413
  return;
49061
50414
  }
50415
+ this.authorizedRequests += 1;
49062
50416
  const changeSetRecovery = this.daemon.status().changeSetRecovery;
49063
50417
  writeJson(response, 200, {
49064
50418
  schemaVersion: RUNTIME_RPC_VERSION,
@@ -49082,6 +50436,7 @@ class ArchctxRuntimeRpcServer {
49082
50436
  return;
49083
50437
  }
49084
50438
  this.inFlightRpcRequests += 1;
50439
+ this.authorizedRequests += 1;
49085
50440
  try {
49086
50441
  const read = await readRpcRequestBody(request, {
49087
50442
  maxBytes: this.options.maxRequestBodyBytes ?? RUNTIME_RPC_MAX_REQUEST_BODY_BYTES,
@@ -49111,8 +50466,12 @@ class ArchctxRuntimeRpcServer {
49111
50466
  throw error;
49112
50467
  });
49113
50468
  writeJson(response, 200, result);
49114
- if (body.method === "shutdown")
49115
- setTimeout(() => void this.stop(), 0);
50469
+ if (body.method === "shutdown") {
50470
+ setTimeout(() => void this.stop().catch((error) => {
50471
+ process.stderr.write(`archctxd shutdown cleanup failed: ${error.code ?? "unknown"}
50472
+ `);
50473
+ }), 0);
50474
+ }
49116
50475
  } finally {
49117
50476
  this.inFlightRpcRequests -= 1;
49118
50477
  this.armIdleTimer();
@@ -49214,8 +50573,8 @@ async function readRpcRequestBody(request, limits) {
49214
50573
  }
49215
50574
 
49216
50575
  // packages/local-runtime/runtime-daemon/src/index.ts
49217
- import { existsSync as existsSync26 } from "node:fs";
49218
- import { resolve as resolve27 } from "node:path";
50576
+ import { existsSync as existsSync27 } from "node:fs";
50577
+ import { resolve as resolve28 } from "node:path";
49219
50578
  init_src();
49220
50579
 
49221
50580
  // packages/local-runtime/runtime-daemon/src/egress.ts
@@ -50154,7 +51513,7 @@ class ArchctxDaemon {
50154
51513
  }
50155
51514
  async restoreRepositorySessions() {
50156
51515
  for (const record of await this.localStore.listRepositorySessions()) {
50157
- if (!record.root || !existsSync26(record.root))
51516
+ if (!record.root || !existsSync27(record.root))
50158
51517
  continue;
50159
51518
  if (repositoryFingerprint(record.root) !== record.repositoryId)
50160
51519
  continue;
@@ -50419,7 +51778,7 @@ function blockedProductionInjections(deps) {
50419
51778
  ].filter((key) => (key in deps));
50420
51779
  }
50421
51780
  function completeTaskProjectionDrift(root) {
50422
- if (!existsSync26(resolve27(root, "docs/architecture/.projection-manifest.json")))
51781
+ if (!existsSync27(resolve28(root, "docs/architecture/.projection-manifest.json")))
50423
51782
  return;
50424
51783
  const profile = loadArchitectureDocumentationProfile(root);
50425
51784
  const loaded = loadArchitectureDocumentationInputs(root, profile);
@@ -50436,6 +51795,7 @@ function completeTaskProjectionDrift(root) {
50436
51795
  profile,
50437
51796
  decisions: loaded.decisions,
50438
51797
  existingFiles: loaded.existingFiles,
51798
+ docsLanguage: loaded.docsLanguage,
50439
51799
  sourceFootprints: loadCapabilitySourceFootprintDigests(root, loaded.model, codeGraphInputs.sourceFiles),
50440
51800
  sourceScaleSignals: loadCapabilitySourceScaleSignals(root, loaded.model, codeGraphInputs.sourceFiles),
50441
51801
  importGraphs: codeGraphInputs.importGraphs,
@@ -50691,7 +52051,7 @@ var LOCAL_MCP_TOOLS = [
50691
52051
  {
50692
52052
  name: "archcontext_plan_update",
50693
52053
  inputSchema: MCP_TOOL_INPUT_SCHEMAS.archcontext_plan_update,
50694
- description: "Create a ChangeSet draft and preview for architecture model updates. Does not write files.",
52054
+ description: "Create a ChangeSet draft and preview for architecture model updates. When the plan writes a node file, preview.ownershipDelta lists the files that become owned, change owner, become contested or become unowned. Does not write files.",
50695
52055
  annotations: { safety: "idempotent", requiresConfirmation: false }
50696
52056
  },
50697
52057
  {
@@ -51134,6 +52494,9 @@ class RuntimeVersionUnsupportedError extends Error {
51134
52494
 
51135
52495
  class RefactorFlagError extends Error {
51136
52496
  }
52497
+
52498
+ class CliUsageError extends Error {
52499
+ }
51137
52500
  var AUDIT_RUN_STATUSES = ["pending", "issuing", "issued", "failed"];
51138
52501
  var AUDIT_GLOBAL_VALUE_FLAGS = ["--format"];
51139
52502
  var AUDIT_GLOBAL_BOOLEAN_FLAGS = ["--json"];
@@ -51141,6 +52504,17 @@ var AUDIT_RUN_VALUE_FLAGS = ["--task-session-id", "--reason", "--risk", "--uncer
51141
52504
  var AUDIT_RUN_BOOLEAN_FLAGS = ["--no-wait", ...AUDIT_GLOBAL_BOOLEAN_FLAGS];
51142
52505
  var AUDIT_APPROVE_VALUE_FLAGS = ["--run-id", "--confirm-public-repo", ...AUDIT_GLOBAL_VALUE_FLAGS];
51143
52506
  var AUDIT_APPROVE_BOOLEAN_FLAGS = ["--resume", ...AUDIT_GLOBAL_BOOLEAN_FLAGS];
52507
+ var CLI_DOCS_ANCHORS_HELP = {
52508
+ usage: "archctx docs anchors [--profile default|repo-harness/v1] [--json]",
52509
+ description: "Read-only check of citations in human-owned module document regions and node summary/responsibilities. It reports missing-path, missing-symbol, line-out-of-range and line-moved per node. It is a signal, not a gate: the result stays ok when it has findings.",
52510
+ options: {
52511
+ "--profile <profile>": "Layout whose module documents are read (default: the committed manifest profile; default without a manifest).",
52512
+ "--json": "Accepted for symmetry; output is always JSON.",
52513
+ "--help, -h": "Show help without reading the repository."
52514
+ },
52515
+ writes: "none; no daemon, no local store. Without a CodeGraph index, path and line checks still run and symbol checks are reported as unchecked (codegraph-unavailable).",
52516
+ capability: "docs-anchors-v1"
52517
+ };
51144
52518
  function refactorFlagValue(args, flag) {
51145
52519
  const index = args.indexOf(flag);
51146
52520
  if (index === -1)
@@ -51153,39 +52527,6 @@ function refactorFlagValueAt(args, index, flag) {
51153
52527
  throw new RefactorFlagError(`refactor ${flag} requires a value`);
51154
52528
  return value;
51155
52529
  }
51156
- if (__require.main == __require.module) {
51157
- if (command === "capabilities") {
51158
- const result = await runCli(command, args, process.cwd());
51159
- if ("ok" in result) {
51160
- process.stdout.write(`${renderResult(result, "json")}
51161
- `);
51162
- if (result.ok === false)
51163
- process.exitCode = 1;
51164
- } else {
51165
- process.stdout.write(`${JSON.stringify(result, null, 2)}
51166
- `);
51167
- }
51168
- } else if (command === "mcp" && args.length === 0) {
51169
- await runStdioMcpLoop(stdinLines(), (line) => process.stdout.write(`${line}
51170
- `), (line) => process.stderr.write(`${line}
51171
- `), { runtimeResolver: (root) => createOrStartRuntimeRpcClient(root) });
51172
- } else if (command === "daemon" && args[0] === "start" && args.includes("--foreground")) {
51173
- await runForegroundDaemon(process.cwd(), args).catch((error) => {
51174
- const message = error instanceof Error ? error.message : String(error);
51175
- process.stderr.write(`${message}
51176
- `);
51177
- process.exitCode = isDaemonOwnedElsewhereError(message) ? DAEMON_OWNED_ELSEWHERE_EXIT_CODE : 1;
51178
- });
51179
- } else {
51180
- const result = await runCli(command, args, process.cwd()).catch((error) => errorEnvelope("cli", "AC_RUNTIME_UNAVAILABLE", error instanceof Error ? error.message : String(error)));
51181
- process.stdout.write(`${renderResult(result, readFlag(args, "--format") ?? "json")}
51182
- `);
51183
- if (result.ok === false)
51184
- process.exitCode = 1;
51185
- if (command === "resolve")
51186
- process.exitCode = resolveCommandExitCode(result);
51187
- }
51188
- }
51189
52530
  async function* stdinLines() {
51190
52531
  let buffer = "";
51191
52532
  for await (const chunk of process.stdin) {
@@ -51271,11 +52612,13 @@ async function runCliUnchecked(command = "help", args = [], cwd, deps = {}) {
51271
52612
  return (await runtime()).runtimeStatus(cwd);
51272
52613
  case "projection":
51273
52614
  return await runProjectionProtocolCommand2(args, cwd, runtime);
52615
+ case "flows":
52616
+ return runFlowsCommand(args, cwd);
51274
52617
  case "repo": {
51275
52618
  const subcommand = args[0] ?? "list";
51276
52619
  if (subcommand === "add") {
51277
52620
  const root = readFlag(args, "--root") ?? cwd;
51278
- if (resolve28(root) !== resolve28(cwd)) {
52621
+ if (resolve29(root) !== resolve29(cwd)) {
51279
52622
  return errorEnvelope("repo.add", "AC_CAPABILITY_UNSUPPORTED", "Multi-repo architecture context is outside the Local Core MVP; run archctx inside one Git repository.");
51280
52623
  }
51281
52624
  const daemon = await runtime();
@@ -51359,6 +52702,8 @@ async function runCliUnchecked(command = "help", args = [], cwd, deps = {}) {
51359
52702
  return result.ok ? { ...result, data: paginate(result.data, args) } : result;
51360
52703
  }
51361
52704
  case "docs":
52705
+ if (args[0] === "anchors")
52706
+ return runDocsAnchorsCliCommand(args.slice(1), cwd);
51362
52707
  return await runDocsCommand(args, cwd, await runtime());
51363
52708
  case "agent-context":
51364
52709
  return await runAgentContextProjectionCommand2(args, cwd, await runtime());
@@ -51517,36 +52862,30 @@ async function runCliUnchecked(command = "help", args = [], cwd, deps = {}) {
51517
52862
  revocation: "archctx tunnel --revoke"
51518
52863
  }
51519
52864
  };
52865
+ case "deps":
52866
+ return runDepsCommand(args, cwd);
51520
52867
  case "resolve": {
52868
+ if (args.includes("--help") || args.includes("-h"))
52869
+ return okEnvelope("resolve", CLI_RESOLVE_HELP);
52870
+ if (args.includes("--path") && args.includes("--paths-from")) {
52871
+ return errorEnvelope("resolve", "AC_SCHEMA_INVALID", "resolve accepts exactly one of --path or --paths-from");
52872
+ }
52873
+ if (args.includes("--paths-from")) {
52874
+ const read = await readResolvePathsFrom(args, cwd, deps);
52875
+ if ("problem" in read)
52876
+ return errorEnvelope("resolve", "AC_SCHEMA_INVALID", read.problem);
52877
+ const model = loadNativeModelFromArchContext(cwd);
52878
+ const outcomes = resolveArchitectureOwnersForPaths(model.nodes, read.paths);
52879
+ return okEnvelope("resolve", { results: outcomes.map((outcome, index) => resolveResultData(read.paths[index], outcome)) });
52880
+ }
51521
52881
  const path = readFlag(args, "--path");
51522
52882
  if (!path)
51523
- return errorEnvelope("resolve", "AC_SCHEMA_INVALID", "resolve requires --path");
52883
+ return errorEnvelope("resolve", "AC_SCHEMA_INVALID", "resolve requires --path or --paths-from");
51524
52884
  if (!isRepoRelativePosixPath(path)) {
51525
52885
  return errorEnvelope("resolve", "AC_SCHEMA_INVALID", "resolve --path must be a repository-relative POSIX path");
51526
52886
  }
51527
52887
  const model = loadNativeModelFromArchContext(cwd);
51528
- const outcome = resolveArchitectureOwnerForPath(model.nodes, path);
51529
- if (outcome.status === "matched") {
51530
- const node = outcome.node;
51531
- return okEnvelope("resolve", {
51532
- matched: true,
51533
- ambiguous: false,
51534
- stableId: node.id,
51535
- kind: node.kind,
51536
- name: node.name,
51537
- ...node.source ? { source: node.source } : {},
51538
- ...node.extensions ? { extensions: node.extensions } : {}
51539
- });
51540
- }
51541
- if (outcome.status === "ambiguous") {
51542
- return okEnvelope("resolve", {
51543
- matched: false,
51544
- ambiguous: true,
51545
- path,
51546
- candidates: outcome.candidates.map((node) => node.id)
51547
- });
51548
- }
51549
- return okEnvelope("resolve", { matched: false, ambiguous: false, path });
52888
+ return okEnvelope("resolve", resolveResultData(path, resolveArchitectureOwnerForPath(model.nodes, path)));
51550
52889
  }
51551
52890
  case "help":
51552
52891
  default:
@@ -51555,8 +52894,8 @@ async function runCliUnchecked(command = "help", args = [], cwd, deps = {}) {
51555
52894
  ok: true,
51556
52895
  requestId: "help",
51557
52896
  data: {
51558
- commands: ["capabilities", "projection", "init", "sync", "validate", "context", "status", "daemon", "state", "repo", "landscape", "ledger", "book", "recommendations", "refactor", "explore", "prepare", "practices", "checkpoint", "hook", "hooks", "investigate", "agents", "jobs", "audit", "plan", "apply", "hash", "review", "complete", "github", "manifest", "config", "mcp", "install", "uninstall", "doctor", "update", "paths", "privacy-audit", "export", "import", "resolve", "tunnel"],
51559
- examples: ["archctx init --name MyApp", "archctx projection run --request-json '{...}'", "archctx plan --id changeset.<id> --op update_entity_fields --path .archcontext/model/nodes/<node>.yaml --expected-hash sha256:<64-hex> --body '<complete YAML>'", "archctx plan --id changeset.<id> --op delete_entity --path .archcontext/model/flows/<flow>.yaml --expected-hash sha256:<64-hex>", "archctx hash --path .archcontext/model/nodes/<node>.yaml", "archctx plan --id changeset.<id> --operations-file <operations.json>", "archctx plan --id changeset.<id> --op update_entity_fields --path .archcontext/model/nodes/<node>.yaml --expected-hash sha256:<64-hex> --body-file <entity.yaml>", "archctx apply --id changeset.<id> --approved --expected-worktree-digest <draft.base.worktreeDigest>", "archctx state recover --from-git", "archctx ledger migrate --from-yaml --dry-run", "archctx ledger promote --mode authoritative --preflight --rollback-plan", "archctx book recommendations --open --explain", "archctx recommendations accept --id recommendation.<id> --reason 'Accepted after local readback.'", "archctx recommendations metrics", "archctx recommendations list --status rejected", "archctx refactor scan --json", "archctx refactor verify --request-json '{...}' --json", "archctx practices validate --strict", "archctx practices list --json", "archctx practices waivers", "archctx practices waive --practice-id modularity.no-new-cycle --owner team-architecture --reason 'External migration window requires this edge until cutover.' --review-at 2026-07-10T00:00:00.000Z --expires-at 2026-07-24T00:00:00.000Z --evidence-digest sha256:<64-hex> --subject module.a->module.b", "archctx checkpoint --task-session-id task_cli", "archctx investigate --runner-port codex", "archctx agents status --status queued,running", "archctx agents budget", "archctx hook enqueue --event post-edit --path src/app.ts", "archctx jobs list --status queued", "archctx audit consent", "archctx audit consent --revoke", "archctx audit run --reason 'quarterly architecture audit'", "archctx audit run --no-wait", "archctx audit list --status pending", "archctx audit show audit_run.<id>", "archctx audit approve audit_run.<id>", "archctx audit approve audit_run.<id> --confirm-public-repo public:<host>/<owner>/<repo>:<baseSha>:<runId>", "archctx audit approve audit_run.<id> --resume", "archctx hooks install --host codex", "archctx paths", "archctx update --check", "archctx doctor --check-updates", "archctx github connect", "archctx github status", "archctx daemon start", "archctx explore start --foreground", "archctx export likec4", "archctx import structurizr --content '<json>'", "archctx resolve --path packages/core/projection-engine/src/index.ts", "archctx tunnel"]
52897
+ commands: ["capabilities", "projection", "init", "sync", "validate", "context", "status", "daemon", "state", "repo", "landscape", "ledger", "book", "recommendations", "refactor", "explore", "prepare", "practices", "checkpoint", "hook", "hooks", "investigate", "agents", "jobs", "audit", "plan", "apply", "hash", "review", "complete", "github", "manifest", "config", "mcp", "install", "uninstall", "doctor", "update", "paths", "privacy-audit", "export", "import", "resolve", "tunnel", "flows", "deps"],
52898
+ examples: ["archctx init --name MyApp", "archctx projection run --request-json '{...}'", "archctx plan --id changeset.<id> --op update_entity_fields --path .archcontext/model/nodes/<node>.yaml --expected-hash sha256:<64-hex> --body '<complete YAML>'", "archctx plan --id changeset.<id> --op delete_entity --path .archcontext/model/flows/<flow>.yaml --expected-hash sha256:<64-hex>", "archctx hash --path .archcontext/model/nodes/<node>.yaml", "archctx plan --id changeset.<id> --operations-file <operations.json>", "archctx plan --id changeset.<id> --op update_entity_fields --path .archcontext/model/nodes/<node>.yaml --expected-hash sha256:<64-hex> --body-file <entity.yaml>", "archctx apply --id changeset.<id> --approved --expected-worktree-digest <draft.base.worktreeDigest>", "archctx state recover --from-git", "archctx ledger migrate --from-yaml --dry-run", "archctx ledger promote --mode authoritative --preflight --rollback-plan", "archctx book recommendations --open --explain", "archctx recommendations accept --id recommendation.<id> --reason 'Accepted after local readback.'", "archctx recommendations metrics", "archctx recommendations list --status rejected", "archctx refactor scan --json", "archctx refactor verify --request-json '{...}' --json", "archctx practices validate --strict", "archctx practices list --json", "archctx practices waivers", "archctx practices waive --practice-id modularity.no-new-cycle --owner team-architecture --reason 'External migration window requires this edge until cutover.' --review-at 2026-07-10T00:00:00.000Z --expires-at 2026-07-24T00:00:00.000Z --evidence-digest sha256:<64-hex> --subject module.a->module.b", "archctx checkpoint --task-session-id task_cli", "archctx investigate --runner-port codex", "archctx agents status --status queued,running", "archctx agents budget", "archctx hook enqueue --event post-edit --path src/app.ts", "archctx jobs list --status queued", "archctx audit consent", "archctx audit consent --revoke", "archctx audit run --reason 'quarterly architecture audit'", "archctx audit run --no-wait", "archctx audit list --status pending", "archctx audit show audit_run.<id>", "archctx audit approve audit_run.<id>", "archctx audit approve audit_run.<id> --confirm-public-repo public:<host>/<owner>/<repo>:<baseSha>:<runId>", "archctx audit approve audit_run.<id> --resume", "archctx hooks install --host codex", "archctx paths", "archctx update --check", "archctx doctor --check-updates", "archctx github connect", "archctx github status", "archctx daemon start", "archctx explore start --foreground", "archctx docs anchors --json", "archctx export likec4", "archctx import structurizr --content '<json>'", "archctx resolve --path packages/core/projection-engine/src/index.ts", "archctx tunnel", "archctx refactor scan --import-node-limit 100000 --json", "archctx resolve --paths-from changed-paths.txt", "archctx flows check --json", "archctx recommendations defer --id recommendation.<id> --import-node-limit 100000 --reason 'Scanned with the same limit.'", "archctx deps --json", "archctx deps --into capability.<id> --json"]
51560
52899
  }
51561
52900
  };
51562
52901
  }
@@ -51965,7 +53304,10 @@ async function runRecommendationsCommand(args, cwd, daemon) {
51965
53304
  if (!recommendationId || recommendationId.startsWith("--")) {
51966
53305
  return errorEnvelope("recommendations.show", "AC_SCHEMA_INVALID", "recommendations show requires --id");
51967
53306
  }
51968
- return daemon.recommendations(cwd, { command: "show", recommendationId });
53307
+ const importNodeLimit = recommendationsImportNodeLimit(args);
53308
+ if (importNodeLimit === null)
53309
+ return recommendationsImportNodeLimitError("show");
53310
+ return daemon.recommendations(cwd, { command: "show", recommendationId, ...importNodeLimit === undefined ? {} : { importNodeLimit } });
51969
53311
  }
51970
53312
  if (subcommand === "list") {
51971
53313
  const status = readFlag(args, "--status");
@@ -51981,6 +53323,9 @@ async function runRecommendationsCommand(args, cwd, daemon) {
51981
53323
  const reason = readFlag(args, "--reason");
51982
53324
  if (!reason)
51983
53325
  return errorEnvelope(`recommendations.${subcommand}`, "AC_SCHEMA_INVALID", `recommendations ${subcommand} requires --reason`);
53326
+ const importNodeLimit = recommendationsImportNodeLimit(args);
53327
+ if (importNodeLimit === null)
53328
+ return recommendationsImportNodeLimitError(subcommand);
51984
53329
  const input = {
51985
53330
  command: subcommand,
51986
53331
  recommendationId,
@@ -51990,11 +53335,25 @@ async function runRecommendationsCommand(args, cwd, daemon) {
51990
53335
  ...readFlag(args, "--source") === undefined ? {} : { source: readFlag(args, "--source") },
51991
53336
  ...readFlag(args, "--expected-worktree-digest") === undefined ? {} : { expectedWorktreeDigest: readFlag(args, "--expected-worktree-digest") },
51992
53337
  ...readFlag(args, "--evidence-digest") === undefined ? {} : { evidenceDigest: readFlag(args, "--evidence-digest") },
53338
+ ...importNodeLimit === undefined ? {} : { importNodeLimit },
51993
53339
  ...readFlag(args, "--agent-job-id") === undefined ? {} : { agentJobId: readFlag(args, "--agent-job-id") },
51994
53340
  ...readFlag(args, "--now") === undefined ? {} : { now: readFlag(args, "--now") }
51995
53341
  };
51996
53342
  return daemon.recommendations(cwd, input);
51997
53343
  }
53344
+ function recommendationsImportNodeLimit(args) {
53345
+ const index = args.indexOf("--import-node-limit");
53346
+ if (index === -1)
53347
+ return;
53348
+ const raw = args[index + 1];
53349
+ return raw === undefined ? null : importNodeLimitValue(raw) ?? null;
53350
+ }
53351
+ function recommendationsImportNodeLimitError(subcommand) {
53352
+ return errorEnvelope(`recommendations.${subcommand}`, "AC_SCHEMA_INVALID", `recommendations ${subcommand} --import-node-limit must be a positive integer`);
53353
+ }
53354
+ function importNodeLimitValue(raw) {
53355
+ return /^[0-9]+$/.test(raw) && Number(raw) >= 1 ? Number(raw) : undefined;
53356
+ }
51998
53357
  async function runRefactorCommand(args, cwd, runtime) {
51999
53358
  const subcommand = args[0] ?? "scan";
52000
53359
  if (subcommand !== "scan" && subcommand !== "record" && subcommand !== "verify") {
@@ -52004,17 +53363,27 @@ async function runRefactorCommand(args, cwd, runtime) {
52004
53363
  try {
52005
53364
  if (subcommand === "scan") {
52006
53365
  const raw = refactorFlagValue(args, "--request-json");
52007
- if (raw === undefined)
53366
+ const limitRaw = refactorFlagValue(args, "--import-node-limit");
53367
+ if (raw === undefined && limitRaw === undefined)
52008
53368
  return (await runtime()).refactorScan(cwd, {});
52009
- let request;
52010
- try {
52011
- request = JSON.parse(raw);
52012
- } catch (error) {
52013
- return errorEnvelope(surface, "AC_SCHEMA_INVALID", `refactor scan --request-json is not valid JSON: ${error instanceof Error ? error.message : String(error)}`);
53369
+ let request = { schemaVersion: REFACTOR_REQUEST_SCHEMA_VERSION, scope: { kind: "repository" } };
53370
+ if (raw !== undefined) {
53371
+ try {
53372
+ request = JSON.parse(raw);
53373
+ } catch (error) {
53374
+ return errorEnvelope(surface, "AC_SCHEMA_INVALID", `refactor scan --request-json is not valid JSON: ${error instanceof Error ? error.message : String(error)}`);
53375
+ }
52014
53376
  }
52015
53377
  if (typeof request !== "object" || request === null || Array.isArray(request)) {
52016
53378
  return errorEnvelope(surface, "AC_SCHEMA_INVALID", "refactor scan --request-json must be a RefactorRequestV1 object");
52017
53379
  }
53380
+ if (limitRaw !== undefined) {
53381
+ const importNodeLimit = importNodeLimitValue(limitRaw);
53382
+ if (importNodeLimit === undefined) {
53383
+ return errorEnvelope(surface, "AC_SCHEMA_INVALID", "refactor scan --import-node-limit must be a positive integer");
53384
+ }
53385
+ request = { ...request, importNodeLimit };
53386
+ }
52018
53387
  const issues = refactorRequestInvariantIssues(request);
52019
53388
  if (issues.length > 0)
52020
53389
  return errorEnvelope(surface, "AC_SCHEMA_INVALID", issues.join("; "));
@@ -52034,6 +53403,14 @@ async function runRefactorCommand(args, cwd, runtime) {
52034
53403
  if (typeof request !== "object" || request === null || Array.isArray(request)) {
52035
53404
  return errorEnvelope(surface, "AC_SCHEMA_INVALID", "refactor verify --request-json must be a RefactorVerificationRequestV1 object");
52036
53405
  }
53406
+ const limitRaw = refactorFlagValue(args, "--import-node-limit");
53407
+ if (limitRaw !== undefined) {
53408
+ const importNodeLimit = importNodeLimitValue(limitRaw);
53409
+ if (importNodeLimit === undefined) {
53410
+ return errorEnvelope(surface, "AC_SCHEMA_INVALID", "refactor verify --import-node-limit must be a positive integer");
53411
+ }
53412
+ request = { ...request, importNodeLimit };
53413
+ }
52037
53414
  const issues = refactorVerificationRequestInvariantIssues(request);
52038
53415
  if (issues.length > 0)
52039
53416
  return errorEnvelope(surface, "AC_SCHEMA_INVALID", issues.join("; "));
@@ -52060,7 +53437,7 @@ async function runDocsCommand(args, cwd, daemon) {
52060
53437
  return runArchitectureDocsProjectionCommand2(args, cwd, daemon);
52061
53438
  }
52062
53439
  if (!["status", "resolve", "pin", "fetch", "purge"].includes(subcommand)) {
52063
- return errorEnvelope("docs", "AC_SCHEMA_INVALID", "docs requires status|resolve|pin|fetch|purge|plan|apply|adopt|drift|clean");
53440
+ return errorEnvelope("docs", "AC_SCHEMA_INVALID", "docs requires status|resolve|pin|fetch|purge|plan|apply|adopt|drift|clean|anchors");
52064
53441
  }
52065
53442
  if (subcommand === "status") {
52066
53443
  return daemon.docs(cwd, { command: "status", provider: "context7" });
@@ -52101,6 +53478,27 @@ async function runDocsCommand(args, cwd, daemon) {
52101
53478
  all: args.includes("--all")
52102
53479
  });
52103
53480
  }
53481
+ function runDocsAnchorsCliCommand(args, cwd) {
53482
+ if (args.includes("--help") || args.includes("-h"))
53483
+ return okEnvelope("help", CLI_DOCS_ANCHORS_HELP);
53484
+ for (let index = 0;index < args.length; index += 1) {
53485
+ const arg = args[index];
53486
+ if (arg === "--json")
53487
+ continue;
53488
+ if (arg === "--profile" && args[index + 1] !== undefined && !args[index + 1].startsWith("--")) {
53489
+ index += 1;
53490
+ continue;
53491
+ }
53492
+ return errorEnvelope("docs.anchors", "AC_SCHEMA_INVALID", `docs anchors does not accept ${arg}; use --help`);
53493
+ }
53494
+ let profile;
53495
+ try {
53496
+ profile = architectureProjectionProfile(args);
53497
+ } catch (error) {
53498
+ return errorEnvelope("docs.anchors", "AC_SCHEMA_INVALID", error instanceof Error ? error.message : String(error));
53499
+ }
53500
+ return runDocsAnchorsCommand(cwd, profile ? { profile } : {});
53501
+ }
52104
53502
  async function runArchitectureDocsProjectionCommand2(args, cwd, daemon) {
52105
53503
  const action = args[0] ?? "plan";
52106
53504
  try {
@@ -52146,6 +53544,21 @@ async function runProjectionProtocolCommand2(args, cwd, runtime) {
52146
53544
  }
52147
53545
  return (await runtime()).projection(findRepositoryRoot2(cwd), { action, request });
52148
53546
  }
53547
+ function runFlowsCommand(args, cwd) {
53548
+ if (args.includes("--help") || args.includes("-h"))
53549
+ return okEnvelope("flows", {
53550
+ command: "archctx flows check [--json]",
53551
+ description: "Check every declared flow selector against the CodeGraph index without rendering a projection.",
53552
+ statuses: "Each selector reports proven, no-direct-call, only-references, false-builtin-call, ambiguous-symbol, evidence-missing or evidence-truncated. Only proven binds a flow step.",
53553
+ writes: "none; runs in-process with no daemon, local store or task session. Like projection check, it runs codegraph sync first."
53554
+ });
53555
+ const formatAt = args.indexOf("--format");
53556
+ const unexpected = args.slice(1).filter((arg, index) => arg !== "--json" && (formatAt < 0 || index + 1 !== formatAt && index !== formatAt));
53557
+ if (args[0] !== "check" || unexpected.length > 0) {
53558
+ return errorEnvelope("flows", "AC_SCHEMA_INVALID", "flows requires check [--json]; use --help");
53559
+ }
53560
+ return runFlowSelectorCheckCommand(cwd);
53561
+ }
52149
53562
  function acceptedArchitectureChange(args) {
52150
53563
  const changeSetId = readFlag(args, "--accepted-change-set-id");
52151
53564
  const eventId = readFlag(args, "--accepted-event-id");
@@ -52520,12 +53933,12 @@ function auditManifestGateRoot(cwd) {
52520
53933
  }
52521
53934
  }
52522
53935
  function auditGithubIssuesEnabled(cwd) {
52523
- const manifestPath = resolve28(auditManifestGateRoot(cwd), ".archcontext/manifest.yaml");
52524
- if (!existsSync27(manifestPath))
53936
+ const manifestPath = resolve29(auditManifestGateRoot(cwd), ".archcontext/manifest.yaml");
53937
+ if (!existsSync28(manifestPath))
52525
53938
  return false;
52526
53939
  let raw;
52527
53940
  try {
52528
- raw = readFileSync27(manifestPath, "utf8");
53941
+ raw = readFileSync28(manifestPath, "utf8");
52529
53942
  } catch {
52530
53943
  return false;
52531
53944
  }
@@ -52768,7 +54181,7 @@ async function runAuditCommand(args, cwd, runtime) {
52768
54181
  const elapsedSeconds = Math.round((Date.now() - (deadline - pollTimeoutMs)) / 1000);
52769
54182
  process.stderr.write(`archctx audit run: ${jobId} is still running (${elapsedSeconds}s elapsed)...
52770
54183
  `);
52771
- await sleep(pollIntervalMs);
54184
+ await sleep2(pollIntervalMs);
52772
54185
  }
52773
54186
  return errorEnvelope("audit.run", "AC_PRECONDITION_FAILED", `archctx audit run: job ${jobId} has not reached a terminal status after ${Math.round(pollTimeoutMs / 1000)}s; this is not a failure, the daemon may still be running it \u2014 check later with: archctx audit list`);
52774
54187
  }
@@ -52907,8 +54320,8 @@ async function runGithubCommand(args, cwd, deps) {
52907
54320
  }
52908
54321
  if (subcommand === "disconnect") {
52909
54322
  const connection = await deps.githubConnectionReader?.readVerifiedConnection({ repositoryRoot: cwd });
52910
- const localRecordRemoved = existsSync27(connectionPath);
52911
- rmSync16(connectionPath, { force: true });
54323
+ const localRecordRemoved = existsSync28(connectionPath);
54324
+ rmSync15(connectionPath, { force: true });
52912
54325
  return okEnvelope("github.disconnect", {
52913
54326
  connected: connection !== undefined,
52914
54327
  localRecordRemoved,
@@ -53187,7 +54600,7 @@ function readPullRequestNumber(args) {
53187
54600
  return;
53188
54601
  const value = Number(raw);
53189
54602
  if (!Number.isInteger(value) || value < 1)
53190
- throw new Error("--pr must be a positive integer");
54603
+ throw new CliUsageError("--pr must be a positive integer");
53191
54604
  return value;
53192
54605
  }
53193
54606
  async function digestReviewChallenge(challenge) {
@@ -53219,7 +54632,7 @@ async function readReviewChallengeV2Arg(args, cwd, commandName = "github verify-
53219
54632
  if (!inline && !challengePath)
53220
54633
  return { ok: false, message: `${commandName} requires --challenge-json or --challenge-path` };
53221
54634
  try {
53222
- const raw = inline ?? readFileSync27(resolve28(cwd, challengePath), "utf8");
54635
+ const raw = inline ?? readFileSync28(resolve29(cwd, challengePath), "utf8");
53223
54636
  const parsed = JSON.parse(raw);
53224
54637
  const attestation = await Promise.resolve().then(() => (init_src6(), {}));
53225
54638
  const assertReviewChallengeV2 = assertReviewChallengeV22;
@@ -53600,7 +55013,7 @@ function readLatestPackageVersion(env) {
53600
55013
  if (env[LATEST_VERSION_ENV]) {
53601
55014
  return { source: "env", version: env[LATEST_VERSION_ENV] };
53602
55015
  }
53603
- const result = withLocalEgress("npm-update-check", () => spawnSync3("npm", ["view", RELEASE_PACKAGE_NAME, "version", "--json"], {
55016
+ const result = withLocalEgress("npm-update-check", () => spawnSync4("npm", ["view", RELEASE_PACKAGE_NAME, "version", "--json"], {
53604
55017
  encoding: "utf8",
53605
55018
  timeout: NPM_VIEW_TIMEOUT_MS,
53606
55019
  shell: false
@@ -53694,10 +55107,10 @@ function doctorSqlite(cwd) {
53694
55107
  const legacyLocalStore = inspectLegacyLocalStoreMigration(cwd);
53695
55108
  return {
53696
55109
  path,
53697
- exists: existsSync27(path),
55110
+ exists: existsSync28(path),
53698
55111
  migrations: productVersionManifest().runtime.sqliteMigrations,
53699
55112
  legacyPath: paths.legacyLocalStorePath,
53700
- legacyExists: existsSync27(paths.legacyLocalStorePath),
55113
+ legacyExists: existsSync28(paths.legacyLocalStorePath),
53701
55114
  legacyLocalStore
53702
55115
  };
53703
55116
  }
@@ -53728,7 +55141,7 @@ function runtimePathsReport(cwd) {
53728
55141
  };
53729
55142
  }
53730
55143
  function pathAccess(path) {
53731
- const exists = existsSync27(path);
55144
+ const exists = existsSync28(path);
53732
55145
  return {
53733
55146
  path,
53734
55147
  exists,
@@ -54140,7 +55553,7 @@ async function waitForPidExit(pid, timeoutMs) {
54140
55553
  while (Date.now() < deadline) {
54141
55554
  if (!isPidAlive(pid))
54142
55555
  return true;
54143
- await sleep(50);
55556
+ await sleep2(50);
54144
55557
  }
54145
55558
  return !isPidAlive(pid);
54146
55559
  }
@@ -54181,16 +55594,16 @@ async function waitForDaemonReady(cwd, timeoutMs, shouldStop) {
54181
55594
  return ready;
54182
55595
  if (shouldStop?.())
54183
55596
  return;
54184
- await sleep(50);
55597
+ await sleep2(50);
54185
55598
  }
54186
55599
  return;
54187
55600
  }
54188
- function sleep(ms) {
55601
+ function sleep2(ms) {
54189
55602
  return new Promise((resolve) => setTimeout(resolve, ms));
54190
55603
  }
54191
55604
  function readFileTail(path, maxBytes = 4096) {
54192
55605
  try {
54193
- const content = readFileSync27(path, "utf8");
55606
+ const content = readFileSync28(path, "utf8");
54194
55607
  return content.slice(Math.max(0, content.length - maxBytes)).replace(/\s+/g, " ").trim();
54195
55608
  } catch {
54196
55609
  return "<unavailable>";
@@ -54254,19 +55667,109 @@ function resolveCommandExitCode(result) {
54254
55667
  if (!result || result.ok !== true)
54255
55668
  return 1;
54256
55669
  const data = result.data;
55670
+ if (Array.isArray(data?.results))
55671
+ return data.results.some((entry) => entry.ambiguous === true) ? 2 : 0;
54257
55672
  if (data?.matched === true)
54258
55673
  return 0;
54259
55674
  if (data?.ambiguous === true)
54260
55675
  return 2;
54261
55676
  return 1;
54262
55677
  }
55678
+ var CLI_RESOLVE_HELP = {
55679
+ command: "archctx resolve (--path <repo-relative path> | --paths-from <file> | --paths-from -)",
55680
+ rule: "ADR-0043. source.exclude applies first. When every matching node lies on one parent chain, the deepest node owns the path; refactor scan uses the same rule. When matching nodes lie on different chains, an ancestor yields to its own descendant, then the longest literal source.include prefix wins, and an equal-length tie is ambiguous.",
55681
+ batch: "--paths-from reads one repository-relative POSIX path per line from a file (relative to the current directory) or from stdin (-). Blank lines are ignored and surrounding whitespace is trimmed. The model loads once. data.results has one entry per input path, in input order. The command reads no daemon state and no CodeGraph index.",
55682
+ result: "Each result carries path, matched, ambiguous and owningCapabilityId. A match adds stableId, kind, name, source and extensions of the most specific node. An ambiguous result adds candidates. owningCapabilityId is the nearest kind: capability node on the owner's parent chain (the owner itself when it is a capability), or null. For an ambiguous result it is set only when all candidates share one owning capability.",
55683
+ exitCodes: "--path: 0 matched, 1 no match or error, 2 ambiguous. --paths-from: 0 when no path is ambiguous (unowned paths included), 2 when any path is ambiguous, 1 on error."
55684
+ };
55685
+ function resolveResultData(path, outcome) {
55686
+ if (outcome.status === "matched") {
55687
+ const node = outcome.node;
55688
+ return {
55689
+ path,
55690
+ matched: true,
55691
+ ambiguous: false,
55692
+ stableId: node.id,
55693
+ kind: node.kind,
55694
+ name: node.name,
55695
+ owningCapabilityId: outcome.owningCapabilityId,
55696
+ ...node.source ? { source: node.source } : {},
55697
+ ...node.extensions ? { extensions: node.extensions } : {}
55698
+ };
55699
+ }
55700
+ if (outcome.status === "ambiguous") {
55701
+ return { path, matched: false, ambiguous: true, candidates: outcome.candidates.map((node) => node.id), owningCapabilityId: outcome.owningCapabilityId };
55702
+ }
55703
+ return { path, matched: false, ambiguous: false, owningCapabilityId: null };
55704
+ }
55705
+ async function readResolvePathsFrom(args, cwd, deps) {
55706
+ const source = readFlag(args, "--paths-from");
55707
+ if (source === undefined || source.startsWith("--"))
55708
+ return { problem: "resolve --paths-from requires a file path or - for stdin" };
55709
+ let content;
55710
+ try {
55711
+ content = source === "-" ? await (deps.readStdin ? deps.readStdin() : readFileSync28(0, "utf8")) : readFileSync28(resolve29(cwd, source), "utf8");
55712
+ } catch (error) {
55713
+ return { problem: `resolve --paths-from ${source} is not readable: ${error instanceof Error ? error.message : String(error)}` };
55714
+ }
55715
+ const paths = [];
55716
+ for (const [index, line] of content.split(`
55717
+ `).entries()) {
55718
+ const path = line.trim();
55719
+ if (path === "")
55720
+ continue;
55721
+ if (!isRepoRelativePosixPath(path))
55722
+ return { problem: `resolve --paths-from line ${index + 1} must be a repository-relative POSIX path: ${path}` };
55723
+ paths.push(path);
55724
+ }
55725
+ return { paths };
55726
+ }
54263
55727
  var CLI_PLAN_ENTITY_FLAGS = ["--op", "--path", "--expected-hash", "--body", "--body-file"];
55728
+ var CLI_DEPS_HELP = {
55729
+ command: "archctx deps [--from <node-id>] [--into <node-id>] [--import-node-limit <n>] [--edge-limit <n>] [--json]",
55730
+ description: "Read-only capability x capability import matrix. The command reads the model files, the HEAD tree and the CodeGraph index. It writes nothing and needs no daemon.",
55731
+ rule: "Each file gets its owners from the parent-chain rule refactor scan uses. Each owner rolls up to the nearest kind: capability node on its parent chain. An edge counts in data.matrix only when both files roll up to exactly one capability. Other edges count in data.imports.unattributed by cause.",
55732
+ filters: "--from <node-id> keeps edges whose importing file the node or a descendant owns. --into <node-id> keeps edges whose imported file the node or a descendant owns. An unknown node id is AC_SCHEMA_INVALID.",
55733
+ imports: "data.imports reports coverage, reasonCodes, truncated, importNodeCount, importNodeLimit and importNodeLimitExceeded from the CodeGraph import read. A missing or stale index gives coverage unknown and an empty matrix. --import-node-limit raises the read cap (default 50000).",
55734
+ calls: "Call counts are not available in v1. data.calls.coverage is unknown with reason call-edges-not-exposed, and every cell has calls: null.",
55735
+ edges: "data.edges lists the attributed file-level edges behind the matrix, sorted by path, up to --edge-limit (default 200). data.edges.count is the full total."
55736
+ };
55737
+ function runDepsCommand(args, cwd) {
55738
+ if (args.includes("--help") || args.includes("-h"))
55739
+ return okEnvelope("deps", CLI_DEPS_HELP);
55740
+ const importNodeLimit = readOptionalPositiveIntegerFlag(args, "--import-node-limit", "deps");
55741
+ if (!importNodeLimit.ok)
55742
+ return importNodeLimit.envelope;
55743
+ const edgeLimit = readOptionalNonNegativeIntegerFlag(args, "--edge-limit", "deps");
55744
+ if (!edgeLimit.ok)
55745
+ return edgeLimit.envelope;
55746
+ const from = readFlag(args, "--from");
55747
+ const into = readFlag(args, "--into");
55748
+ for (const [flag, value] of [["--from", from], ["--into", into]]) {
55749
+ if (args.includes(flag) && (value === undefined || value.startsWith("--"))) {
55750
+ return errorEnvelope("deps", "AC_SCHEMA_INVALID", `deps ${flag} requires a node id`);
55751
+ }
55752
+ }
55753
+ try {
55754
+ return okEnvelope("deps", readCapabilityDependencies(findRepositoryRoot2(cwd), {
55755
+ ...from === undefined ? {} : { from },
55756
+ ...into === undefined ? {} : { into },
55757
+ ...importNodeLimit.value === undefined ? {} : { importNodeLimit: importNodeLimit.value },
55758
+ ...edgeLimit.value === undefined ? {} : { edgeLimit: edgeLimit.value }
55759
+ }));
55760
+ } catch (error) {
55761
+ if (error instanceof CapabilityDependenciesError)
55762
+ return errorEnvelope("deps", error.code, error.message);
55763
+ throw error;
55764
+ }
55765
+ }
54264
55766
  var CLI_PLAN_HELP = {
54265
- command: "archctx plan --id <changeset.id> (--op <create_entity|update_entity_fields|delete_entity> --path <file> [--expected-hash sha256:<64-hex>] [--body <yaml> | --body - | --body-file <path>] | --operations-file <operations.json>)",
54266
- operations: "--operations-file takes a repository-relative path to a JSON array of operations in the archcontext_plan_update MCP shape and plans them as one ChangeSet; it cannot be combined with --op, --path, --expected-hash, --body or --body-file.",
54267
- body: "--body is the complete new YAML document, not a field patch. Give exactly one of --body <yaml>, --body - (stdin) or --body-file <path>. --body-file and --operations-file read only repository-relative files inside the repository (no absolute or .. paths, no symlinks); content from outside the repository must come through --body - (stdin). delete_entity accepts no body.",
55767
+ command: "archctx plan --id <changeset.id> (--op <create_entity|update_entity_fields|delete_entity> --path <file> [--expected-hash sha256:<64-hex>] [--body <yaml> | --body - | --body-file <path>] | --operations-file <operations.json | ->)",
55768
+ operations: "--operations-file takes a repository-relative path to a JSON array of operations in the archcontext_plan_update MCP shape, or - to read that array from stdin, and plans them as one ChangeSet; it cannot be combined with --op, --path, --expected-hash, --body or --body-file.",
55769
+ body: "--body is the complete new YAML document, not a field patch. Give exactly one of --body <yaml>, --body - (stdin) or --body-file <path>. --body-file and --operations-file read only repository-relative files inside the repository (no absolute or .. paths, no symlinks); content from outside the repository must come through stdin (--body - or --operations-file -). delete_entity accepts no body.",
54268
55770
  expectedHash: "digestJson({ body }) of the file's current UTF-8 contents, not sha256sum of the file. Read it with `archctx hash --path <file>`; use `missing` (the create default) for a new file.",
54269
55771
  drafts: "A draft lives in daemon memory. Planning the same --id again replaces it, and a daemon restart between plan and apply drops it: apply then fails with `Unknown ChangeSet`; plan again.",
55772
+ ownershipDelta: "When the plan writes a node file, data.preview.ownershipDelta compares file ownership before and after the planned model over the files the HEAD tree carries, with the parent-chain rule refactor scan uses. becameOwned, becameContested, ownerChanged and becameUnowned each give count, up to 50 files with before and after owners, and omittedCount. status unavailable gives the reason, for example a planned body that does not parse. Read-only.",
54270
55773
  next: "archctx apply --id <changeset.id> --approved --expected-worktree-digest <draft.base.worktreeDigest>"
54271
55774
  };
54272
55775
  var CLI_HASH_HELP = {
@@ -54295,7 +55798,7 @@ function readRepositoryInputFile(cwd, flag, file) {
54295
55798
  const absolute = assertPathHasNoSymlinkSegments2(cwd, file);
54296
55799
  if (!statSync10(absolute).isFile())
54297
55800
  return { problem: `plan ${flag} ${file} is not a regular file` };
54298
- return { content: readFileSync27(absolute, "utf8") };
55801
+ return { content: readFileSync28(absolute, "utf8") };
54299
55802
  } catch (error) {
54300
55803
  return { problem: `plan ${flag} ${file} is not readable: ${error instanceof Error ? error.message : String(error)}` };
54301
55804
  }
@@ -54313,10 +55816,10 @@ function runHashCommand(args, cwd) {
54313
55816
  } catch (error) {
54314
55817
  return errorEnvelope("hash", "AC_SCHEMA_INVALID", error instanceof Error ? error.message : String(error));
54315
55818
  }
54316
- if (!existsSync27(absolute) || !statSync10(absolute).isFile()) {
55819
+ if (!existsSync28(absolute) || !statSync10(absolute).isFile()) {
54317
55820
  return errorEnvelope("hash", "AC_SCHEMA_INVALID", `hash --path is not an existing file: ${path}`);
54318
55821
  }
54319
- return okEnvelope("hash", { path, hash: digestJson({ body: readFileSync27(absolute, "utf8") }) });
55822
+ return okEnvelope("hash", { path, hash: digestJson({ body: readFileSync28(absolute, "utf8") }) });
54320
55823
  }
54321
55824
  async function readCliPlanOperations(args, cwd, deps) {
54322
55825
  if (args.includes("--operations-file")) {
@@ -54326,7 +55829,7 @@ async function readCliPlanOperations(args, cwd, deps) {
54326
55829
  const file = readFlag(args, "--operations-file");
54327
55830
  if (!file)
54328
55831
  return "plan --operations-file requires a path";
54329
- const read = readRepositoryInputFile(cwd, "--operations-file", file);
55832
+ const read = file === "-" ? { content: await (deps.readStdin ? deps.readStdin() : readFileSync28(0, "utf8")) } : readRepositoryInputFile(cwd, "--operations-file", file);
54330
55833
  if ("problem" in read)
54331
55834
  return read.problem;
54332
55835
  let parsed;
@@ -54388,7 +55891,7 @@ async function readCliPlanBody(args, cwd, deps) {
54388
55891
  return { problem: "plan --body requires a value (use - to read stdin)" };
54389
55892
  if (inline !== "-")
54390
55893
  return inline;
54391
- return deps.readStdin ? deps.readStdin() : readFileSync27(0, "utf8");
55894
+ return deps.readStdin ? deps.readStdin() : readFileSync28(0, "utf8");
54392
55895
  }
54393
55896
  function readFlag(args, flag) {
54394
55897
  const index = args.indexOf(flag);
@@ -54399,7 +55902,7 @@ function readFlag(args, flag) {
54399
55902
  function requireFlag(args, flag) {
54400
55903
  const value = readFlag(args, flag);
54401
55904
  if (!value)
54402
- throw new Error(`${flag} is required`);
55905
+ throw new CliUsageError(`${flag} is required`);
54403
55906
  return value;
54404
55907
  }
54405
55908
  function copyOptionalFlag(args, flag) {
@@ -54424,11 +55927,11 @@ function explorerProjectionQueryV2FromCli(args) {
54424
55927
  const expectedObservedFactsDigest = readFlag(args, "--expected-observed-facts-digest");
54425
55928
  const expectedRequired = [expectedHeadSha, expectedWorktreeDigest, expectedGraphDigest];
54426
55929
  if (expectedRequired.some(Boolean) && !expectedRequired.every(Boolean)) {
54427
- throw new Error("--expected-head-sha, --expected-worktree-digest, and --expected-graph-digest must be provided together");
55930
+ throw new CliUsageError("--expected-head-sha, --expected-worktree-digest, and --expected-graph-digest must be provided together");
54428
55931
  }
54429
55932
  const viewId = readFlag(args, "--view") ?? "system-map";
54430
55933
  if (!EXPLORER_VIEW_IDS.includes(viewId))
54431
- throw new Error(`unsupported Explorer view: ${viewId}`);
55934
+ throw new CliUsageError(`unsupported Explorer view: ${viewId}`);
54432
55935
  return {
54433
55936
  schemaVersion: "archcontext.explorer-projection-query/v2",
54434
55937
  viewId,
@@ -54451,6 +55954,44 @@ function explorerProjectionQueryV2FromCli(args) {
54451
55954
  }
54452
55955
  };
54453
55956
  }
55957
+ function cliFailureEnvelope(error) {
55958
+ if (error instanceof CliUsageError)
55959
+ return errorEnvelope("cli", "AC_SCHEMA_INVALID", error.message);
55960
+ return errorEnvelope("cli", "AC_RUNTIME_UNAVAILABLE", error instanceof Error ? error.message : String(error));
55961
+ }
55962
+ if (__require.main == __require.module) {
55963
+ if (command === "capabilities") {
55964
+ const result = await runCli(command, args, process.cwd());
55965
+ if ("ok" in result) {
55966
+ process.stdout.write(`${renderResult(result, "json")}
55967
+ `);
55968
+ if (result.ok === false)
55969
+ process.exitCode = 1;
55970
+ } else {
55971
+ process.stdout.write(`${JSON.stringify(result, null, 2)}
55972
+ `);
55973
+ }
55974
+ } else if (command === "mcp" && args.length === 0) {
55975
+ await runStdioMcpLoop(stdinLines(), (line) => process.stdout.write(`${line}
55976
+ `), (line) => process.stderr.write(`${line}
55977
+ `), { runtimeResolver: (root) => createOrStartRuntimeRpcClient(root) });
55978
+ } else if (command === "daemon" && args[0] === "start" && args.includes("--foreground")) {
55979
+ await runForegroundDaemon(process.cwd(), args).catch((error) => {
55980
+ const message = error instanceof Error ? error.message : String(error);
55981
+ process.stderr.write(`${message}
55982
+ `);
55983
+ process.exitCode = isDaemonOwnedElsewhereError(message) ? DAEMON_OWNED_ELSEWHERE_EXIT_CODE : 1;
55984
+ });
55985
+ } else {
55986
+ const result = await runCli(command, args, process.cwd()).catch(cliFailureEnvelope);
55987
+ process.stdout.write(`${renderResult(result, readFlag(args, "--format") ?? "json")}
55988
+ `);
55989
+ if (result.ok === false)
55990
+ process.exitCode = 1;
55991
+ if (command === "resolve")
55992
+ process.exitCode = resolveCommandExitCode(result);
55993
+ }
55994
+ }
54454
55995
  export {
54455
55996
  resolveCommandExitCode,
54456
55997
  runCapabilitiesCommand,