@ontrails/core 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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,33 @@
1
1
  # @ontrails/core
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
+
9
+ ### Patch Changes
10
+
11
+ - 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.
12
+ - f8d80b9: Refresh current-facing compose vocabulary in package documentation after the composition cutover.
13
+ - 846a597: Reject versioned trail marker schemas that use Zod validation checks or object
14
+ catchall policies outside the bounded marker subset.
15
+ - 223aaad: Fix `ctx.compose(trail, input)` inference for trails that do not define a
16
+ `composeInput` schema while preserving authored compose-input requirements.
17
+ - 3125f4d: Add pure revision transpose validation and execution helpers for trail versions.
18
+ - 2494dc6: Infer `resource()` create-context config types from resource config schemas.
19
+ - 2d53717: Add trail-only `version` / `versions` authoring types and TopoGraph projection.
20
+ - 16cb740: Run examples and contract checks across live trail version entries, and project version-entry example coverage into topo and survey reports.
21
+ - 8894ecb: Project content-addressed trail version markers and marker-prefix resolution.
22
+ - fdf7ec9: Resolve trail versions during execution, including live revisions, forks, marker references, and unsupported-version errors.
23
+ - d76be13: Require deprecated trail version entries to carry successor, migration, or note guidance and expose typed lifecycle helpers.
24
+ - 84f56a5: Project live trail-version metadata on CLI, HTTP, and MCP surfaces and thread explicit surface version selection into shared trail execution.
25
+ - 431b04c: Expose archived trail version lifecycle helpers and validate archived status reason metadata.
26
+ - 5d88104: Polish Trails blaze terminology across package docs and Warden guidance.
27
+ - f04a9ef: Tighten trail-versioning API polish by keeping executor cross-validation internals out of public options and improving absent marker diagnostics.
28
+
29
+ ## 1.0.0-beta.18
30
+
3
31
  ## 1.0.0-beta.17
4
32
 
5
33
  ### Patch Changes
package/README.md CHANGED
@@ -21,14 +21,14 @@ const greet = trail('greet', {
21
21
  const graph = topo('myapp', { greet });
22
22
  ```
23
23
 
24
- Trails compose other trails through `crosses` and `ctx.cross()`:
24
+ Trails compose other trails through `composes` and `ctx.compose()`:
25
25
 
26
26
  ```typescript
27
27
  const onboard = trail('entity.onboard', {
28
- crosses: ['entity.add', 'entity.relate'],
28
+ composes: ['entity.add', 'entity.relate'],
29
29
  input: z.object({ name: z.string(), type: z.string() }),
30
30
  blaze: async (input, ctx) => {
31
- const added = await ctx.cross('entity.add', input);
31
+ const added = await ctx.compose('entity.add', input);
32
32
  if (added.isErr()) return added;
33
33
  return Result.ok({ entity: added.value });
34
34
  },
@@ -41,20 +41,20 @@ const onboard = trail('entity.onboard', {
41
41
 
42
42
  | Export | What it does |
43
43
  | --- | --- |
44
- | `trail(id, spec)` | Define a unit of work with typed input and `Result` output; use `crosses` for composition |
44
+ | `trail(id, spec)` | Define a unit of work with typed input and `Result` output; use `composes` for composition |
45
45
  | `signal(id, spec)` | Define a server-originated notification with a typed data schema |
46
46
  | `resource(id, spec)` | Define an infrastructure dependency with `create`, `dispose`, and optional `mock` |
47
47
  | `drainResources(resources, ctx, configValues?)` | Evict and dispose cached resource singletons for surface/test shutdown |
48
48
  | `blobRefSchema` / `createBlobRef(...)` | Declare and create binary output references with a shared descriptor contract |
49
49
  | `topo(name, ...modules, options?)` | Collect trail modules into a queryable topology with optional `observe:` sinks |
50
50
  | `deriveTrail(contour, operation, spec)` | Derive CRUD-shaped trail contracts from a contour on the `@ontrails/core/trails` subpath |
51
- | `validateTopo(topo)` | Structural validation: cross targets exist, no cycles, examples parse, output schemas present |
51
+ | `validateTopo(topo)` | Structural validation: compose targets exist, no cycles, examples parse, output schemas present |
52
52
 
53
53
  ### Execution
54
54
 
55
55
  | Export | What it does |
56
56
  | --- | --- |
57
- | `executeTrail(trail, rawInput, options?)` | Centralized execution pipeline: validates input, builds context, composes layers, runs the implementation. Never throws -- exceptions become `Result.err(InternalError)`. |
57
+ | `executeTrail(trail, rawInput, options?)` | Centralized execution pipeline: validates input, builds context, composes layers, runs the blazed trail. Never throws -- exceptions become `Result.err(InternalError)`. |
58
58
  | `run(topo, id, input, options?)` | Headless trail execution by ID. Looks up the trail in the topo, then delegates to `executeTrail`. Returns `Result.err(NotFoundError)` if the ID is not registered. |
59
59
 
60
60
  ```typescript
@@ -115,8 +115,7 @@ result.unwrapOr(fallback); // Value or fallback
115
115
 
116
116
  ### Error taxonomy
117
117
 
118
- The current taxonomy is generated from the `errorClasses` owner registry and
119
- category code maps in `@ontrails/core`.
118
+ The current taxonomy is generated from the `errorClasses` owner registry and category code maps in `@ontrails/core`.
120
119
 
121
120
  <!-- error-taxonomy:start -->
122
121
  <!-- GENERATED: run `bun run error-taxonomy:sync`; check with `bun run error-taxonomy:check`. Variant: category. -->
@@ -124,7 +123,7 @@ category code maps in `@ontrails/core`.
124
123
  | Category | CLI Exit | HTTP | JSON-RPC | Retryable | Fixed Classes |
125
124
  | --- | --- | --- | --- | --- | --- |
126
125
  | `validation` | 1 | 400 | -32602 | No | `ValidationError`, `AmbiguousError` |
127
- | `not_found` | 2 | 404 | -32601 | No | `NotFoundError` |
126
+ | `not_found` | 2 | 404 | -32601 | No | `NotFoundError`, `VersionNotSupportedError` |
128
127
  | `conflict` | 3 | 409 | -32603 | No | `AlreadyExistsError`, `ConflictError` |
129
128
  | `permission` | 4 | 403 | -32600 | No | `PermissionError`, `PermitError` |
130
129
  | `timeout` | 5 | 504 | -32603 | Yes | `TimeoutError` |
@@ -139,11 +138,7 @@ Dynamic classes:
139
138
  - `RetryExhaustedError` inherits category and surface codes from its wrapped `TrailsError`; retryable is always No.
140
139
  <!-- error-taxonomy:end -->
141
140
 
142
- Public surface projections redact sensitive substrings before exposing a
143
- non-internal `TrailsError` message. Internal-category `TrailsError` instances and
144
- unknown native errors project with the generic message `Internal server error`;
145
- diagnostics and serialized payloads keep their useful structure while redacting
146
- messages, context, and stack strings.
141
+ Public surface projections redact sensitive substrings before exposing a non-internal `TrailsError` message. Internal-category `TrailsError` instances and unknown native errors project with the generic message `Internal server error`; diagnostics and serialized payloads keep their useful structure while redacting messages, context, and stack strings.
147
142
 
148
143
  The developer returns `Result.err(new NotFoundError(...))`. The framework maps it to the right code on every surface.
149
144
 
@@ -163,19 +158,11 @@ The developer returns `Result.err(new NotFoundError(...))`. The framework maps i
163
158
 
164
159
  ### Public helper boundaries
165
160
 
166
- The root package also exposes a few low-level contracts that other framework
167
- packages build on:
161
+ The root package also exposes a few low-level contracts that other framework packages build on:
168
162
 
169
- - **Intrinsic tracing** -- `TraceRecord`, `TraceSink`, `TraceContext`, and the
170
- sink registry helpers are the core-owned execution record shape shared by
171
- `@ontrails/observe`, `@ontrails/tracing`, and adapters.
172
- - **Trails DB** -- `deriveTrailsDbPath`, `deriveTrailsDir`,
173
- `ensureSubsystemSchema`, `openReadTrailsDb`, and `openWriteTrailsDb` are the
174
- generic database primitive used by framework subsystems.
175
- - **Surface projection helpers** -- safe error projection, layer field
176
- projection, cross-batch validation, late-bound signal references, and Zod
177
- default-wrapper stripping are stable root exports for first-party surfaces,
178
- store helpers, and tests.
163
+ - **Intrinsic tracing** -- `TraceRecord`, `TraceSink`, `TraceContext`, and the sink registry helpers are the core-owned execution record shape shared by `@ontrails/observe`, `@ontrails/tracing`, and adapters.
164
+ - **Trails DB** -- `deriveTrailsDbPath`, `deriveTrailsDir`, `ensureSubsystemSchema`, `openReadTrailsDb`, and `openWriteTrailsDb` are the generic database primitive used by framework subsystems.
165
+ - **Surface projection helpers** -- safe error projection, layer field projection, compose-batch validation, late-bound signal references, and Zod default-wrapper stripping are stable root exports for first-party surfaces, store helpers, and tests.
179
166
 
180
167
  See the [API Reference](../../docs/api-reference.md) for the full list.
181
168
 
@@ -196,5 +183,5 @@ Types `ReadOnlyTopoStore`, `MockTopoStoreSeed`, `TopoSnapshot`, `TopoStoreRef`,
196
183
  ## Installation
197
184
 
198
185
  ```bash
199
- bun add @ontrails/core zod
186
+ bun add @ontrails/core@beta zod
200
187
  ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ontrails/core",
3
- "version": "1.0.0-beta.17",
3
+ "version": "1.0.0-beta.19",
4
4
  "files": [
5
5
  "src/**/*.ts",
6
6
  "!src/**/__tests__/**",
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Shared helpers for `ctx.cross([...])` batch execution.
2
+ * Shared helpers for `ctx.compose([...])` batch execution.
3
3
  *
4
4
  * These helpers normalize batch options, produce validation results, and
5
5
  * implement the unlimited/limited worker-pool execution strategies used by
@@ -11,18 +11,18 @@
11
11
 
12
12
  import { ValidationError } from './errors.js';
13
13
  import { Result } from './result.js';
14
- import type { CrossBatchOptions } from './types.js';
14
+ import type { ComposeBatchOptions } from './types.js';
15
15
 
16
16
  /**
17
- * Validate the `concurrency` option on a batch `ctx.cross()` call.
17
+ * Validate the `concurrency` option on a batch `ctx.compose()` call.
18
18
  *
19
19
  * Returns `Ok(undefined)` when no limit is requested, `Ok(n)` when a
20
20
  * positive integer is supplied, and `Err(ValidationError)` for any other
21
21
  * value. The error message is load-bearing: callers and tests depend on
22
22
  * the exact string.
23
23
  */
24
- export const normalizeCrossBatchConcurrency = (
25
- options: CrossBatchOptions | undefined
24
+ export const normalizeComposeBatchConcurrency = (
25
+ options: ComposeBatchOptions | undefined
26
26
  ): Result<number | undefined, Error> => {
27
27
  const concurrency = options?.concurrency;
28
28
  if (concurrency === undefined) {
@@ -32,7 +32,7 @@ export const normalizeCrossBatchConcurrency = (
32
32
  if (!Number.isInteger(concurrency) || concurrency < 1) {
33
33
  return Result.err(
34
34
  new ValidationError(
35
- 'ctx.cross() batch concurrency must be a positive integer'
35
+ 'ctx.compose() batch concurrency must be a positive integer'
36
36
  )
37
37
  );
38
38
  }
@@ -42,10 +42,10 @@ export const normalizeCrossBatchConcurrency = (
42
42
 
43
43
  /**
44
44
  * Produce one validation-error result per call, preserving the original
45
- * call order. Used when `normalizeCrossBatchConcurrency` fails so the caller
46
- * can surface a uniform batch shape to the trail implementation.
45
+ * call order. Used when `normalizeComposeBatchConcurrency` fails so the caller
46
+ * can surface a uniform batch shape to the blaze.
47
47
  */
48
- export const createCrossBatchValidationResults = <TCall>(
48
+ export const createComposeBatchValidationResults = <TCall>(
49
49
  calls: readonly TCall[],
50
50
  error: Error
51
51
  ): Result<unknown, Error>[] => calls.map(() => Result.err(error));
@@ -55,7 +55,7 @@ export const createCrossBatchValidationResults = <TCall>(
55
55
  * multiple worker coroutines because JavaScript is single-threaded between
56
56
  * awaits — the read/increment pair runs without interleaving.
57
57
  */
58
- export const claimNextCrossBatchIndex = <TCall>(
58
+ export const claimNextComposeBatchIndex = <TCall>(
59
59
  nextIndex: { value: number },
60
60
  calls: readonly TCall[]
61
61
  ): number | undefined => {
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Compose-invocation schema merging for trails with `composeInput`.
3
+ *
4
+ * When a trail declares `composeInput`, callers via `ctx.compose()` pass both
5
+ * public input and composition-only fields. The merged schema validates the
6
+ * combined shape so `executeTrail` doesn't reject the extra fields.
7
+ */
8
+
9
+ import { z } from 'zod';
10
+
11
+ import type { AnyTrail } from './trail.js';
12
+
13
+ /**
14
+ * Build the validation schema for a compose-invoked trail.
15
+ *
16
+ * When the target trail declares `composeInput`, returns the intersection of
17
+ * `trail.input` and `trail.composeInput`. Returns `undefined` when no
18
+ * `composeInput` is declared, signaling that normal input validation suffices.
19
+ */
20
+ export const buildComposeValidationSchema = (
21
+ trailDef: AnyTrail
22
+ ): z.ZodType | undefined => {
23
+ if (!trailDef.composeInput) {
24
+ return undefined;
25
+ }
26
+ // Prefer .merge() for ZodObject pairs — produces a proper merged object
27
+ // schema that strips unknown keys and exposes .shape. Fall back to
28
+ // z.intersection for non-object schemas.
29
+ if (
30
+ trailDef.input instanceof z.ZodObject &&
31
+ trailDef.composeInput instanceof z.ZodObject
32
+ ) {
33
+ return trailDef.input.merge(trailDef.composeInput);
34
+ }
35
+ return z.intersection(trailDef.input, trailDef.composeInput);
36
+ };
package/src/contour.ts CHANGED
@@ -13,6 +13,8 @@ export interface ContourOptions<
13
13
  readonly identity: TIdentity;
14
14
  /** Example instances validated against the contour schema at declaration time. */
15
15
  readonly examples?: readonly z.output<z.ZodObject<TShape>>[] | undefined;
16
+ /** Reserved for future contour-specific design; trail versioning is trail-only. */
17
+ readonly version?: never;
16
18
  }
17
19
 
18
20
  /** Type-level brand name applied to a contour's identity schema. */
package/src/draft.ts CHANGED
@@ -10,7 +10,7 @@ import type { AnyTrail } from './trail.js';
10
10
  export const DRAFT_ID_PREFIX = '_draft.';
11
11
 
12
12
  export type DraftDependencyKind =
13
- | 'cross'
13
+ | 'compose'
14
14
  | 'contour'
15
15
  | 'resource'
16
16
  | 'replaced-by'
@@ -88,7 +88,7 @@ const trailDependencies = (trail: AnyTrail): DraftDependency[] => [
88
88
  (trail.contours ?? []).map((contour) => contour.name),
89
89
  'contour'
90
90
  ),
91
- ...dependenciesFromIds(trail.id, trail.crosses, 'cross'),
91
+ ...dependenciesFromIds(trail.id, trail.composes, 'compose'),
92
92
  ...dependenciesFromIds(
93
93
  trail.id,
94
94
  trail.resources.map(({ id }) => id),
package/src/errors.ts CHANGED
@@ -68,6 +68,64 @@ export class NotFoundError extends TrailsError {
68
68
  readonly retryable = false as const;
69
69
  }
70
70
 
71
+ export class VersionNotSupportedError extends NotFoundError {
72
+ readonly reason?: string | undefined;
73
+ readonly requested?: number | string | undefined;
74
+ readonly supported?: readonly number[] | undefined;
75
+ readonly trailId?: string | undefined;
76
+
77
+ constructor(
78
+ message: string,
79
+ options?: { cause?: Error; context?: Record<string, unknown> }
80
+ );
81
+ constructor(
82
+ trailId: string,
83
+ requested: number | string,
84
+ supported: readonly number[],
85
+ reason?: string | undefined
86
+ );
87
+ constructor(
88
+ messageOrTrailId: string,
89
+ optionsOrRequested?:
90
+ | { cause?: Error; context?: Record<string, unknown> }
91
+ | number
92
+ | string,
93
+ supported?: readonly number[],
94
+ reason?: string | undefined
95
+ ) {
96
+ if (supported === undefined) {
97
+ super(
98
+ messageOrTrailId,
99
+ optionsOrRequested as
100
+ | { cause?: Error; context?: Record<string, unknown> }
101
+ | undefined
102
+ );
103
+ this.name = 'VersionNotSupportedError';
104
+ return;
105
+ }
106
+
107
+ const requested = optionsOrRequested as number | string;
108
+ const supportedLabel =
109
+ supported.length === 0 ? 'none' : supported.join(', ');
110
+ super(
111
+ `Trail "${messageOrTrailId}" version ${String(requested)} is not supported (supported: ${supportedLabel})`,
112
+ {
113
+ context: {
114
+ ...(reason === undefined ? {} : { reason }),
115
+ requested,
116
+ supported,
117
+ trailId: messageOrTrailId,
118
+ },
119
+ }
120
+ );
121
+ this.name = 'VersionNotSupportedError';
122
+ this.reason = reason;
123
+ this.requested = requested;
124
+ this.supported = supported;
125
+ this.trailId = messageOrTrailId;
126
+ }
127
+ }
128
+
71
129
  export class AlreadyExistsError extends TrailsError {
72
130
  readonly category = 'conflict' as const;
73
131
  readonly retryable = false as const;
@@ -148,7 +206,7 @@ export class CancelledError extends TrailsError {
148
206
  *
149
207
  * Inherits the wrapped error's category for surface mapping (e.g. a
150
208
  * `RetryExhaustedError<ConflictError>` maps to HTTP 409), but always
151
- * sets `retryable = false` to prevent amplification across `ctx.cross()`
209
+ * sets `retryable = false` to prevent amplification across `ctx.compose()`
152
210
  * boundaries or stacked layers.
153
211
  */
154
212
  export class RetryExhaustedError<
@@ -238,6 +296,12 @@ export const errorClasses = [
238
296
  name: 'NotFoundError',
239
297
  retryable: false,
240
298
  },
299
+ {
300
+ category: 'not_found',
301
+ ctor: VersionNotSupportedError,
302
+ name: 'VersionNotSupportedError',
303
+ retryable: false,
304
+ },
241
305
  {
242
306
  category: 'conflict',
243
307
  ctor: AlreadyExistsError,