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/assets/catalog.yaml +2 -2
- package/bin/archctx.mjs +1839 -298
- package/package.json +1 -1
- package/schemas/runtime/archctx-capabilities.schema.json +1 -1
- package/schemas/runtime/capability-dependencies.schema.json +116 -0
- package/schemas/runtime/docs-anchors.schema.json +143 -0
- package/schemas/runtime/flow-selector-check.schema.json +109 -0
- package/schemas/runtime/recommendation-v3.schema.json +1 -0
- package/schemas/runtime/runtime-recommendation-show.schema.json +14 -0
- package/schemas/runtime/runtime-refactor-scan.schema.json +19 -0
- package/schemas/runtime/structural-observation-payload.schema.json +1 -0
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.
|
|
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
|
-
|
|
13376
|
-
|
|
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
|
|
16367
|
-
if (
|
|
16368
|
-
input.diagnostics.push({ code:
|
|
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) => ({ '"': "
|
|
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:
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
18709
|
+
text.p3,
|
|
18092
18710
|
"",
|
|
18093
|
-
|
|
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,
|
|
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
|
|
18923
|
+
function orphanSkeletons(committedEntityTargets, path, targetId) {
|
|
18303
18924
|
const committed = committedEntityTargets.get(targetId);
|
|
18304
|
-
return committed && committed.path === path ?
|
|
18925
|
+
return committed && committed.path === path ? entitySummarySkeletonVariants({ id: committed.nodeId }) : [];
|
|
18305
18926
|
}
|
|
18306
|
-
function orphanHoldsOnlyIntactGeneratedRegion(body, targetId,
|
|
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
|
|
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
|
-
|
|
18549
|
-
|
|
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
|
-
|
|
18554
|
-
|
|
18555
|
-
|
|
18556
|
-
|
|
18557
|
-
|
|
18558
|
-
|
|
18559
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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 ??
|
|
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 ??
|
|
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 {
|
|
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
|
|
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
|
|
19801
|
-
|
|
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
|
-
|
|
19820
|
-
|
|
19821
|
-
|
|
19822
|
-
|
|
19823
|
-
|
|
19824
|
-
|
|
19825
|
-
|
|
19826
|
-
|
|
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
|
|
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
|
|
20468
|
-
import { accessSync as accessSync2, closeSync as closeSync10, constants as constants2, existsSync as
|
|
20469
|
-
import { dirname as dirname15, join as join18, resolve as
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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" ?
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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) =>
|
|
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
|
|
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
|
-
|
|
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/
|
|
44954
|
+
// packages/local-runtime/runtime-daemon/src/docs-anchors.ts
|
|
44072
44955
|
init_src();
|
|
44073
|
-
|
|
44074
|
-
|
|
44075
|
-
|
|
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
|
|
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
|
|
44294
|
-
|
|
44295
|
-
|
|
44296
|
-
|
|
44297
|
-
|
|
44298
|
-
|
|
44299
|
-
|
|
44300
|
-
|
|
44301
|
-
|
|
44302
|
-
|
|
44303
|
-
const
|
|
44304
|
-
|
|
44305
|
-
|
|
44306
|
-
|
|
44307
|
-
|
|
44308
|
-
|
|
44309
|
-
|
|
44310
|
-
|
|
44311
|
-
|
|
44312
|
-
|
|
44313
|
-
|
|
44314
|
-
|
|
44315
|
-
|
|
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
|
|
44318
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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,
|
|
47887
|
+
imports = repositoryImportPairs(input.root, CODEGRAPH_BINARY, edgeLimit, input.worktreeDigest);
|
|
46729
47888
|
} catch {
|
|
46730
|
-
imports = {
|
|
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(
|
|
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
|
|
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(
|
|
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(
|
|
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,
|
|
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,
|
|
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:
|
|
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
|
|
47970
|
-
import { resolve as
|
|
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
|
|
47976
|
-
import { resolve as
|
|
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 =
|
|
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(
|
|
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
|
|
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 =
|
|
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:
|
|
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:
|
|
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,
|
|
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
|
-
|
|
50260
|
+
await removePrivateControlFile(connection.connectionPath);
|
|
48934
50261
|
if (this.lockFd !== undefined) {
|
|
48935
50262
|
closeSync9(this.lockFd);
|
|
48936
50263
|
this.lockFd = undefined;
|
|
48937
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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()
|
|
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
|
|
49218
|
-
import { resolve as
|
|
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 || !
|
|
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 (!
|
|
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 (
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
52011
|
-
|
|
52012
|
-
|
|
52013
|
-
|
|
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 =
|
|
52524
|
-
if (!
|
|
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 =
|
|
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
|
|
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 =
|
|
52911
|
-
|
|
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
|
|
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 ??
|
|
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", () =>
|
|
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:
|
|
55110
|
+
exists: existsSync28(path),
|
|
53698
55111
|
migrations: productVersionManifest().runtime.sqliteMigrations,
|
|
53699
55112
|
legacyPath: paths.legacyLocalStorePath,
|
|
53700
|
-
legacyExists:
|
|
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 =
|
|
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
|
|
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
|
|
55597
|
+
await sleep2(50);
|
|
54185
55598
|
}
|
|
54186
55599
|
return;
|
|
54187
55600
|
}
|
|
54188
|
-
function
|
|
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 =
|
|
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 -
|
|
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:
|
|
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 (!
|
|
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:
|
|
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() :
|
|
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
|
|
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
|
|
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
|
|
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,
|