@ontrails/trails 1.0.0-beta.4 → 1.0.0-beta.41

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.
Files changed (165) hide show
  1. package/CHANGELOG.md +1104 -4
  2. package/README.md +27 -0
  3. package/package.json +31 -7
  4. package/src/app.ts +147 -3
  5. package/src/cli.ts +345 -11
  6. package/src/completions.ts +240 -0
  7. package/src/lifecycle-source-io.ts +33 -0
  8. package/src/load-app-mirror.ts +202 -0
  9. package/src/local-state-io.ts +173 -0
  10. package/src/mcp-app.ts +42 -0
  11. package/src/mcp-options.ts +91 -0
  12. package/src/mcp.ts +8 -0
  13. package/src/project-writes.ts +377 -0
  14. package/src/regrade/config.ts +152 -0
  15. package/src/regrade/history.ts +430 -0
  16. package/src/regrade/live-api-preserve.ts +8 -0
  17. package/src/regrade/plan-artifact.ts +285 -0
  18. package/src/release/bindings.ts +39 -0
  19. package/src/release/check.ts +844 -0
  20. package/src/release/config.ts +63 -0
  21. package/src/release/contract-facts.ts +425 -0
  22. package/src/release/index.ts +142 -0
  23. package/src/release/lock-roundtrip-smoke.ts +230 -0
  24. package/src/release/native-bun-publish.ts +651 -0
  25. package/src/release/native-bun-registry.ts +765 -0
  26. package/src/release/notes-cli.ts +171 -0
  27. package/src/release/notes.ts +390 -0
  28. package/src/release/pack-coherence.ts +455 -0
  29. package/src/release/packed-artifacts-smoke.ts +236 -0
  30. package/src/release/policy.ts +1687 -0
  31. package/src/release/semver.ts +104 -0
  32. package/src/release/smoke.ts +56 -0
  33. package/src/release/wayfinder-dogfood-smoke.ts +760 -0
  34. package/src/retired-topo-command.ts +36 -0
  35. package/src/run-adapter-check.ts +76 -0
  36. package/src/run-collision.ts +126 -0
  37. package/src/run-completions-install.ts +179 -0
  38. package/src/run-example.ts +149 -0
  39. package/src/run-examples.ts +148 -0
  40. package/src/run-quiet.ts +75 -0
  41. package/src/run-release-check.ts +74 -0
  42. package/src/run-schema.ts +74 -0
  43. package/src/run-trace.ts +273 -0
  44. package/src/run-warden.ts +39 -0
  45. package/src/run-watch.ts +432 -0
  46. package/src/run-wayfind-outline.ts +170 -0
  47. package/src/scaffold-version-sync.ts +183 -0
  48. package/src/scaffold-versions.generated.ts +12 -0
  49. package/src/trails/adapter-check.ts +244 -0
  50. package/src/trails/add-surface.ts +99 -45
  51. package/src/trails/add-trail.ts +84 -37
  52. package/src/trails/add-verify.ts +100 -30
  53. package/src/trails/compile.ts +58 -0
  54. package/src/trails/completions-complete.ts +165 -0
  55. package/src/trails/completions.ts +47 -0
  56. package/src/trails/create-adapter.ts +785 -0
  57. package/src/trails/create-scaffold.ts +401 -106
  58. package/src/trails/create-versions.ts +62 -0
  59. package/src/trails/create.ts +186 -72
  60. package/src/trails/deprecate.ts +59 -0
  61. package/src/trails/dev-clean.ts +82 -0
  62. package/src/trails/dev-reset.ts +50 -0
  63. package/src/trails/dev-stats.ts +72 -0
  64. package/src/trails/dev-support.ts +360 -0
  65. package/src/trails/doctor.ts +77 -0
  66. package/src/trails/draft-promote.ts +949 -0
  67. package/src/trails/guide.ts +70 -68
  68. package/src/trails/load-app.ts +1123 -15
  69. package/src/trails/operator-context.ts +66 -0
  70. package/src/trails/project.ts +17 -3
  71. package/src/trails/regrade.ts +3091 -0
  72. package/src/trails/release-check.ts +105 -0
  73. package/src/trails/release-smoke.ts +49 -0
  74. package/src/trails/revise.ts +53 -0
  75. package/src/trails/root-dir.ts +21 -0
  76. package/src/trails/run-example.ts +475 -0
  77. package/src/trails/run-examples.ts +129 -0
  78. package/src/trails/run.ts +434 -0
  79. package/src/trails/scaffold-json.ts +58 -0
  80. package/src/trails/survey.ts +877 -214
  81. package/src/trails/topo-activation.ts +14 -0
  82. package/src/trails/topo-constants.ts +2 -0
  83. package/src/trails/topo-history.ts +47 -0
  84. package/src/trails/topo-output-schemas.ts +259 -0
  85. package/src/trails/topo-pin.ts +38 -0
  86. package/src/trails/topo-read-support.ts +368 -0
  87. package/src/trails/topo-reports.ts +809 -0
  88. package/src/trails/topo-store-support.ts +325 -0
  89. package/src/trails/topo-support.ts +220 -0
  90. package/src/trails/topo-unpin.ts +61 -0
  91. package/src/trails/topo.ts +92 -0
  92. package/src/trails/validate.ts +27 -0
  93. package/src/trails/version-lifecycle-support.ts +936 -0
  94. package/src/trails/warden-guide.ts +134 -0
  95. package/src/trails/warden.ts +198 -58
  96. package/src/trails/wayfind-outline.ts +876 -0
  97. package/src/trails/wayfind.ts +1053 -0
  98. package/src/versions.ts +31 -0
  99. package/.turbo/turbo-build.log +0 -1
  100. package/.turbo/turbo-lint.log +0 -3
  101. package/.turbo/turbo-typecheck.log +0 -1
  102. package/__tests__/examples.test.ts +0 -6
  103. package/dist/bin/trails.d.ts +0 -3
  104. package/dist/bin/trails.d.ts.map +0 -1
  105. package/dist/bin/trails.js +0 -4
  106. package/dist/bin/trails.js.map +0 -1
  107. package/dist/src/app.d.ts +0 -2
  108. package/dist/src/app.d.ts.map +0 -1
  109. package/dist/src/app.js +0 -11
  110. package/dist/src/app.js.map +0 -1
  111. package/dist/src/clack.d.ts +0 -9
  112. package/dist/src/clack.d.ts.map +0 -1
  113. package/dist/src/clack.js +0 -84
  114. package/dist/src/clack.js.map +0 -1
  115. package/dist/src/cli.d.ts +0 -2
  116. package/dist/src/cli.d.ts.map +0 -1
  117. package/dist/src/cli.js +0 -13
  118. package/dist/src/cli.js.map +0 -1
  119. package/dist/src/trails/add-surface.d.ts +0 -13
  120. package/dist/src/trails/add-surface.d.ts.map +0 -1
  121. package/dist/src/trails/add-surface.js +0 -88
  122. package/dist/src/trails/add-surface.js.map +0 -1
  123. package/dist/src/trails/add-trail.d.ts +0 -10
  124. package/dist/src/trails/add-trail.d.ts.map +0 -1
  125. package/dist/src/trails/add-trail.js +0 -77
  126. package/dist/src/trails/add-trail.js.map +0 -1
  127. package/dist/src/trails/add-verify.d.ts +0 -10
  128. package/dist/src/trails/add-verify.d.ts.map +0 -1
  129. package/dist/src/trails/add-verify.js +0 -67
  130. package/dist/src/trails/add-verify.js.map +0 -1
  131. package/dist/src/trails/create-scaffold.d.ts +0 -15
  132. package/dist/src/trails/create-scaffold.d.ts.map +0 -1
  133. package/dist/src/trails/create-scaffold.js +0 -288
  134. package/dist/src/trails/create-scaffold.js.map +0 -1
  135. package/dist/src/trails/create.d.ts +0 -22
  136. package/dist/src/trails/create.d.ts.map +0 -1
  137. package/dist/src/trails/create.js +0 -121
  138. package/dist/src/trails/create.js.map +0 -1
  139. package/dist/src/trails/guide.d.ts +0 -11
  140. package/dist/src/trails/guide.d.ts.map +0 -1
  141. package/dist/src/trails/guide.js +0 -80
  142. package/dist/src/trails/guide.js.map +0 -1
  143. package/dist/src/trails/load-app.d.ts +0 -4
  144. package/dist/src/trails/load-app.d.ts.map +0 -1
  145. package/dist/src/trails/load-app.js +0 -24
  146. package/dist/src/trails/load-app.js.map +0 -1
  147. package/dist/src/trails/project.d.ts +0 -8
  148. package/dist/src/trails/project.d.ts.map +0 -1
  149. package/dist/src/trails/project.js +0 -43
  150. package/dist/src/trails/project.js.map +0 -1
  151. package/dist/src/trails/survey.d.ts +0 -31
  152. package/dist/src/trails/survey.d.ts.map +0 -1
  153. package/dist/src/trails/survey.js +0 -221
  154. package/dist/src/trails/survey.js.map +0 -1
  155. package/dist/src/trails/warden.d.ts +0 -19
  156. package/dist/src/trails/warden.d.ts.map +0 -1
  157. package/dist/src/trails/warden.js +0 -88
  158. package/dist/src/trails/warden.js.map +0 -1
  159. package/dist/tsconfig.tsbuildinfo +0 -1
  160. package/src/__tests__/create.test.ts +0 -349
  161. package/src/__tests__/guide.test.ts +0 -91
  162. package/src/__tests__/load-app.test.ts +0 -15
  163. package/src/__tests__/survey.test.ts +0 -159
  164. package/src/__tests__/warden.test.ts +0 -74
  165. package/tsconfig.json +0 -9
@@ -0,0 +1,368 @@
1
+ /**
2
+ * Read-only live topo consumer helpers.
3
+ *
4
+ * Extracted from topo-support.ts to isolate read-only topo consumer helpers,
5
+ * keeping module boundaries clean.
6
+ */
7
+
8
+ import { existsSync } from 'node:fs';
9
+ import { join } from 'node:path';
10
+
11
+ import type { Topo, TrailContext } from '@ontrails/core';
12
+ import {
13
+ ConflictError,
14
+ deriveTrailsDbPath,
15
+ deriveTrailsDir,
16
+ NotFoundError,
17
+ Result,
18
+ SURFACE_LAYER_NAMES_KEY,
19
+ ValidationError,
20
+ } from '@ontrails/core';
21
+ import {
22
+ deriveTopoGraph,
23
+ deriveTopoGraphDiff,
24
+ deriveTopoGraphHash,
25
+ LOCK_MANIFEST_SCHEMA_VERSION,
26
+ readLockManifest,
27
+ readTopoGraph,
28
+ readTrailsLock,
29
+ stripTopoGraphForces,
30
+ } from '@ontrails/topography';
31
+ import type {
32
+ LockManifest,
33
+ TopoGraph,
34
+ TopoGraphOverlayRegistration,
35
+ } from '@ontrails/topography';
36
+
37
+ import type {
38
+ BriefReport,
39
+ SignalDetailReport,
40
+ SurfaceLayerNames,
41
+ SurveyListReport,
42
+ TrailDetailReport,
43
+ } from './topo-reports.js';
44
+ import {
45
+ countTrailExamples,
46
+ deriveBriefReport,
47
+ deriveResourceDetail,
48
+ deriveSignalDetail,
49
+ deriveSurveyList,
50
+ deriveTrailDetail,
51
+ } from './topo-reports.js';
52
+ import type { ActivationGraphReport } from './topo-activation.js';
53
+ import { deriveActivationGraph } from './topo-activation.js';
54
+ import type { TopoSummaryReport, TopoValidateReport } from './topo-support.js';
55
+ import { deriveRootDir, LOCK_PATH } from './topo-support.js';
56
+ import { deriveCurrentTopoExport } from './topo-store-support.js';
57
+
58
+ export type CurrentTrailDetail = TrailDetailReport;
59
+
60
+ export interface CurrentResourceDetail {
61
+ readonly description: string | null;
62
+ readonly health: 'available' | 'none';
63
+ readonly id: string;
64
+ readonly kind: 'resource';
65
+ readonly lifetime: 'singleton';
66
+ readonly usedBy: readonly string[];
67
+ }
68
+
69
+ export type CurrentTopoDetail =
70
+ | CurrentResourceDetail
71
+ | CurrentTrailDetail
72
+ | SignalDetailReport;
73
+
74
+ export interface CurrentTopoMatch {
75
+ readonly kind: CurrentTopoDetail['kind'];
76
+ readonly detail: CurrentTopoDetail;
77
+ }
78
+
79
+ export interface CurrentTopoReadOptions {
80
+ readonly rootDir?: string | undefined;
81
+ readonly overlays?: readonly TopoGraphOverlayRegistration[] | undefined;
82
+ readonly surfaceLayerNames?: Partial<SurfaceLayerNames> | undefined;
83
+ }
84
+
85
+ const isStringArray = (value: unknown): value is readonly string[] =>
86
+ Array.isArray(value) && value.every((item) => typeof item === 'string');
87
+
88
+ export const readSurfaceLayerNamesFromContext = (
89
+ ctx: TrailContext
90
+ ): Partial<SurfaceLayerNames> => {
91
+ const value = ctx.extensions?.[SURFACE_LAYER_NAMES_KEY];
92
+ if (value === null || typeof value !== 'object' || Array.isArray(value)) {
93
+ return {};
94
+ }
95
+ const raw = value as Record<string, unknown>;
96
+ return {
97
+ ...(isStringArray(raw['cli']) ? { cli: raw['cli'] } : {}),
98
+ ...(isStringArray(raw['http']) ? { http: raw['http'] } : {}),
99
+ ...(isStringArray(raw['mcp']) ? { mcp: raw['mcp'] } : {}),
100
+ };
101
+ };
102
+
103
+ const hasCommittedLock = (trailsDir: string): boolean =>
104
+ existsSync(join(trailsDir, 'trails.lock'));
105
+
106
+ const readCommittedLock = async (
107
+ rootDir: string
108
+ ): Promise<{
109
+ readonly lockManifest: LockManifest;
110
+ readonly topoGraph: TopoGraph;
111
+ } | null> => {
112
+ const trailsLock = await readTrailsLock({ dir: rootDir });
113
+ if (trailsLock !== null) {
114
+ return {
115
+ lockManifest: {
116
+ artifacts: [
117
+ {
118
+ path: 'topo.lock',
119
+ role: 'topo',
120
+ sha256: trailsLock.topoGraphHash,
121
+ },
122
+ ],
123
+ scope: trailsLock.scope,
124
+ summary: trailsLock.summary,
125
+ version: LOCK_MANIFEST_SCHEMA_VERSION,
126
+ },
127
+ topoGraph: trailsLock.topoGraph as TopoGraph,
128
+ };
129
+ }
130
+
131
+ const legacyDir = deriveTrailsDir({ rootDir });
132
+ const legacyManifest = await readLockManifest({ dir: legacyDir });
133
+ if (legacyManifest === null) {
134
+ return null;
135
+ }
136
+ const legacyTopoGraph = await readTopoGraph({ dir: legacyDir });
137
+ if (legacyTopoGraph === null) {
138
+ return null;
139
+ }
140
+ return {
141
+ lockManifest: legacyManifest,
142
+ topoGraph: legacyTopoGraph,
143
+ };
144
+ };
145
+
146
+ // ---------------------------------------------------------------------------
147
+ // Public read-only consumers
148
+ // ---------------------------------------------------------------------------
149
+
150
+ export const deriveTopoSummary = (
151
+ app: Topo,
152
+ options?: { readonly rootDir?: string }
153
+ ): TopoSummaryReport => {
154
+ const rootDir = deriveRootDir(options?.rootDir);
155
+ return {
156
+ app: deriveBriefReport(app),
157
+ dbPath: deriveTrailsDbPath({ rootDir }),
158
+ list: deriveSurveyList(app),
159
+ lockExists:
160
+ hasCommittedLock(rootDir) ||
161
+ hasCommittedLock(deriveTrailsDir({ rootDir })),
162
+ lockPath: LOCK_PATH,
163
+ };
164
+ };
165
+
166
+ export const deriveCurrentTopoBrief = (
167
+ app: Topo,
168
+ _options?: { readonly rootDir?: string }
169
+ ): BriefReport => deriveBriefReport(app);
170
+
171
+ export const deriveCurrentTopoList = (
172
+ app: Topo,
173
+ _options?: { readonly rootDir?: string }
174
+ ): SurveyListReport => deriveSurveyList(app);
175
+
176
+ export const deriveCurrentGuideEntries = (
177
+ app: Topo,
178
+ _options?: { readonly rootDir?: string }
179
+ ): readonly {
180
+ readonly description: string;
181
+ readonly exampleCount: number;
182
+ readonly id: string;
183
+ readonly kind: 'trail';
184
+ }[] =>
185
+ app
186
+ .list()
187
+ .map((trail) => ({
188
+ description: trail.description ?? '(no description)',
189
+ exampleCount: countTrailExamples(trail),
190
+ id: trail.id,
191
+ kind: 'trail' as const,
192
+ }))
193
+ .toSorted((a, b) => a.id.localeCompare(b.id));
194
+
195
+ export const deriveCurrentTrailDetail = (
196
+ app: Topo,
197
+ id: string,
198
+ options?: CurrentTopoReadOptions
199
+ ): CurrentTrailDetail | undefined => {
200
+ const trail = app.get(id);
201
+ return trail === undefined
202
+ ? undefined
203
+ : deriveTrailDetail(trail, app, undefined, {
204
+ surfaceLayerNames: options?.surfaceLayerNames,
205
+ topoGraph: deriveTopoGraph(app, {
206
+ overlays: options?.overlays,
207
+ }),
208
+ });
209
+ };
210
+
211
+ export const deriveCurrentResourceDetail = (
212
+ app: Topo,
213
+ id: string,
214
+ _options?: { readonly rootDir?: string }
215
+ ): CurrentResourceDetail | undefined =>
216
+ app.getResource(id) === undefined
217
+ ? undefined
218
+ : (deriveResourceDetail(app, id) as CurrentResourceDetail);
219
+
220
+ export const deriveCurrentSignalDetail = (
221
+ app: Topo,
222
+ id: string,
223
+ _options?: { readonly rootDir?: string }
224
+ ): SignalDetailReport | undefined => deriveSignalDetail(app, id);
225
+
226
+ export const deriveCurrentTopoDetail = (
227
+ app: Topo,
228
+ id: string,
229
+ options?: CurrentTopoReadOptions
230
+ ): CurrentTopoDetail | undefined =>
231
+ deriveCurrentTrailDetail(app, id, options) ??
232
+ deriveCurrentResourceDetail(app, id) ??
233
+ deriveCurrentSignalDetail(app, id);
234
+
235
+ export const deriveCurrentTopoMatches = (
236
+ app: Topo,
237
+ id: string,
238
+ options?: CurrentTopoReadOptions
239
+ ): readonly CurrentTopoMatch[] => {
240
+ const matches: CurrentTopoMatch[] = [];
241
+ let activationGraph: ActivationGraphReport | undefined;
242
+ const getActivationGraph = (): ActivationGraphReport =>
243
+ (activationGraph ??= deriveActivationGraph(app));
244
+ let topoGraph: ReturnType<typeof deriveTopoGraph> | undefined;
245
+ const getTopoGraph = (): ReturnType<typeof deriveTopoGraph> =>
246
+ (topoGraph ??= deriveTopoGraph(app, {
247
+ overlays: options?.overlays,
248
+ }));
249
+
250
+ const trail = app.get(id);
251
+ if (trail !== undefined) {
252
+ matches.push({
253
+ detail: deriveTrailDetail(trail, app, getActivationGraph(), {
254
+ surfaceLayerNames: options?.surfaceLayerNames,
255
+ topoGraph: getTopoGraph(),
256
+ }),
257
+ kind: 'trail',
258
+ });
259
+ }
260
+
261
+ const resource = deriveCurrentResourceDetail(app, id);
262
+ if (resource !== undefined) {
263
+ matches.push({ detail: resource, kind: 'resource' });
264
+ }
265
+
266
+ const signal = deriveSignalDetail(app, id, activationGraph);
267
+ if (signal !== undefined) {
268
+ matches.push({ detail: signal, kind: 'signal' });
269
+ }
270
+
271
+ return matches;
272
+ };
273
+
274
+ export const validateCurrentTopo = async (
275
+ app: Topo,
276
+ options?: {
277
+ readonly rootDir?: string;
278
+ readonly overlays?: readonly TopoGraphOverlayRegistration[] | undefined;
279
+ }
280
+ ): Promise<Result<TopoValidateReport, Error>> => {
281
+ const rootDir = deriveRootDir(options?.rootDir);
282
+ let committedLock: Awaited<ReturnType<typeof readCommittedLock>>;
283
+ try {
284
+ committedLock = await readCommittedLock(rootDir);
285
+ } catch (error) {
286
+ const message =
287
+ error instanceof Error
288
+ ? error.message
289
+ : 'Unable to read committed trails.lock manifest.';
290
+ return Result.err(
291
+ error instanceof Error
292
+ ? new ValidationError(message, { cause: error })
293
+ : new ValidationError(message)
294
+ );
295
+ }
296
+
297
+ if (committedLock === null) {
298
+ return Result.err(
299
+ new NotFoundError(
300
+ 'No committed trails.lock found. Run `trails compile` first.'
301
+ )
302
+ );
303
+ }
304
+
305
+ const currentExport = deriveCurrentTopoExport(app, {
306
+ overlays: options?.overlays,
307
+ rootDir,
308
+ });
309
+ if (currentExport.isErr()) {
310
+ return currentExport;
311
+ }
312
+ const currentTopo = JSON.parse(
313
+ currentExport.value.topoGraphJson
314
+ ) as TopoGraph;
315
+ const currentHash = currentExport.value.topoGraphHash;
316
+ const topoArtifact = committedLock.lockManifest.artifacts.find(
317
+ (artifact) => artifact.role === 'topo' && artifact.path === 'topo.lock'
318
+ );
319
+ if (topoArtifact === undefined) {
320
+ return Result.err(
321
+ new NotFoundError(
322
+ 'No topo.lock artifact found in trails.lock. Run `trails compile` first.'
323
+ )
324
+ );
325
+ }
326
+
327
+ const committedTopo = committedLock.topoGraph;
328
+ const committedHash = deriveTopoGraphHash(committedTopo);
329
+ if (committedHash !== topoArtifact.sha256) {
330
+ return Result.err(
331
+ new ValidationError(
332
+ 'trails.lock graph hash does not match its embedded TopoGraph. Run `trails compile` to refresh it.'
333
+ )
334
+ );
335
+ }
336
+
337
+ if (topoArtifact.sha256 !== currentHash) {
338
+ const forceStrippedHash = deriveTopoGraphHash(
339
+ stripTopoGraphForces(committedTopo)
340
+ );
341
+ if (forceStrippedHash === currentHash) {
342
+ return Result.ok({
343
+ committedHash: topoArtifact.sha256,
344
+ currentHash,
345
+ lockPath: LOCK_PATH,
346
+ stale: false,
347
+ });
348
+ }
349
+ const breakingSummary = (() => {
350
+ const diff = deriveTopoGraphDiff(committedTopo, currentTopo);
351
+ return diff.breaking.length > 0
352
+ ? ` Breaking changes detected: ${diff.breaking.length}.`
353
+ : '';
354
+ })();
355
+ return Result.err(
356
+ new ConflictError(
357
+ `trails.lock is stale. Run \`trails compile\` to refresh it.${breakingSummary}`
358
+ )
359
+ );
360
+ }
361
+
362
+ return Result.ok({
363
+ committedHash: topoArtifact.sha256,
364
+ currentHash,
365
+ lockPath: LOCK_PATH,
366
+ stale: false,
367
+ });
368
+ };