gitnexus 1.6.10-aptos.0 → 1.6.10-aptos.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/README.md CHANGED
@@ -375,6 +375,31 @@ described in [Move compiler provisioning](#move-compiler-provisioning) —
375
375
  including the `MOVE_FLOW` override and `GITNEXUS_SKIP_MOVE_FLOW=1` for
376
376
  air-gapped hosts.
377
377
 
378
+ ### Analyzing real-world Move repositories
379
+
380
+ Real repos routinely contain Move packages that cannot build standalone (test
381
+ fixtures, examples, fuzzer corpora — `aptos-core` alone has 470+). `analyze`
382
+ handles them per package instead of giving up:
383
+
384
+ - **Unbuildable packages are skipped with a warning**, not fatal: their
385
+ `.move` files stay out of the graph and the final summary names each skipped
386
+ package with the compiler's first diagnostic. Set `GITNEXUS_MOVE_STRICT=1`
387
+ to make any build failure abort the analyze instead.
388
+ - **`_` placeholder addresses** (`econia = "_"` in `[addresses]`) are caught
389
+ pre-flight — MoveFlow has no dev-mode build, so set concrete addresses in
390
+ `Move.toml` or exclude the package.
391
+ - **Builds with compiler errors still yield facts, at reduced fidelity**: the
392
+ MoveFlow compiler silently omits inferred `acquires` data from erroring
393
+ builds, so such packages are ingested with a persistent
394
+ "compiled with errors" warning. A common cause is a framework dependency
395
+ newer than MoveFlow's pinned compiler (e.g. unrecognized spec pragmas).
396
+ - **`.gitnexusignore`** (gitignore syntax, repo root) excludes directories from
397
+ analysis entirely — the fastest way to scope large repos to the packages you
398
+ care about, and the remedy the skip warnings suggest.
399
+ - **Cold builds of git-based framework dependencies can exceed the 5-minute
400
+ compile budget**; raise it with `GITNEXUS_MOVE_FLOW_TIMEOUT_MS` (e.g.
401
+ `1800000` for 30 min) for the first analyze.
402
+
378
403
  ## Release candidates
379
404
 
380
405
  Stable releases publish to the default `latest` dist-tag. When a pull request
@@ -1193,6 +1193,14 @@ const analyzeCommandImpl = async (inputPath, cliOptions, runnerIdentityAtBootstr
1193
1193
  ` Install it once with network access (GITNEXUS_LBUG_EXTENSION_INSTALL=auto) then rerun, or\n` +
1194
1194
  ` run \`gitnexus analyze --repair-fts\` when connected. Run \`gitnexus doctor\` for details.`);
1195
1195
  }
1196
+ // Standalone-ingest warnings (skipped/degraded Move packages) share the
1197
+ // FTS warning's rationale: mid-run progress lines scroll away, so anything
1198
+ // the operator must act on has to reappear in the final summary.
1199
+ if (result.ingestWarnings && result.ingestWarnings.length > 0) {
1200
+ for (const warning of result.ingestWarnings) {
1201
+ console.log(`\n Warning: ${warning}`);
1202
+ }
1203
+ }
1196
1204
  try {
1197
1205
  await fs.access(getGlobalRegistryPath());
1198
1206
  }
@@ -5,6 +5,12 @@ import type { PipelinePhase } from './types.js';
5
5
  */
6
6
  export interface StandaloneIngestOutput {
7
7
  readonly ingestedFiles: ReadonlySet<string>;
8
+ /**
9
+ * Operator-actionable warnings the ingester wants surfaced in the persistent
10
+ * CLI summary (e.g. a package it had to skip or ingest at degraded fidelity).
11
+ * Language-neutral: the pipeline passes these through without interpreting.
12
+ */
13
+ readonly ingestWarnings?: readonly string[];
8
14
  }
9
15
  /** Default no-op used when the caller does not supply a standalone ingester. */
10
16
  export declare const emptyStandaloneIngestPhase: PipelinePhase<StandaloneIngestOutput>;
@@ -81,6 +81,14 @@ export const runPipelineFromRepo = async (repoPath, onProgress, options) => {
81
81
  const { totalFiles, usedWorkerPool } = getPhaseOutput(results, 'parse');
82
82
  let communityResult;
83
83
  let processResult;
84
+ // Standalone-ingest warnings, passed through opaquely (language-neutral).
85
+ let ingestWarnings;
86
+ try {
87
+ ingestWarnings = getPhaseOutput(results, 'standaloneIngest').ingestWarnings;
88
+ }
89
+ catch {
90
+ /* phase filtered out of this run — nothing to surface */
91
+ }
84
92
  const scopeResolutionOutput = getPhaseOutput(results, 'scopeResolution');
85
93
  const resolutionOutcomes = scopeResolutionOutput.resolutionOutcomes;
86
94
  // Streamed PDG-emit manifest (#2202): present only when streaming was on.
@@ -110,5 +118,6 @@ export const runPipelineFromRepo = async (repoPath, onProgress, options) => {
110
118
  resolutionOutcomes,
111
119
  usedWorkerPool,
112
120
  pdgEmitManifest,
121
+ ingestWarnings,
113
122
  };
114
123
  };
@@ -6,7 +6,15 @@ export interface MoveConsistencyIssue {
6
6
  code: 'missing-owned-caller' | 'missing-owned-callee' | 'malformed-source-evidence' | 'unresolved-resource-target'
7
7
  /** Package with .move sources returned facts `{}` - severity policy in
8
8
  * `emptyFactsIssue` below. */
9
- | 'empty-package-facts';
9
+ | 'empty-package-facts'
10
+ /** Package skipped: move-flow could not build it (skip-and-warn, #2624). */
11
+ | 'package-build-failed'
12
+ /** Package skipped pre-flight: Move.toml [addresses] has `_` placeholders
13
+ * move-flow cannot resolve (it has no dev-mode build). */
14
+ | 'unresolved-named-address'
15
+ /** Package ingested, but its build carries compiler errors - move-flow
16
+ * silently omits inferred facts (acquires) from such builds. */
17
+ | 'degraded-package-facts';
10
18
  severity: MoveConsistencySeverity;
11
19
  message: string;
12
20
  details?: Record<string, unknown>;
@@ -25,4 +33,42 @@ export interface EmptyFactsPackage {
25
33
  * error. Pure mapping - probing the status (client I/O) stays in the phase.
26
34
  */
27
35
  export declare function emptyFactsIssue(pkg: EmptyFactsPackage): MoveConsistencyIssue;
36
+ /**
37
+ * A package skipped because move-flow could not build it (skip-and-warn).
38
+ * Warning, not error: the analyze continues and the skip is surfaced in the
39
+ * CLI summary; GITNEXUS_MOVE_STRICT=1 restores the historical fatal behavior.
40
+ */
41
+ export declare function buildFailedIssue(pkg: {
42
+ pkgRoot: string;
43
+ moveFileCount: number;
44
+ diagnostics: string;
45
+ }): MoveConsistencyIssue;
46
+ /**
47
+ * A package skipped pre-flight: its Move.toml `[addresses]` contains `_`
48
+ * placeholders. move-flow's `move_package_query` has no dev-mode, so the build
49
+ * would always fail with "Unresolved addresses" - skip with the remedy instead.
50
+ */
51
+ export declare function unresolvedAddressIssue(pkg: {
52
+ pkgRoot: string;
53
+ moveFileCount: number;
54
+ placeholders: string[];
55
+ }): MoveConsistencyIssue;
56
+ /**
57
+ * A package that WAS ingested but whose build carries compiler errors.
58
+ * move-flow still serves structurally complete facts for such builds but
59
+ * silently drops inference-stage output (`acquiresInferred`), so the graph is
60
+ * missing ACQUIRES edges/properties — surface it instead of implying full
61
+ * fidelity. (Commonly: a framework dependency newer than move-flow's pinned
62
+ * compiler, e.g. spec pragmas it does not recognize.)
63
+ */
64
+ export declare function degradedFactsIssue(pkg: {
65
+ pkgRoot: string;
66
+ diagnostics: string;
67
+ }): MoveConsistencyIssue;
68
+ /**
69
+ * The persistent CLI-summary warnings for a run's Move issues: the three
70
+ * skip/degrade codes are operator-actionable and must survive past the
71
+ * scrolling progress bar (same rationale as the FTS warning, #1161).
72
+ */
73
+ export declare function cliWarningsFromIssues(issues: readonly MoveConsistencyIssue[]): string[];
28
74
  export declare function validateMoveIngestOutput(graph: KnowledgeGraph, moveIngest: MoveIngestOutput): MoveConsistencyIssue[];
@@ -35,6 +35,90 @@ export function emptyFactsIssue(pkg) {
35
35
  details: { packageRoot: pkgRoot, moveFileCount, diagnostics: status.diagnostics },
36
36
  };
37
37
  }
38
+ /** First non-empty line of a compiler diagnostic blob (for one-line summaries). */
39
+ function firstDiagnosticLine(diagnostics) {
40
+ if (!diagnostics)
41
+ return '';
42
+ for (const line of diagnostics.split('\n')) {
43
+ const trimmed = line.trim();
44
+ if (trimmed)
45
+ return trimmed;
46
+ }
47
+ return '';
48
+ }
49
+ /**
50
+ * A package skipped because move-flow could not build it (skip-and-warn).
51
+ * Warning, not error: the analyze continues and the skip is surfaced in the
52
+ * CLI summary; GITNEXUS_MOVE_STRICT=1 restores the historical fatal behavior.
53
+ */
54
+ export function buildFailedIssue(pkg) {
55
+ const firstLine = firstDiagnosticLine(pkg.diagnostics);
56
+ return {
57
+ code: 'package-build-failed',
58
+ severity: 'warning',
59
+ message: `Move package skipped — move-flow could not build it` +
60
+ (firstLine ? ` (${firstLine})` : '') +
61
+ `: ${pkg.pkgRoot}. Fix the package or exclude its directory via .gitnexusignore; ` +
62
+ `set GITNEXUS_MOVE_STRICT=1 to make build failures fatal.`,
63
+ details: {
64
+ packageRoot: pkg.pkgRoot,
65
+ moveFileCount: pkg.moveFileCount,
66
+ diagnostics: pkg.diagnostics,
67
+ },
68
+ };
69
+ }
70
+ /**
71
+ * A package skipped pre-flight: its Move.toml `[addresses]` contains `_`
72
+ * placeholders. move-flow's `move_package_query` has no dev-mode, so the build
73
+ * would always fail with "Unresolved addresses" - skip with the remedy instead.
74
+ */
75
+ export function unresolvedAddressIssue(pkg) {
76
+ return {
77
+ code: 'unresolved-named-address',
78
+ severity: 'warning',
79
+ message: `Move package skipped — named address(es) ${pkg.placeholders.join(', ')} are "_" ` +
80
+ `placeholders in Move.toml (move-flow cannot build dev-mode): ${pkg.pkgRoot}. ` +
81
+ `Set concrete addresses in [addresses] or exclude the directory via .gitnexusignore.`,
82
+ details: {
83
+ packageRoot: pkg.pkgRoot,
84
+ moveFileCount: pkg.moveFileCount,
85
+ placeholders: pkg.placeholders,
86
+ },
87
+ };
88
+ }
89
+ /**
90
+ * A package that WAS ingested but whose build carries compiler errors.
91
+ * move-flow still serves structurally complete facts for such builds but
92
+ * silently drops inference-stage output (`acquiresInferred`), so the graph is
93
+ * missing ACQUIRES edges/properties — surface it instead of implying full
94
+ * fidelity. (Commonly: a framework dependency newer than move-flow's pinned
95
+ * compiler, e.g. spec pragmas it does not recognize.)
96
+ */
97
+ export function degradedFactsIssue(pkg) {
98
+ const firstLine = firstDiagnosticLine(pkg.diagnostics);
99
+ return {
100
+ code: 'degraded-package-facts',
101
+ severity: 'warning',
102
+ message: `Move package compiled with errors — compiler-inferred facts (acquires) may be ` +
103
+ `incomplete` +
104
+ (firstLine ? ` (${firstLine})` : '') +
105
+ `: ${pkg.pkgRoot}`,
106
+ details: { packageRoot: pkg.pkgRoot, diagnostics: pkg.diagnostics },
107
+ };
108
+ }
109
+ /**
110
+ * The persistent CLI-summary warnings for a run's Move issues: the three
111
+ * skip/degrade codes are operator-actionable and must survive past the
112
+ * scrolling progress bar (same rationale as the FTS warning, #1161).
113
+ */
114
+ export function cliWarningsFromIssues(issues) {
115
+ const surfaced = [
116
+ 'package-build-failed',
117
+ 'unresolved-named-address',
118
+ 'degraded-package-facts',
119
+ ];
120
+ return issues.filter((i) => surfaced.includes(i.code)).map((i) => i.message);
121
+ }
38
122
  export function validateMoveIngestOutput(graph, moveIngest) {
39
123
  const issues = [];
40
124
  for (const [moduleQualified, filePath] of moveIngest.moduleFileMap) {
@@ -45,5 +45,8 @@ export interface MoveIngestOutput extends StandaloneIngestOutput {
45
45
  }[];
46
46
  /** Non-fatal consistency issues found after Move ingestion. */
47
47
  consistencyIssues: MoveConsistencyIssue[];
48
+ /** Operator-actionable warnings for the persistent CLI summary (skipped or
49
+ * degraded packages). Part of the neutral StandaloneIngestOutput contract. */
50
+ ingestWarnings?: readonly string[];
48
51
  }
49
52
  export declare function createMoveIngestPhase(client: MoveFlowClient | null): PipelinePhase<MoveIngestOutput>;
@@ -19,12 +19,13 @@
19
19
  * ACQUIRES/USES_TYPE/ENTRY_POINT_OF/IMPORTS edges)
20
20
  */
21
21
  import * as path from 'node:path';
22
+ import { readFile } from 'node:fs/promises';
22
23
  import { getPhaseOutput } from '../ingestion/pipeline-phases/types.js';
23
24
  import { MOVE_EDGE_REASON, moveRepoRelativePath } from './constants.js';
24
25
  import { MoveFlowToolCallError, } from './mcp-client.js';
25
26
  import { buildLocalNameIndex, mapFactsToGraph, resolveFriendEdges, resolveLambdaHostEdges, resolveResourceEdges, resolveTypeRefEdges, } from './facts-mapper.js';
26
27
  import { moveModuleNodeId, moveModuleQualifiedName, moveRelId } from './symbol-id.js';
27
- import { emptyFactsIssue, validateMoveIngestOutput, } from './consistency.js';
28
+ import { buildFailedIssue, cliWarningsFromIssues, degradedFactsIssue, emptyFactsIssue, unresolvedAddressIssue, validateMoveIngestOutput, } from './consistency.js';
28
29
  import { createMoveEntryPointEdges } from './entry-points.js';
29
30
  function createState() {
30
31
  return {
@@ -54,8 +55,49 @@ function toOutput(state, packageRoots, consistencyIssues = []) {
54
55
  callGraphByPackage: state.callGraphByPackage,
55
56
  droppedResourceRefs: state.droppedResourceRefs,
56
57
  consistencyIssues,
58
+ ingestWarnings: cliWarningsFromIssues(consistencyIssues),
57
59
  };
58
60
  }
61
+ /** GITNEXUS_MOVE_STRICT=1|true restores the historical fatal-on-build-failure
62
+ * behavior instead of skip-and-warn. */
63
+ function isStrictMove() {
64
+ const v = process.env.GITNEXUS_MOVE_STRICT?.trim().toLowerCase();
65
+ return v === '1' || v === 'true';
66
+ }
67
+ /**
68
+ * Named addresses assigned the `_` placeholder in a package's Move.toml
69
+ * `[addresses]` section. Deliberately a line-oriented scan, not a TOML parser:
70
+ * the two token shapes involved (`[section]`, `name = "_"`) are stable across
71
+ * every Move manifest and a full parser dependency buys nothing here.
72
+ * Unreadable/absent manifest → `[]` (the build itself will surface that).
73
+ */
74
+ async function findPlaceholderAddresses(pkgRoot) {
75
+ let text;
76
+ try {
77
+ text = await readFile(path.join(pkgRoot, 'Move.toml'), 'utf8');
78
+ }
79
+ catch {
80
+ return [];
81
+ }
82
+ const placeholders = [];
83
+ let section = '';
84
+ for (const raw of text.split('\n')) {
85
+ const line = raw.replace(/#.*$/, '').trim();
86
+ if (!line)
87
+ continue;
88
+ const sectionMatch = line.match(/^\[(.+)\]$/);
89
+ if (sectionMatch) {
90
+ section = sectionMatch[1].trim();
91
+ continue;
92
+ }
93
+ if (section !== 'addresses')
94
+ continue;
95
+ const kv = line.match(/^([A-Za-z_][A-Za-z0-9_]*)\s*=\s*["']_["']$/);
96
+ if (kv)
97
+ placeholders.push(kv[1]);
98
+ }
99
+ return placeholders;
100
+ }
59
101
  /** Add a mapped package's nodes/edges to the graph and merge its identity maps. */
60
102
  function applyMapped(graph, mapped, pkgRoot, state) {
61
103
  for (const node of mapped.nodes)
@@ -121,6 +163,8 @@ export function createMoveIngestPhase(client) {
121
163
  moveFilesByPackage.set(owner, files);
122
164
  }
123
165
  const emptyFactsPackages = [];
166
+ const packageIssues = [];
167
+ const strictMove = isStrictMove();
124
168
  // Pass 1: per-package nodes/edges (all packages first, so cross-package
125
169
  // CALLS in Pass 2 can resolve callees in later packages).
126
170
  for (const pkgRoot of packageRoots) {
@@ -130,6 +174,14 @@ export function createMoveIngestPhase(client) {
130
174
  message: `Ingesting Move package: ${path.basename(pkgRoot)}`,
131
175
  stats: { filesProcessed: 0, totalFiles, nodesCreated: ctx.graph.nodeCount },
132
176
  });
177
+ const pkgMoveFiles = moveFilesByPackage.get(pkgRoot) ?? [];
178
+ // Pre-flight: `_` placeholder addresses always fail the build (move-flow
179
+ // has no dev-mode), so skip before spending a compile on the known outcome.
180
+ const placeholders = await findPlaceholderAddresses(pkgRoot);
181
+ if (placeholders.length > 0) {
182
+ packageIssues.push(unresolvedAddressIssue({ pkgRoot, moveFileCount: pkgMoveFiles.length, placeholders }));
183
+ continue;
184
+ }
133
185
  let callGraphData;
134
186
  let factsMap;
135
187
  try {
@@ -138,14 +190,30 @@ export function createMoveIngestPhase(client) {
138
190
  }
139
191
  catch (err) {
140
192
  if (err instanceof MoveFlowToolCallError) {
141
- // userActionable: rendered as a one-liner without a stack - a Move
142
- // package that does not build (bad manifest, missing dependency,
143
- // nonexistent path) is an operator problem, not a code bug.
144
- throw Object.assign(new Error(`move-flow could not build Move package ${pkgRoot}: ${err.message}`), { userActionable: true });
193
+ // A Move package that does not build (bad manifest, missing
194
+ // dependency, unresolved address) is an operator problem, not a
195
+ // code bug. Default: skip the package (its files stay un-ingested,
196
+ // like the empty-facts path) and surface a persistent warning
197
+ // one broken auxiliary package must not abort the whole analyze.
198
+ if (strictMove) {
199
+ // userActionable: rendered as a one-liner without a stack.
200
+ throw Object.assign(new Error(`move-flow could not build Move package ${pkgRoot}: ${err.message}`), { userActionable: true });
201
+ }
202
+ packageIssues.push(buildFailedIssue({
203
+ pkgRoot,
204
+ moveFileCount: pkgMoveFiles.length,
205
+ diagnostics: err.message,
206
+ }));
207
+ ctx.onProgress({
208
+ phase: 'moveIngest',
209
+ percent: 18,
210
+ message: `Skipping Move package (build failed): ${path.basename(pkgRoot)}`,
211
+ stats: { filesProcessed: 0, totalFiles, nodesCreated: ctx.graph.nodeCount },
212
+ });
213
+ continue;
145
214
  }
146
215
  throw err;
147
216
  }
148
- const pkgMoveFiles = moveFilesByPackage.get(pkgRoot) ?? [];
149
217
  if (Object.keys(factsMap).length === 0 && pkgMoveFiles.length > 0) {
150
218
  // Facts `{}` is ambiguous: syntax-broken packages return it as a
151
219
  // SUCCESS (the compiler diagnostic only surfaces via
@@ -166,6 +234,14 @@ export function createMoveIngestPhase(client) {
166
234
  for (const rel of pkgMoveFiles)
167
235
  state.ingestedFiles.add(rel);
168
236
  applyMapped(ctx.graph, mapFactsToGraph(factsMap, pkgRoot, ctx.repoPath), pkgRoot, state);
237
+ // Facts arrived, but move-flow serves structurally complete facts even
238
+ // for builds with compiler errors — and such builds silently lose the
239
+ // inference stage (`acquiresInferred`, hence ACQUIRES edges). Probe the
240
+ // build status so the degraded fidelity is surfaced, not implied away.
241
+ const status = await probePackageStatus(client, pkgRoot, hasStatusTool);
242
+ if (status && !status.ok) {
243
+ packageIssues.push(degradedFactsIssue({ pkgRoot, diagnostics: status.diagnostics }));
244
+ }
169
245
  }
170
246
  // Pass 2+: link edges that need the full cross-package node index.
171
247
  linkCallEdges(ctx.graph, state);
@@ -175,10 +251,17 @@ export function createMoveIngestPhase(client) {
175
251
  linkFileModuleContains(ctx.graph, state);
176
252
  const output = toOutput(state, packageRoots);
177
253
  createMoveEntryPointEdges(ctx.graph, output);
178
- const consistencyIssues = emptyFactsPackages.map(emptyFactsIssue);
254
+ const consistencyIssues = [
255
+ ...packageIssues,
256
+ ...emptyFactsPackages.map(emptyFactsIssue),
257
+ ];
179
258
  consistencyIssues.push(...validateMoveIngestOutput(ctx.graph, output));
180
259
  reportConsistencyIssues(ctx, consistencyIssues);
181
- return { ...output, consistencyIssues };
260
+ return {
261
+ ...output,
262
+ consistencyIssues,
263
+ ingestWarnings: cliWarningsFromIssues(consistencyIssues),
264
+ };
182
265
  },
183
266
  };
184
267
  }
@@ -175,6 +175,12 @@ export interface AnalyzeResult {
175
175
  * the persisted meta surface the degraded state instead of reporting healthy.
176
176
  */
177
177
  ftsSkipped?: boolean;
178
+ /**
179
+ * Operator-actionable warnings from the standalone ingest phase (e.g. Move
180
+ * packages skipped or ingested at degraded fidelity). Rendered persistently
181
+ * in the CLI summary — same rationale as the FTS warning (#1161).
182
+ */
183
+ ingestWarnings?: readonly string[];
178
184
  /**
179
185
  * True when the index this run produced/validated is the flat workspace
180
186
  * slot (#2106 R2, inverted by #2354 to follow the checked-out branch).
@@ -2091,6 +2091,7 @@ export async function runFullAnalysis(repoPath, options, callbacks, runnerIdenti
2091
2091
  stats: meta.stats,
2092
2092
  pipelineResult,
2093
2093
  ftsSkipped: !ftsReady,
2094
+ ingestWarnings: pipelineResult.ingestWarnings,
2094
2095
  isPrimaryBranch: !placement.branch,
2095
2096
  };
2096
2097
  }
@@ -34,4 +34,11 @@ export interface PipelineResult {
34
34
  * layer (if any) is resident in `graph` and persists via the whole-graph emit.
35
35
  */
36
36
  pdgEmitManifest?: PdgEmitManifest;
37
+ /**
38
+ * Operator-actionable warnings from the standalone ingest phase (skipped or
39
+ * degraded-fidelity packages). Passed through opaquely — the pipeline does
40
+ * not know which language produced them — so the CLI summary can render them
41
+ * persistently (same rationale as the FTS warning).
42
+ */
43
+ ingestWarnings?: readonly string[];
37
44
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gitnexus",
3
- "version": "1.6.10-aptos.0",
3
+ "version": "1.6.10-aptos.1",
4
4
  "description": "Graph-powered code intelligence for AI agents. Index any codebase, query via MCP or CLI.",
5
5
  "author": "Abhigyan Patwari",
6
6
  "license": "PolyForm-Noncommercial-1.0.0",