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

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 +1106 -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,230 @@
1
+ /**
2
+ * Lock round-trip invariant (TRL-1200).
3
+ *
4
+ * For every committed `trails.lock` in the repo, a fresh `trails compile`
5
+ * against a cold per-user store followed by `trails validate` must be
6
+ * green, and the recompiled lock must be byte-identical to the committed
7
+ * one. Evidence the toolchain cannot reproduce does not merge.
8
+ *
9
+ * Failures name the divergence and the command that fixes it. Hand-editing
10
+ * a lock is never the remediation.
11
+ */
12
+
13
+ import { existsSync, readFileSync, writeFileSync } from 'node:fs';
14
+ import { mkdtemp, rm } from 'node:fs/promises';
15
+ import { tmpdir } from 'node:os';
16
+ import { dirname, join, relative, resolve } from 'node:path';
17
+
18
+ const trailsBinFor = (repoRoot: string): string =>
19
+ join(repoRoot, 'apps/trails/bin/trails.ts');
20
+
21
+ export interface LockRoundtripSmokeResult {
22
+ readonly check: 'lock-roundtrip';
23
+ readonly lockCount: number;
24
+ readonly message: string;
25
+ readonly passed: true;
26
+ }
27
+
28
+ export interface LockRoundtripSmokeOptions {
29
+ /** Override lock discovery with explicit repo-relative lock paths. */
30
+ readonly lockPaths?: readonly string[];
31
+ readonly repoRoot?: string;
32
+ }
33
+
34
+ const discoverCommittedLocks = (repoRoot: string): readonly string[] => {
35
+ const result = Bun.spawnSync({
36
+ cmd: ['git', 'ls-files', '--', 'trails.lock', '*/trails.lock'],
37
+ cwd: repoRoot,
38
+ stderr: 'pipe',
39
+ stdout: 'pipe',
40
+ });
41
+ if (result.exitCode !== 0) {
42
+ throw new Error(
43
+ `lock-roundtrip: git ls-files failed: ${result.stderr.toString()}`
44
+ );
45
+ }
46
+ return result.stdout
47
+ .toString()
48
+ .split('\n')
49
+ .map((line) => line.trim())
50
+ .filter((line) => line.length > 0);
51
+ };
52
+
53
+ const resolveAppModule = (appDir: string): string => {
54
+ if (existsSync(join(appDir, 'src', 'app.ts'))) {
55
+ return './src/app.ts';
56
+ }
57
+ const packageJsonPath = join(appDir, 'package.json');
58
+ if (existsSync(packageJsonPath)) {
59
+ const parsed = JSON.parse(readFileSync(packageJsonPath, 'utf8')) as {
60
+ readonly main?: unknown;
61
+ };
62
+ if (typeof parsed.main === 'string') {
63
+ return parsed.main;
64
+ }
65
+ }
66
+ throw new Error(
67
+ `lock-roundtrip: unable to resolve the app module for "${appDir}" — expected src/app.ts or a package.json "main" entry.`
68
+ );
69
+ };
70
+
71
+ const runTrailsCommand = (
72
+ repoRoot: string,
73
+ stateHome: string,
74
+ args: readonly string[]
75
+ ): { readonly exitCode: number; readonly output: string } => {
76
+ const result = Bun.spawnSync({
77
+ cmd: [process.execPath, trailsBinFor(repoRoot), ...args],
78
+ cwd: repoRoot,
79
+ env: {
80
+ ...process.env,
81
+ NO_COLOR: '1',
82
+ TRAILS_STATE_HOME: stateHome,
83
+ XDG_STATE_HOME: stateHome,
84
+ } as Record<string, string | undefined>,
85
+ stderr: 'pipe',
86
+ stdout: 'pipe',
87
+ });
88
+ return {
89
+ exitCode: result.exitCode,
90
+ output: `${result.stdout.toString()}${result.stderr.toString()}`,
91
+ };
92
+ };
93
+
94
+ const summarizeEntryIds = (value: unknown): readonly string[] => {
95
+ if (typeof value !== 'object' || value === null) {
96
+ return [];
97
+ }
98
+ const { topoGraph } = value as {
99
+ readonly topoGraph?: { readonly entries?: readonly { id?: string }[] };
100
+ };
101
+ return (topoGraph?.entries ?? [])
102
+ .map((entry) => entry.id)
103
+ .filter((id): id is string => typeof id === 'string');
104
+ };
105
+
106
+ /** Name what diverged between the committed and recompiled lock bytes. */
107
+ const describeLockDivergence = (
108
+ committed: string,
109
+ recompiled: string
110
+ ): readonly string[] => {
111
+ const details: string[] = [];
112
+ let committedParsed: unknown;
113
+ let recompiledParsed: unknown;
114
+ try {
115
+ committedParsed = JSON.parse(committed);
116
+ recompiledParsed = JSON.parse(recompiled);
117
+ } catch {
118
+ return ['committed lock is not valid JSON'];
119
+ }
120
+ const committedLock = committedParsed as Record<string, unknown>;
121
+ const recompiledLock = recompiledParsed as Record<string, unknown>;
122
+
123
+ for (const section of ['scope', 'summary', 'topoGraphHash', 'version']) {
124
+ const left = JSON.stringify(committedLock[section]);
125
+ const right = JSON.stringify(recompiledLock[section]);
126
+ if (left !== right) {
127
+ details.push(`${section}: committed ${left} vs recompiled ${right}`);
128
+ }
129
+ }
130
+
131
+ const committedIds = new Set(summarizeEntryIds(committedParsed));
132
+ const recompiledIds = new Set(summarizeEntryIds(recompiledParsed));
133
+ const missing = [...committedIds].filter((id) => !recompiledIds.has(id));
134
+ const added = [...recompiledIds].filter((id) => !committedIds.has(id));
135
+ if (missing.length > 0) {
136
+ details.push(`entries only in committed lock: ${missing.join(', ')}`);
137
+ }
138
+ if (added.length > 0) {
139
+ details.push(`entries only in recompiled lock: ${added.join(', ')}`);
140
+ }
141
+ if (details.length === 0) {
142
+ details.push('topoGraph entry content differs (same ids, different facts)');
143
+ }
144
+ return details;
145
+ };
146
+
147
+ const checkSingleLock = async (
148
+ repoRoot: string,
149
+ lockPath: string
150
+ ): Promise<void> => {
151
+ const absoluteLockPath = resolve(repoRoot, lockPath);
152
+ const appDir = dirname(absoluteLockPath);
153
+ const relativeAppDir = relative(repoRoot, appDir) || '.';
154
+ const appModule = resolveAppModule(appDir);
155
+ const committedBytes = readFileSync(absoluteLockPath, 'utf8');
156
+ const stateHome = await mkdtemp(join(tmpdir(), 'lock-roundtrip-'));
157
+ const fixCommand = `bun apps/trails/bin/trails.ts compile --module ${appModule} --root-dir ${relativeAppDir} --permit '{"id":"lock-refresh","scopes":["topo:write"]}'`;
158
+ const remediation = `Fix: run \`${fixCommand}\` and commit the refreshed trails.lock. Never hand-edit the lock.`;
159
+
160
+ try {
161
+ const compileResult = runTrailsCommand(repoRoot, stateHome, [
162
+ 'compile',
163
+ '--module',
164
+ appModule,
165
+ '--root-dir',
166
+ relativeAppDir,
167
+ '--permit',
168
+ '{"id":"lock-roundtrip-gate","scopes":["topo:write"]}',
169
+ '--json',
170
+ ]);
171
+ if (compileResult.exitCode !== 0) {
172
+ throw new Error(
173
+ `lock-roundtrip: cold compile failed for ${lockPath}.\n${compileResult.output}\n${remediation}`
174
+ );
175
+ }
176
+
177
+ const validateResult = runTrailsCommand(repoRoot, stateHome, [
178
+ 'validate',
179
+ '--module',
180
+ appModule,
181
+ '--root-dir',
182
+ relativeAppDir,
183
+ '--json',
184
+ ]);
185
+ if (validateResult.exitCode !== 0) {
186
+ throw new Error(
187
+ `lock-roundtrip: validate failed for ${lockPath} after a cold recompile.\n${validateResult.output}\n${remediation}`
188
+ );
189
+ }
190
+
191
+ const recompiledBytes = readFileSync(absoluteLockPath, 'utf8');
192
+ if (recompiledBytes !== committedBytes) {
193
+ const details = describeLockDivergence(committedBytes, recompiledBytes);
194
+ throw new Error(
195
+ [
196
+ `lock-roundtrip: ${lockPath} is not byte-identical after a cold recompile.`,
197
+ ...details.map((detail) => ` - ${detail}`),
198
+ remediation,
199
+ ].join('\n')
200
+ );
201
+ }
202
+ } finally {
203
+ // The gate is read-only: always restore the committed bytes.
204
+ writeFileSync(absoluteLockPath, committedBytes);
205
+ await rm(stateHome, { force: true, recursive: true });
206
+ }
207
+ };
208
+
209
+ export const runLockRoundtripSmoke = async (
210
+ options?: LockRoundtripSmokeOptions
211
+ ): Promise<LockRoundtripSmokeResult> => {
212
+ const repoRoot = resolve(options?.repoRoot ?? process.cwd());
213
+ const lockPaths = options?.lockPaths ?? discoverCommittedLocks(repoRoot);
214
+
215
+ for (const lockPath of lockPaths) {
216
+ await checkSingleLock(repoRoot, lockPath);
217
+ }
218
+
219
+ const message =
220
+ lockPaths.length === 0
221
+ ? 'lock-roundtrip: no committed trails.lock files found — nothing to verify'
222
+ : `lock-roundtrip: ${lockPaths.length} committed trails.lock file(s) recompiled cold — validate green, byte-identical (${lockPaths.join(', ')})`;
223
+
224
+ return {
225
+ check: 'lock-roundtrip',
226
+ lockCount: lockPaths.length,
227
+ message,
228
+ passed: true,
229
+ };
230
+ };