@ontrails/trails 1.0.0-beta.17 → 1.0.0-beta.19

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 (45) hide show
  1. package/CHANGELOG.md +139 -0
  2. package/README.md +7 -10
  3. package/package.json +13 -12
  4. package/src/app.ts +14 -4
  5. package/src/cli.ts +16 -0
  6. package/src/lifecycle-source-io.ts +33 -0
  7. package/src/project-writes.ts +62 -5
  8. package/src/retired-topo-command.ts +36 -0
  9. package/src/run-adapter-check.ts +76 -0
  10. package/src/run-collision.ts +1 -0
  11. package/src/trails/adapter-check.ts +244 -0
  12. package/src/trails/add-surface.ts +18 -18
  13. package/src/trails/add-trail.ts +3 -2
  14. package/src/trails/add-verify.ts +30 -6
  15. package/src/trails/{topo-compile.ts → compile.ts} +16 -8
  16. package/src/trails/completions-complete.ts +1 -1
  17. package/src/trails/create-adapter.ts +1084 -0
  18. package/src/trails/create-scaffold.ts +243 -29
  19. package/src/trails/create.ts +118 -17
  20. package/src/trails/deprecate.ts +59 -0
  21. package/src/trails/dev-clean.ts +2 -2
  22. package/src/trails/dev-reset.ts +2 -2
  23. package/src/trails/dev-stats.ts +1 -1
  24. package/src/trails/doctor.ts +56 -0
  25. package/src/trails/draft-promote.ts +1 -0
  26. package/src/trails/guide.ts +2 -2
  27. package/src/trails/revise.ts +53 -0
  28. package/src/trails/run-example.ts +12 -7
  29. package/src/trails/run-examples.ts +3 -3
  30. package/src/trails/run.ts +7 -4
  31. package/src/trails/survey.ts +332 -25
  32. package/src/trails/topo-history.ts +1 -1
  33. package/src/trails/topo-output-schemas.ts +30 -1
  34. package/src/trails/topo-pin.ts +3 -2
  35. package/src/trails/topo-read-support.ts +49 -8
  36. package/src/trails/topo-reports.ts +39 -22
  37. package/src/trails/topo-store-support.ts +62 -16
  38. package/src/trails/topo-support.ts +1 -1
  39. package/src/trails/topo-unpin.ts +2 -2
  40. package/src/trails/topo.ts +2 -2
  41. package/src/trails/{topo-verify.ts → validate.ts} +7 -7
  42. package/src/trails/version-lifecycle-support.ts +945 -0
  43. package/src/trails/warden-guide.ts +8 -0
  44. package/src/trails/warden.ts +18 -2
  45. package/src/versions.ts +4 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,144 @@
1
1
  # trails
2
2
 
3
+ ## 1.0.0-beta.19
4
+
5
+ ### Major Changes
6
+
7
+ - 1eb5bdc: Rename first-class trail composition from the `cross` API family to the `compose` family across core contracts, testing helpers, topo projections, Warden rules, CLI scaffolds, and docs. `composes`, `ctx.compose`, `composeInput`, and `Compose*` type names are now the public authoring vocabulary; topo persistence migrates legacy composition rows and graph keys forward.
8
+ - 120caf5: Promote topo artifact commands to `trails compile` and `trails validate`.
9
+
10
+ ### Patch Changes
11
+
12
+ - e41c382: Document beta-channel install guidance in package and adapter README install snippets so consumers use explicit `@beta` (or pinned `1.0.0-beta.N`) tags instead of accidental `latest` resolution during the prerelease line. Adds the policy doc at `docs/releases/beta-channel-policy.md`, prints both `latest` and `beta` dist-tags in `bun run publish:registry-check`, and aligns plugin/skill install snippets.
13
+ - 14714b8: Add a beta.15 to beta.19 downstream migration guide (`docs/releases/beta15-to-beta19.md`) that ties together package install, CLI/MCP/HTTP surface decisions, public output schemas, contract testing, resource mocks / `unmockable`, error taxonomy, observability, Topographer artifact workflow, layer evolution, the `cross`→`compose` composition rename, trail-versioning runtime adoption, and adapter authoring. Linked from `docs/index.md` Release Notes and cross-references the focused migration guides under `docs/migration/`.
14
+ - 91328d3: Make `trails create` reruns reconcile existing scaffold files instead of overwriting present files and then failing on existing surfaces.
15
+ - 6471b73: Preserve the original `create.scaffold` Result boundary when `trails create` cannot scaffold a project.
16
+ - 51aac45: Add `entity.list` and `entity.delete` trails to the generated entity starter so fresh scaffolds model complete CRUD coverage.
17
+ - 5efa32c: Generate project-level `AGENTS.md` and `CLAUDE.md` guidance so new Trails apps
18
+ start with canonical agent instructions.
19
+ - 88c0316: Generate a contextual `README.md` for new Trails projects with first-run
20
+ commands, selected surfaces, starter notes, and agent guidance pointers.
21
+ - 99154d4: Generate `tsconfig.tests.json` in new Trails projects so root test files are
22
+ covered by editor TypeScript tooling without changing build output.
23
+ - 492f71c: Move CLI, MCP, HTTP, established-surface, and surface-parity helpers behind explicit subpaths so root contract testing imports no longer require optional surface peers. The Trails CLI scaffolder now emits `import { testAllEstablished } from '@ontrails/testing/established'` for generated verification.
24
+ - 4bc8a99: Clarify the Topographer artifact workflow around top-level `trails compile`, `trails validate`, and `trails diff` commands, including explicit diagnostics for retired `trails topo compile`, `trails topo verify`, and `trails topo check` attempts.
25
+ - 16cb740: Run examples and contract checks across live trail version entries, and project version-entry example coverage into topo and survey reports.
26
+ - 92e709b: Declare explicit permit scopes on mutating built-in CLI trails and scaffolded entity starter trails.
27
+
28
+ Preserve the resolved CLI permit on result callbacks so run-collision recovery can re-execute protected trails without losing authorization context.
29
+
30
+ - 1f48342: Preserve original Result error boundaries in CLI trails by returning existing Result failures directly instead of re-wrapping their errors.
31
+ - c14aa3a: Report structured entry and graph force audit details from `trails doctor`.
32
+ - 2df73cc: Configure scaffolded Trails projects to allow `TODO :::` fieldwork markers while keeping standard `TODO:` warning comments blocked.
33
+ - 7f50fe2: Add version lifecycle CLI trails for revising, deprecating, archiving, and diagnosing trail version entries.
34
+ - 653d1fc: Add a top-level `trails diff` command and extend TopoGraph diffs with version, marker, lifecycle status, support set, and force-event audit details.
35
+ - 2e76288: Add graph-only force event projection for forced compile break acceptance and block unforced breaking topo changes.
36
+ - 52e4e8f: Add the `@ontrails/trails` CLI package and core framework command scripts to newly scaffolded projects.
37
+ - 58be821: Generated projects now pin `@ontrails/*` packages to the exact scaffolded
38
+ package version instead of emitting caret prerelease ranges.
39
+ - da7cbcb: Generated projects now include a minimal `.trails/scaffold.json` provenance
40
+ breadcrumb recording the scaffold schema version, package version, starter
41
+ template, and generation timestamp.
42
+ - fc00aeb: Add adapter target conformance metadata and scaffold extracted HTTP adapters through `trails create adapter`.
43
+ - 1c975c3: Define the Warden fix-metadata contract (`WardenFix`, `WardenFixCapability`, `WardenFixClass`, `WardenFixSafety`, `WardenFixEdit`) with optional `fix` metadata on diagnostics and rule metadata, projected through the guide, manifest, markdown, and agent guidance. Export `wardenFixClasses`/`wardenFixSafeties` value arrays and surface the rule `fix` capability in the `warden.guide` trail output schema. Dormant until a rule declares it.
44
+ - d5d518e: Add `warden --fix` to apply safe source fixes. The executor applies only `safety: 'safe'` edits last-to-first, re-reading and rewriting affected files, while review-required, edit-less, and topo diagnostics stay reported but unapplied. The report surfaces applied, changed-file, and skipped counts.
45
+
46
+ Expose `fix` through the Trails app wrapper and mark the `warden` trail as write intent with explicit public access because `fix: true` mutates source files while the local governance command remains directly runnable.
47
+
48
+ - 678cb1c: Expose the shared adapter readiness engine through Warden's opt-in
49
+ `--adapter-check` diagnostics and the local `trails adapter check` authoring
50
+ workflow.
51
+ - 619cb15: Add a Warden rule (`no-destructured-compose`) that coaches trail blazes to call `ctx.compose(...)` directly instead of destructuring `compose` from the context.
52
+
53
+ Keep the generated `create` trail on the direct `ctx.compose(...)` shape so framework-authored trails follow the same composition guidance.
54
+
55
+ - Updated dependencies [bb81ffe]
56
+ - Updated dependencies [e41c382]
57
+ - Updated dependencies [ed5926b]
58
+ - Updated dependencies [a2f1825]
59
+ - Updated dependencies [a2f1825]
60
+ - Updated dependencies [1eb5bdc]
61
+ - Updated dependencies [f8d80b9]
62
+ - Updated dependencies [94a8380]
63
+ - Updated dependencies [94a8380]
64
+ - Updated dependencies [846a597]
65
+ - Updated dependencies [8638dae]
66
+ - Updated dependencies [8638dae]
67
+ - Updated dependencies [8638dae]
68
+ - Updated dependencies [f0f7e2f]
69
+ - Updated dependencies [223aaad]
70
+ - Updated dependencies [3125f4d]
71
+ - Updated dependencies [2494dc6]
72
+ - Updated dependencies [4bc8a99]
73
+ - Updated dependencies [120caf5]
74
+ - Updated dependencies [2d53717]
75
+ - Updated dependencies [16cb740]
76
+ - Updated dependencies [8894ecb]
77
+ - Updated dependencies [fdf7ec9]
78
+ - Updated dependencies [92e709b]
79
+ - Updated dependencies [d76be13]
80
+ - Updated dependencies [84f56a5]
81
+ - Updated dependencies [64fb15a]
82
+ - Updated dependencies [653d1fc]
83
+ - Updated dependencies [431b04c]
84
+ - Updated dependencies [2e76288]
85
+ - Updated dependencies [5d88104]
86
+ - Updated dependencies [f04a9ef]
87
+ - Updated dependencies [fc00aeb]
88
+ - Updated dependencies [1c975c3]
89
+ - Updated dependencies [48d5ff4]
90
+ - Updated dependencies [d5d518e]
91
+ - Updated dependencies [216bf10]
92
+ - Updated dependencies [ab1c77c]
93
+ - Updated dependencies [8ca5b85]
94
+ - Updated dependencies [4f43874]
95
+ - Updated dependencies [678cb1c]
96
+ - Updated dependencies [5874fd6]
97
+ - Updated dependencies [619cb15]
98
+ - Updated dependencies [4642268]
99
+ - Updated dependencies [9bab0cf]
100
+ - Updated dependencies [3ceeba8]
101
+ - Updated dependencies [beafd03]
102
+ - Updated dependencies [7b173e0]
103
+ - Updated dependencies [6e50e7b]
104
+ - Updated dependencies [48edf8d]
105
+ - Updated dependencies [12ffa3b]
106
+ - Updated dependencies [2f262f7]
107
+ - Updated dependencies [58b01f2]
108
+ - @ontrails/adapter-kit@1.0.0-beta.19
109
+ - @ontrails/core@1.0.0-beta.19
110
+ - @ontrails/cli@1.0.0-beta.19
111
+ - @ontrails/commander@1.0.0-beta.19
112
+ - @ontrails/http@1.0.0-beta.19
113
+ - @ontrails/mcp@1.0.0-beta.19
114
+ - @ontrails/topographer@1.0.0-beta.19
115
+ - @ontrails/warden@1.0.0-beta.19
116
+ - @ontrails/observe@1.0.0-beta.19
117
+ - @ontrails/tracing@1.0.0-beta.19
118
+ - @ontrails/permits@1.0.0-beta.19
119
+
120
+ ## 1.0.0-beta.18
121
+
122
+ ### Patch Changes
123
+
124
+ - Updated dependencies [c0b2948]
125
+ - Updated dependencies [fc3219c]
126
+ - Updated dependencies [bc2d327]
127
+ - Updated dependencies [bf44972]
128
+ - Updated dependencies [57c8672]
129
+ - Updated dependencies [510ea50]
130
+ - Updated dependencies [e0ae995]
131
+ - @ontrails/http@1.0.0-beta.18
132
+ - @ontrails/observe@1.0.0-beta.18
133
+ - @ontrails/tracing@1.0.0-beta.18
134
+ - @ontrails/commander@1.0.0-beta.18
135
+ - @ontrails/cli@1.0.0-beta.18
136
+ - @ontrails/core@1.0.0-beta.18
137
+ - @ontrails/mcp@1.0.0-beta.18
138
+ - @ontrails/permits@1.0.0-beta.18
139
+ - @ontrails/topographer@1.0.0-beta.18
140
+ - @ontrails/warden@1.0.0-beta.18
141
+
3
142
  ## 1.0.0-beta.17
4
143
 
5
144
  ### Patch Changes
package/README.md CHANGED
@@ -2,8 +2,7 @@
2
2
 
3
3
  Command-line tools for working with Trails projects.
4
4
 
5
- Use the CLI to scaffold a Trails app, add surfaces, inspect the current topo, run
6
- warden checks, manage draft state, and keep local Trails project state tidy.
5
+ Use the CLI to scaffold a Trails app, add surfaces, inspect the current topo, run warden checks, manage draft state, and keep local Trails project state tidy.
7
6
 
8
7
  ```bash
9
8
  bunx @ontrails/trails create
@@ -11,17 +10,15 @@ bunx @ontrails/trails create
11
10
 
12
11
  Common workflows:
13
12
 
14
- - `trails create` starts a new Trails project with generated trail, topo, surface,
15
- and verification files.
13
+ - `trails create` starts a new Trails project with generated trail, topo, surface, and verification files.
16
14
  - `trails add surface` adds another surface entrypoint to an existing project.
17
- - `trails topo` inspects, exports, verifies, pins, and unpins topo state.
18
- - `trails warden` runs Trails governance checks for contract and architecture
19
- drift.
15
+ - `trails topo` inspects topo state and manages pins/history.
16
+ - `trails compile` writes committed topo artifacts.
17
+ - `trails validate` checks committed topo artifacts for drift.
18
+ - `trails warden` runs Trails governance checks for contract and architecture drift.
20
19
  - `trails guide` shows available trails and examples from a project.
21
20
 
22
- Trails is contract-first: define trails once with typed input, Result output,
23
- examples, and meta; the framework derives CLI, MCP, HTTP, and future surfaces from
24
- the same contracts.
21
+ Trails is contract-first: define trails once with typed input, Result output, examples, and meta; the framework derives CLI, MCP, HTTP, and future surfaces from the same contracts.
25
22
 
26
23
  See the main Trails documentation for the full framework guide:
27
24
  <https://github.com/outfitter-dev/trails>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ontrails/trails",
3
- "version": "1.0.0-beta.17",
3
+ "version": "1.0.0-beta.19",
4
4
  "bin": {
5
5
  "trails": "./bin/trails.ts"
6
6
  },
@@ -23,20 +23,21 @@
23
23
  },
24
24
  "dependencies": {
25
25
  "@clack/prompts": "^1.1.0",
26
- "@ontrails/cli": "^1.0.0-beta.16",
27
- "@ontrails/commander": "^1.0.0-beta.16",
28
- "@ontrails/core": "^1.0.0-beta.16",
29
- "@ontrails/http": "^1.0.0-beta.16",
30
- "@ontrails/mcp": "^1.0.0-beta.16",
31
- "@ontrails/observe": "^1.0.0-beta.16",
32
- "@ontrails/permits": "^1.0.0-beta.16",
33
- "@ontrails/topographer": "^1.0.0-beta.16",
34
- "@ontrails/tracing": "^1.0.0-beta.16",
35
- "@ontrails/warden": "^1.0.0-beta.16",
26
+ "@ontrails/adapter-kit": "^1.0.0-beta.19",
27
+ "@ontrails/cli": "^1.0.0-beta.19",
28
+ "@ontrails/commander": "^1.0.0-beta.19",
29
+ "@ontrails/core": "^1.0.0-beta.19",
30
+ "@ontrails/http": "^1.0.0-beta.19",
31
+ "@ontrails/mcp": "^1.0.0-beta.19",
32
+ "@ontrails/observe": "^1.0.0-beta.19",
33
+ "@ontrails/permits": "^1.0.0-beta.19",
34
+ "@ontrails/topographer": "^1.0.0-beta.19",
35
+ "@ontrails/tracing": "^1.0.0-beta.19",
36
+ "@ontrails/warden": "^1.0.0-beta.19",
36
37
  "commander": "^14.0.3",
37
38
  "zod": "^4.3.5"
38
39
  },
39
40
  "devDependencies": {
40
- "@ontrails/testing": "^1.0.0-beta.16"
41
+ "@ontrails/testing": "^1.0.0-beta.19"
41
42
  }
42
43
  }
package/src/app.ts CHANGED
@@ -3,25 +3,30 @@ import { topo } from '@ontrails/core';
3
3
  import * as addSurface from './trails/add-surface.js';
4
4
  import * as addTrail from './trails/add-trail.js';
5
5
  import * as addVerify from './trails/add-verify.js';
6
+ import * as adapterCheck from './trails/adapter-check.js';
7
+ import * as compile from './trails/compile.js';
6
8
  import * as completions from './trails/completions.js';
7
9
  import * as completionsComplete from './trails/completions-complete.js';
8
10
  import * as create from './trails/create.js';
11
+ import * as createAdapter from './trails/create-adapter.js';
9
12
  import * as createScaffold from './trails/create-scaffold.js';
13
+ import * as deprecate from './trails/deprecate.js';
10
14
  import * as devClean from './trails/dev-clean.js';
11
15
  import * as devReset from './trails/dev-reset.js';
12
16
  import * as devStats from './trails/dev-stats.js';
17
+ import * as doctor from './trails/doctor.js';
13
18
  import * as draftPromote from './trails/draft-promote.js';
14
19
  import * as guide from './trails/guide.js';
20
+ import * as revise from './trails/revise.js';
15
21
  import * as run from './trails/run.js';
16
22
  import * as runExample from './trails/run-example.js';
17
23
  import * as runExamples from './trails/run-examples.js';
18
24
  import * as survey from './trails/survey.js';
19
- import * as topoCompile from './trails/topo-compile.js';
20
25
  import * as topoHistory from './trails/topo-history.js';
21
26
  import * as topoPin from './trails/topo-pin.js';
22
27
  import * as topoCommand from './trails/topo.js';
23
28
  import * as topoUnpin from './trails/topo-unpin.js';
24
- import * as topoVerify from './trails/topo-verify.js';
29
+ import * as validate from './trails/validate.js';
25
30
  import * as warden from './trails/warden.js';
26
31
  import * as wardenGuide from './trails/warden-guide.js';
27
32
 
@@ -32,19 +37,24 @@ export const app = topo(
32
37
  runExample,
33
38
  survey,
34
39
  topoCommand,
35
- topoCompile,
40
+ compile,
36
41
  topoHistory,
37
42
  topoPin,
38
43
  topoUnpin,
39
- topoVerify,
44
+ validate,
45
+ revise,
46
+ deprecate,
47
+ doctor,
40
48
  devStats,
41
49
  devClean,
42
50
  devReset,
43
51
  guide,
44
52
  draftPromote,
53
+ adapterCheck,
45
54
  warden,
46
55
  wardenGuide,
47
56
  create,
57
+ createAdapter,
48
58
  createScaffold,
49
59
  addSurface,
50
60
  addVerify,
package/src/cli.ts CHANGED
@@ -21,7 +21,12 @@ import { deriveTopoGraph } from '@ontrails/topographer';
21
21
 
22
22
  import { app } from './app.js';
23
23
  import { resolveInputWithClack } from './clack.js';
24
+ import { getRetiredTopoCommandDiagnostic } from './retired-topo-command.js';
24
25
  import { attachCompletionsInstallCommand } from './run-completions-install.js';
26
+ import {
27
+ applyAdapterCheckExitCode,
28
+ tryAdapterCheckOutput,
29
+ } from './run-adapter-check.js';
25
30
  import { tryRecoverFromRunCollision } from './run-collision.js';
26
31
  import { tryExampleRunOutput } from './run-example.js';
27
32
  import { tryExamplesRunOutput } from './run-examples.js';
@@ -64,6 +69,7 @@ const buildOnResult =
64
69
  // envelope on stdout that includes the captured records under
65
70
  // `tracing`. Hand that case off before the regular chain so the
66
71
  // existing handlers do not also write to stdout.
72
+ applyAdapterCheckExitCode(resolvedCtx);
67
73
  if (session !== undefined && tryTraceJsonOutput(resolvedCtx, session)) {
68
74
  return;
69
75
  }
@@ -80,6 +86,9 @@ const buildOnResult =
80
86
  if (tryWardenOutput(resolvedCtx)) {
81
87
  return;
82
88
  }
89
+ if (tryAdapterCheckOutput(resolvedCtx)) {
90
+ return;
91
+ }
83
92
  await defaultOnResult(resolvedCtx);
84
93
  };
85
94
 
@@ -267,6 +276,13 @@ const normalizeWardenArgv = (argv: readonly string[]): string[] => {
267
276
  * process-lifetime sink.
268
277
  */
269
278
  const runSurfaceOnce = async (): Promise<void> => {
279
+ const retiredTopoCommand = getRetiredTopoCommandDiagnostic(process.argv);
280
+ if (retiredTopoCommand !== null) {
281
+ process.stderr.write(`${retiredTopoCommand.message}\n`);
282
+ process.exitCode = 1;
283
+ return;
284
+ }
285
+
270
286
  const session = maybeInstallTraceSession();
271
287
  try {
272
288
  const program = createProgram(app, {
@@ -0,0 +1,33 @@
1
+ import { readFileSync, writeFileSync } from 'node:fs';
2
+
3
+ import { InternalError, Result } from '@ontrails/core';
4
+
5
+ export const readLifecycleSourceFile = (
6
+ filePath: string
7
+ ): Result<string, Error> => {
8
+ try {
9
+ return Result.ok(readFileSync(filePath, 'utf8'));
10
+ } catch (error: unknown) {
11
+ return Result.err(
12
+ error instanceof Error
13
+ ? error
14
+ : new InternalError(`Unable to read lifecycle source file ${filePath}`)
15
+ );
16
+ }
17
+ };
18
+
19
+ export const writeLifecycleSourceFile = (
20
+ filePath: string,
21
+ source: string
22
+ ): Result<void, Error> => {
23
+ try {
24
+ writeFileSync(filePath, source);
25
+ return Result.ok();
26
+ } catch (error: unknown) {
27
+ return Result.err(
28
+ error instanceof Error
29
+ ? error
30
+ : new InternalError(`Unable to write lifecycle source file ${filePath}`)
31
+ );
32
+ }
33
+ };
@@ -36,6 +36,10 @@ export type ProjectWriteOperation =
36
36
  readonly path: string;
37
37
  };
38
38
 
39
+ interface ProjectOperationOptions {
40
+ readonly existing?: 'overwrite' | 'preserve';
41
+ }
42
+
39
43
  export const validateProjectName = (
40
44
  name: string
41
45
  ): TrailsResult<string, ValidationError> =>
@@ -222,12 +226,59 @@ export const planProjectOperation = (
222
226
  }
223
227
  };
224
228
 
229
+ const shouldApplyProjectOperation = (
230
+ projectDir: string,
231
+ operation: ProjectWriteOperation,
232
+ options: ProjectOperationOptions
233
+ ): TrailsResult<boolean, Error> => {
234
+ if (options.existing !== 'preserve' || operation.kind === 'rename') {
235
+ return Result.ok(true);
236
+ }
237
+
238
+ const { path } = operation;
239
+ const target = resolveProjectPath(projectDir, path);
240
+ if (target.isErr()) {
241
+ return Result.err(target.error);
242
+ }
243
+
244
+ return Result.ok(!existsSync(target.value));
245
+ };
246
+
247
+ const selectProjectOperations = (
248
+ projectDir: string,
249
+ operations: readonly ProjectWriteOperation[],
250
+ options: ProjectOperationOptions
251
+ ): TrailsResult<ProjectWriteOperation[], Error> => {
252
+ const selected: ProjectWriteOperation[] = [];
253
+ for (const operation of operations) {
254
+ const shouldApply = shouldApplyProjectOperation(
255
+ projectDir,
256
+ operation,
257
+ options
258
+ );
259
+ if (shouldApply.isErr()) {
260
+ return Result.err(shouldApply.error);
261
+ }
262
+ if (shouldApply.value) {
263
+ selected.push(operation);
264
+ }
265
+ }
266
+
267
+ return Result.ok(selected);
268
+ };
269
+
225
270
  export const planProjectOperations = (
226
271
  projectDir: string,
227
- operations: readonly ProjectWriteOperation[]
272
+ operations: readonly ProjectWriteOperation[],
273
+ options: ProjectOperationOptions = {}
228
274
  ): TrailsResult<PlannedProjectOperation[], Error> => {
275
+ const selected = selectProjectOperations(projectDir, operations, options);
276
+ if (selected.isErr()) {
277
+ return Result.err(selected.error);
278
+ }
279
+
229
280
  const planned: PlannedProjectOperation[] = [];
230
- for (const operation of operations) {
281
+ for (const operation of selected.value) {
231
282
  const result = planProjectOperation(projectDir, operation);
232
283
  if (result.isErr()) {
233
284
  return Result.err(result.error);
@@ -302,14 +353,20 @@ const applyProjectOperation = async (
302
353
 
303
354
  export const applyProjectOperations = async (
304
355
  projectDir: string,
305
- operations: readonly ProjectWriteOperation[]
356
+ operations: readonly ProjectWriteOperation[],
357
+ options: ProjectOperationOptions = {}
306
358
  ): Promise<TrailsResult<PlannedProjectOperation[], Error>> => {
307
- const planned = planProjectOperations(projectDir, operations);
359
+ const selected = selectProjectOperations(projectDir, operations, options);
360
+ if (selected.isErr()) {
361
+ return Result.err(selected.error);
362
+ }
363
+
364
+ const planned = planProjectOperations(projectDir, selected.value);
308
365
  if (planned.isErr()) {
309
366
  return Result.err(planned.error);
310
367
  }
311
368
 
312
- for (const operation of operations) {
369
+ for (const operation of selected.value) {
313
370
  const applied = await applyProjectOperation(projectDir, operation);
314
371
  if (applied.isErr()) {
315
372
  return Result.err(applied.error);
@@ -0,0 +1,36 @@
1
+ const retiredTopoCommandReplacements = {
2
+ check: 'trails validate',
3
+ compile: 'trails compile',
4
+ verify: 'trails validate',
5
+ } as const;
6
+
7
+ export type RetiredTopoCommand = keyof typeof retiredTopoCommandReplacements;
8
+
9
+ export interface RetiredTopoCommandDiagnostic {
10
+ readonly attempted: `trails topo ${RetiredTopoCommand}`;
11
+ readonly message: string;
12
+ readonly replacement: (typeof retiredTopoCommandReplacements)[RetiredTopoCommand];
13
+ }
14
+
15
+ const isRetiredTopoCommand = (
16
+ command: string | undefined
17
+ ): command is RetiredTopoCommand =>
18
+ command !== undefined && command in retiredTopoCommandReplacements;
19
+
20
+ export const getRetiredTopoCommandDiagnostic = (
21
+ argv: readonly string[]
22
+ ): RetiredTopoCommandDiagnostic | null => {
23
+ const [command, subcommand] = argv.slice(2);
24
+ if (command !== 'topo' || !isRetiredTopoCommand(subcommand)) {
25
+ return null;
26
+ }
27
+
28
+ const replacement = retiredTopoCommandReplacements[subcommand];
29
+ const attempted = `trails topo ${subcommand}` as const;
30
+
31
+ return {
32
+ attempted,
33
+ message: `"${attempted}" was retired. Use "${replacement}" instead.\nTopographer artifact commands now live at the top level: "trails compile", "trails validate", and "trails diff". "trails topo" is for topo-store history, pin, and unpin.`,
34
+ replacement,
35
+ };
36
+ };
@@ -0,0 +1,76 @@
1
+ import type { ActionResultContext } from '@ontrails/cli';
2
+ import { deriveOutputMode } from '@ontrails/cli';
3
+
4
+ interface AdapterCheckResultValue {
5
+ readonly formatted: string;
6
+ readonly passed: boolean;
7
+ }
8
+
9
+ const isAdapterCheckResultValue = (
10
+ value: unknown
11
+ ): value is AdapterCheckResultValue => {
12
+ if (typeof value !== 'object' || value === null) {
13
+ return false;
14
+ }
15
+ const candidate = value as Record<string, unknown>;
16
+ return (
17
+ typeof candidate['formatted'] === 'string' &&
18
+ typeof candidate['passed'] === 'boolean'
19
+ );
20
+ };
21
+
22
+ const wantsStructuredOutput = (ctx: ActionResultContext): boolean =>
23
+ deriveOutputMode(ctx.flags, ctx.topoName).mode !== 'text';
24
+
25
+ const isAdapterCheckTrail = (ctx: ActionResultContext): boolean =>
26
+ ctx.trail.id === 'adapter.check';
27
+
28
+ const readAdapterCheckResultValue = (
29
+ ctx: ActionResultContext
30
+ ): AdapterCheckResultValue | undefined => {
31
+ if (!isAdapterCheckTrail(ctx) || ctx.result.isErr()) {
32
+ return undefined;
33
+ }
34
+
35
+ return isAdapterCheckResultValue(ctx.result.value)
36
+ ? ctx.result.value
37
+ : undefined;
38
+ };
39
+
40
+ export const applyAdapterCheckExitCode = (
41
+ ctx: ActionResultContext
42
+ ): boolean => {
43
+ if (!isAdapterCheckTrail(ctx)) {
44
+ return false;
45
+ }
46
+
47
+ if (ctx.result.isErr()) {
48
+ process.exitCode = 1;
49
+ return true;
50
+ }
51
+
52
+ const value = readAdapterCheckResultValue(ctx);
53
+ if (!value) {
54
+ return false;
55
+ }
56
+
57
+ process.exitCode = value.passed ? 0 : 1;
58
+ return true;
59
+ };
60
+
61
+ export const tryAdapterCheckOutput = (ctx: ActionResultContext): boolean => {
62
+ const value = readAdapterCheckResultValue(ctx);
63
+ if (!value) {
64
+ return false;
65
+ }
66
+
67
+ applyAdapterCheckExitCode(ctx);
68
+ if (wantsStructuredOutput(ctx)) {
69
+ return false;
70
+ }
71
+
72
+ if (value.formatted.length > 0) {
73
+ process.stdout.write(`${value.formatted}\n`);
74
+ }
75
+ return true;
76
+ };
@@ -120,6 +120,7 @@ export const tryRecoverFromRunCollision = async (
120
120
  }
121
121
 
122
122
  return await executeTrail(ctx.trail, mergeAppOverride(ctx.input, chosen), {
123
+ ctx: ctx.permit === undefined ? {} : { permit: ctx.permit },
123
124
  topo: deps.graph,
124
125
  });
125
126
  };