@ontrails/trails 1.0.0-beta.3 → 1.0.0-beta.30

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