@ontrails/trails 1.0.0-beta.10 → 1.0.0-beta.11

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.
@@ -1,3 +1,3 @@
1
1
  $ oxlint ./src
2
2
  Found 0 warnings and 0 errors.
3
- Finished in 70ms on 18 files with 93 rules using 24 threads.
3
+ Finished in 69ms on 18 files with 93 rules using 24 threads.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,32 @@
1
1
  # trails
2
2
 
3
+ ## 1.0.0-beta.11
4
+
5
+ ### Patch Changes
6
+
7
+ - Add services as a first-class primitive.
8
+
9
+ Services make infrastructure dependencies declarative, injectable, and governable. Define a service with `service()`, declare it on a trail with `services: [db]`, and access it with `db.from(ctx)` or `ctx.service()`.
10
+
11
+ **Core:** `service()` factory, `ServiceSpec<T>`, `ServiceContext`, singleton resolution in `executeTrail`, in-flight creation dedup, `isService` guard, `findDuplicateServiceId`, topo service discovery and validation, `services` field on trail specs.
12
+
13
+ **Testing:** Auto-resolution of `mock` factories in `testAll`, `testExamples`, `testContracts`, and `testFollows`. Explicit `services` overrides with correct precedence (`explicit > ctx.extensions > auto-mock`). Service mock propagation through follow graphs.
14
+
15
+ **Warden:** `service-declarations` rule validates `db.from(ctx)` and `ctx.service()` usage matches declared `services: [...]`. `service-exists` rule validates declared service IDs resolve in project context. Scope-aware AST walking skips nested function boundaries.
16
+
17
+ **Surfaces:** Service overrides thread through `dispatch` and `blaze` on CLI, MCP, and HTTP.
18
+
19
+ **Introspection:** Survey and surface map outputs include service graph. Topo exposes `.services`, `.getService()`, `.hasService()`, `.listServices()`, `.serviceIds()`, `.serviceCount`.
20
+
21
+ **Docs:** ADR-009 accepted. Unified services guide, updated vocabulary, getting-started, architecture, and package READMEs.
22
+
23
+ - Updated dependencies
24
+ - @ontrails/core@1.0.0-beta.11
25
+ - @ontrails/warden@1.0.0-beta.11
26
+ - @ontrails/cli@1.0.0-beta.11
27
+ - @ontrails/schema@1.0.0-beta.11
28
+ - @ontrails/logging@1.0.0-beta.11
29
+
3
30
  ## 1.0.0-beta.10
4
31
 
5
32
  ### Patch Changes
@@ -10,6 +10,7 @@ export interface BriefReport {
10
10
  readonly version: string;
11
11
  readonly contractVersion: string;
12
12
  readonly features: {
13
+ readonly services: boolean;
13
14
  readonly outputSchemas: boolean;
14
15
  readonly examples: boolean;
15
16
  readonly detours: boolean;
@@ -17,9 +18,48 @@ export interface BriefReport {
17
18
  };
18
19
  readonly trails: number;
19
20
  readonly events: number;
21
+ readonly services: number;
22
+ }
23
+ export interface SurveyListReport {
24
+ readonly count: number;
25
+ readonly entries: readonly {
26
+ readonly examples: number;
27
+ readonly id: string;
28
+ readonly kind: string;
29
+ readonly safety: string;
30
+ }[];
31
+ readonly serviceCount: number;
32
+ readonly services: readonly {
33
+ readonly description: string | null;
34
+ readonly health: 'available' | 'none';
35
+ readonly id: string;
36
+ readonly kind: 'service';
37
+ readonly lifetime: 'singleton';
38
+ readonly usedBy: readonly string[];
39
+ }[];
40
+ }
41
+ export interface TrailDetailReport {
42
+ readonly description: string | null;
43
+ readonly detours: Trail<unknown, unknown>['detours'] | null;
44
+ readonly examples: readonly unknown[];
45
+ readonly follow: readonly string[];
46
+ readonly id: string;
47
+ readonly intent: 'read' | 'write' | 'destroy';
48
+ readonly kind: string;
49
+ readonly safety: string;
50
+ readonly services: readonly string[];
20
51
  }
21
52
  /** Generate a compact capability report for the given topo. */
22
53
  export declare const generateBriefReport: (app: Topo) => BriefReport;
54
+ export declare const generateSurveyList: (app: Topo) => SurveyListReport;
55
+ /**
56
+ * Build a human-readable detail view for a single trail.
57
+ *
58
+ * Overlaps with `trailToEntry` in `@ontrails/schema` which builds the
59
+ * surface-map entry. The two serve different audiences (human display vs
60
+ * machine-diffable surface map) so they are kept separate.
61
+ */
62
+ export declare const generateTrailDetail: (item: Trail<unknown, unknown>) => TrailDetailReport;
23
63
  export declare const surveyTrail: Trail<{
24
64
  breakingOnly: boolean;
25
65
  brief: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"survey.d.ts","sourceRoot":"","sources":["../../../src/trails/survey.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAoBlD,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,QAAQ,EAAE;QACjB,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;QAChC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;QAC3B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;QAC1B,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;KAC1B,CAAC;IACF,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAwBD,+DAA+D;AAC/D,eAAO,MAAM,mBAAmB,GAAI,KAAK,IAAI,KAAG,WAgB/C,CAAC;AA2KF,eAAO,MAAM,WAAW;;;;;;;;UA2GtB,CAAC"}
1
+ {"version":3,"file":"survey.d.ts","sourceRoot":"","sources":["../../../src/trails/survey.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAoBlD,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,QAAQ,EAAE;QACjB,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;QAC3B,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;QAChC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;QAC3B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;QAC1B,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;KAC1B,CAAC;IACF,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,SAAS;QACzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;QAC1B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;QACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;KACzB,EAAE,CAAC;IACJ,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,QAAQ,EAAE,SAAS;QAC1B,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;QACpC,QAAQ,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAAC;QACtC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;QACpB,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;QACzB,QAAQ,CAAC,QAAQ,EAAE,WAAW,CAAC;QAC/B,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;KACpC,EAAE,CAAC;CACL;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,QAAQ,CAAC,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,CAAC;IAC5D,QAAQ,CAAC,QAAQ,EAAE,SAAS,OAAO,EAAE,CAAC;IACtC,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC;IAC9C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;CACtC;AAiCD,+DAA+D;AAC/D,eAAO,MAAM,mBAAmB,GAAI,KAAK,IAAI,KAAG,WAmB/C,CAAC;AAwDF,eAAO,MAAM,kBAAkB,GAAI,KAAK,IAAI,KAAG,gBA4B9C,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,GAC9B,MAAM,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,KAC5B,iBAgBF,CAAC;AA+HF,eAAO,MAAM,WAAW;;;;;;;;UAmItB,CAAC"}
@@ -22,11 +22,12 @@ const detectFeatures = (app) => {
22
22
  hasDetours: trails.some((r) => trailHas(r, 'detours')),
23
23
  hasExamples: trails.some((r) => trailHas(r, 'examples')),
24
24
  hasOutputSchemas: trails.some((r) => trailHas(r, 'output')),
25
+ hasServices: trails.some((r) => Array.isArray(r['services']) && r['services'].length > 0),
25
26
  };
26
27
  };
27
28
  /** Generate a compact capability report for the given topo. */
28
29
  export const generateBriefReport = (app) => {
29
- const { hasDetours, hasExamples, hasOutputSchemas } = detectFeatures(app);
30
+ const { hasDetours, hasExamples, hasOutputSchemas, hasServices } = detectFeatures(app);
30
31
  return {
31
32
  contractVersion: '2026-03',
32
33
  events: app.events.size,
@@ -35,8 +36,10 @@ export const generateBriefReport = (app) => {
35
36
  events: app.events.size > 0,
36
37
  examples: hasExamples,
37
38
  outputSchemas: hasOutputSchemas,
39
+ services: hasServices,
38
40
  },
39
41
  name: app.name,
42
+ services: app.services.size,
40
43
  trails: app.trails.size,
41
44
  version: '0.1.0',
42
45
  };
@@ -53,7 +56,33 @@ const safetyLabel = (entry) => {
53
56
  }
54
57
  return '-';
55
58
  };
56
- const formatTrailList = (app) => {
59
+ const buildServiceUsage = (app) => {
60
+ const usage = new Map();
61
+ for (const trailDef of app.list()) {
62
+ for (const declaredService of trailDef.services) {
63
+ const users = usage.get(declaredService.id) ?? [];
64
+ users.push(trailDef.id);
65
+ usage.set(declaredService.id, users);
66
+ }
67
+ }
68
+ return new Map([...usage.entries()].map(([id, users]) => [id, users.toSorted()]));
69
+ };
70
+ const serviceHealthStatus = (service) => service.health === undefined ? 'none' : 'available';
71
+ const formatServiceList = (app) => {
72
+ const usage = buildServiceUsage(app);
73
+ return app
74
+ .listServices()
75
+ .map((service) => ({
76
+ description: service.description ?? null,
77
+ health: serviceHealthStatus(service),
78
+ id: service.id,
79
+ kind: service.kind,
80
+ lifetime: 'singleton',
81
+ usedBy: usage.get(service.id) ?? [],
82
+ }))
83
+ .toSorted((a, b) => a.id.localeCompare(b.id));
84
+ };
85
+ export const generateSurveyList = (app) => {
57
86
  const items = app.list();
58
87
  const entries = items.map((item) => {
59
88
  const safety = safetyLabel(item);
@@ -67,7 +96,13 @@ const formatTrailList = (app) => {
67
96
  safety,
68
97
  };
69
98
  });
70
- return { count: items.length, entries };
99
+ const services = formatServiceList(app);
100
+ return {
101
+ count: items.length,
102
+ entries,
103
+ serviceCount: services.length,
104
+ services,
105
+ };
71
106
  };
72
107
  /**
73
108
  * Build a human-readable detail view for a single trail.
@@ -76,15 +111,30 @@ const formatTrailList = (app) => {
76
111
  * surface-map entry. The two serve different audiences (human display vs
77
112
  * machine-diffable surface map) so they are kept separate.
78
113
  */
79
- const formatTrailDetail = (item) => {
114
+ export const generateTrailDetail = (item) => {
80
115
  const safety = safetyLabel(item);
81
116
  return {
82
117
  description: item.description ?? null,
83
118
  detours: item.detours ?? null,
84
119
  examples: item.examples ?? [],
120
+ follow: item.follow.toSorted(),
85
121
  id: item.id,
122
+ intent: item.intent,
86
123
  kind: item.kind,
87
124
  safety,
125
+ services: item.services.map((service) => service.id).toSorted(),
126
+ };
127
+ };
128
+ const formatServiceDetail = (app, serviceId) => {
129
+ const item = app.getService(serviceId);
130
+ const usedBy = buildServiceUsage(app).get(serviceId) ?? [];
131
+ return {
132
+ description: item?.description ?? null,
133
+ health: item ? serviceHealthStatus(item) : 'none',
134
+ id: serviceId,
135
+ kind: 'service',
136
+ lifetime: 'singleton',
137
+ usedBy,
88
138
  };
89
139
  };
90
140
  const formatDiff = (diff) => ({
@@ -111,10 +161,13 @@ const buildSurveyDiff = async (app, breakingOnly) => {
111
161
  };
112
162
  const buildSurveyDetail = (app, trailId) => {
113
163
  const item = app.get(trailId);
114
- if (!item) {
115
- return Result.err(new Error(`Trail not found: ${trailId}`));
164
+ if (item) {
165
+ return Result.ok(generateTrailDetail(item));
166
+ }
167
+ if (app.getService(trailId)) {
168
+ return Result.ok(formatServiceDetail(app, trailId));
116
169
  }
117
- return Result.ok(formatTrailDetail(item));
170
+ return Result.err(new Error(`Trail or service not found: ${trailId}`));
118
171
  };
119
172
  const buildSurveyGenerate = async (app) => {
120
173
  const surfaceMap = generateSurfaceMap(app);
@@ -139,7 +192,7 @@ const surveyHandlers = {
139
192
  detail: (app, input) => buildSurveyDetail(app, input.trailId ?? ''),
140
193
  diff: (app, input) => buildSurveyDiff(app, input.breakingOnly),
141
194
  generate: (app) => buildSurveyGenerate(app),
142
- list: (app) => Result.ok(formatTrailList(app)),
195
+ list: (app) => Result.ok(generateSurveyList(app)),
143
196
  openapi: (app) => Result.ok(generateOpenApiSpec(app)),
144
197
  };
145
198
  /** Dispatch to the appropriate survey sub-command based on input flags. */
@@ -198,6 +251,15 @@ export const surveyTrail = trail('survey', {
198
251
  kind: z.string(),
199
252
  safety: z.string(),
200
253
  })),
254
+ serviceCount: z.number(),
255
+ services: z.array(z.object({
256
+ description: z.string().nullable(),
257
+ health: z.enum(['available', 'none']),
258
+ id: z.string(),
259
+ kind: z.literal('service'),
260
+ lifetime: z.literal('singleton'),
261
+ usedBy: z.array(z.string()),
262
+ })),
201
263
  }),
202
264
  z.object({
203
265
  contractVersion: z.string(),
@@ -207,8 +269,10 @@ export const surveyTrail = trail('survey', {
207
269
  events: z.boolean(),
208
270
  examples: z.boolean(),
209
271
  outputSchemas: z.boolean(),
272
+ services: z.boolean(),
210
273
  }),
211
274
  name: z.string(),
275
+ services: z.number(),
212
276
  trails: z.number(),
213
277
  version: z.string(),
214
278
  }),
@@ -222,9 +286,20 @@ export const surveyTrail = trail('survey', {
222
286
  description: z.unknown().nullable(),
223
287
  detours: z.unknown().nullable(),
224
288
  examples: z.array(z.unknown()),
289
+ follow: z.array(z.string()),
225
290
  id: z.string(),
291
+ intent: z.enum(['read', 'write', 'destroy']),
226
292
  kind: z.string(),
227
293
  safety: z.string(),
294
+ services: z.array(z.string()),
295
+ }),
296
+ z.object({
297
+ description: z.string().nullable(),
298
+ health: z.enum(['available', 'none']),
299
+ id: z.string(),
300
+ kind: z.literal('service'),
301
+ lifetime: z.literal('singleton'),
302
+ usedBy: z.array(z.string()),
228
303
  }),
229
304
  z.object({
230
305
  hash: z.string(),
@@ -1 +1 @@
1
- {"version":3,"file":"survey.js","sourceRoot":"","sources":["../../../src/trails/survey.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"survey.js","sourceRoot":"","sources":["../../../src/trails/survey.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ontrails/trails",
3
- "version": "1.0.0-beta.10",
3
+ "version": "1.0.0-beta.11",
4
4
  "bin": {
5
5
  "trails": "./bin/trails.ts"
6
6
  },
@@ -1,6 +1,6 @@
1
1
  import { describe, expect, test } from 'bun:test';
2
2
 
3
- import { topo, trail, Result } from '@ontrails/core';
3
+ import { Result, service, topo, trail } from '@ontrails/core';
4
4
  import {
5
5
  generateSurfaceMap,
6
6
  hashSurfaceMap,
@@ -9,8 +9,16 @@ import {
9
9
  import type { SurfaceMap } from '@ontrails/schema';
10
10
  import { z } from 'zod';
11
11
 
12
- import { generateBriefReport } from '../trails/survey.js';
13
- import type { BriefReport } from '../trails/survey.js';
12
+ import {
13
+ generateBriefReport,
14
+ generateSurveyList,
15
+ generateTrailDetail,
16
+ } from '../trails/survey.js';
17
+ import type {
18
+ BriefReport,
19
+ SurveyListReport,
20
+ TrailDetailReport,
21
+ } from '../trails/survey.js';
14
22
 
15
23
  // ---------------------------------------------------------------------------
16
24
  // Test fixtures
@@ -35,6 +43,11 @@ const helloTrail = trail('hello', {
35
43
  const name = input.name ?? 'world';
36
44
  return Result.ok({ message: `Hello, ${name}!` });
37
45
  },
46
+ services: [
47
+ service('db.main', {
48
+ create: () => Result.ok({ source: 'factory' }),
49
+ }),
50
+ ],
38
51
  });
39
52
 
40
53
  const byeTrail = trail('bye', {
@@ -44,7 +57,16 @@ const byeTrail = trail('bye', {
44
57
  run: (input) => Result.ok({ message: `Goodbye, ${input.name}!` }),
45
58
  });
46
59
 
47
- const app = topo('test-app', { bye: byeTrail, hello: helloTrail });
60
+ const [dbService] = helloTrail.services;
61
+ if (!dbService) {
62
+ throw new Error('Expected helloTrail to declare db.main');
63
+ }
64
+
65
+ const app = topo('test-app', {
66
+ bye: byeTrail,
67
+ dbService,
68
+ hello: helloTrail,
69
+ });
48
70
 
49
71
  // ---------------------------------------------------------------------------
50
72
  // Tests
@@ -53,10 +75,11 @@ const app = topo('test-app', { bye: byeTrail, hello: helloTrail });
53
75
  describe('trails survey', () => {
54
76
  test('generateSurfaceMap includes all trails', () => {
55
77
  const surfaceMap = generateSurfaceMap(app);
56
- expect(surfaceMap.entries.length).toBe(2);
78
+ expect(surfaceMap.entries.length).toBe(3);
57
79
  const ids = surfaceMap.entries.map((e) => e.id);
58
80
  expect(ids).toContain('hello');
59
81
  expect(ids).toContain('bye');
82
+ expect(ids).toContain('db.main');
60
83
  });
61
84
 
62
85
  test('surface map entries have expected fields', () => {
@@ -66,6 +89,7 @@ describe('trails survey', () => {
66
89
  expect(hello?.kind).toBe('trail');
67
90
  expect(hello?.intent).toBe('read');
68
91
  expect(hello?.exampleCount).toBe(1);
92
+ expect(hello?.services).toEqual(['db.main']);
69
93
  });
70
94
 
71
95
  test('JSON output is valid JSON', () => {
@@ -73,7 +97,7 @@ describe('trails survey', () => {
73
97
  const json = JSON.stringify(surfaceMap, null, 2);
74
98
  const parsed = JSON.parse(json) as SurfaceMap;
75
99
  expect(parsed.version).toBe('1.0');
76
- expect(parsed.entries.length).toBe(2);
100
+ expect(parsed.entries.length).toBe(3);
77
101
  });
78
102
 
79
103
  test('hashSurfaceMap produces stable hash', () => {
@@ -130,6 +154,7 @@ describe('trails survey --brief', () => {
130
154
  const report = generateBriefReport(app);
131
155
  expect(report.trails).toBe(2);
132
156
  expect(report.events).toBe(0);
157
+ expect(report.services).toBe(1);
133
158
  });
134
159
 
135
160
  test('detects features in use', () => {
@@ -138,6 +163,7 @@ describe('trails survey --brief', () => {
138
163
  expect(report.features.examples).toBe(true);
139
164
  expect(report.features.detours).toBe(true);
140
165
  expect(report.features.events).toBe(false);
166
+ expect(report.features.services).toBe(true);
141
167
  });
142
168
 
143
169
  test('JSON output is valid', () => {
@@ -146,6 +172,7 @@ describe('trails survey --brief', () => {
146
172
  const parsed = JSON.parse(json) as BriefReport;
147
173
  expect(parsed.name).toBe('test-app');
148
174
  expect(parsed.trails).toBe(2);
175
+ expect(parsed.services).toBe(1);
149
176
  });
150
177
 
151
178
  test('empty app reports zero features', () => {
@@ -155,5 +182,35 @@ describe('trails survey --brief', () => {
155
182
  expect(report.features.outputSchemas).toBe(false);
156
183
  expect(report.features.examples).toBe(false);
157
184
  expect(report.features.detours).toBe(false);
185
+ expect(report.features.services).toBe(false);
186
+ });
187
+ });
188
+
189
+ describe('trails survey detail', () => {
190
+ test('trail detail includes declared services, follow, and intent', () => {
191
+ const detail = generateTrailDetail(helloTrail);
192
+ const parsed = structuredClone(detail) as TrailDetailReport;
193
+
194
+ expect(parsed.follow).toEqual([]);
195
+ expect(parsed.intent).toBe('read');
196
+ expect(parsed.services).toEqual(['db.main']);
197
+ });
198
+ });
199
+
200
+ describe('trails survey services section', () => {
201
+ test('list output includes service lifetime and health status', () => {
202
+ const report = generateSurveyList(app);
203
+ const parsed = structuredClone(report) as SurveyListReport;
204
+ const db = parsed.services.find((entry) => entry.id === 'db.main');
205
+
206
+ expect(parsed.serviceCount).toBe(1);
207
+ expect(db).toEqual({
208
+ description: null,
209
+ health: 'none',
210
+ id: 'db.main',
211
+ kind: 'service',
212
+ lifetime: 'singleton',
213
+ usedBy: ['hello'],
214
+ });
158
215
  });
159
216
  });
@@ -30,6 +30,7 @@ export interface BriefReport {
30
30
  readonly version: string;
31
31
  readonly contractVersion: string;
32
32
  readonly features: {
33
+ readonly services: boolean;
33
34
  readonly outputSchemas: boolean;
34
35
  readonly examples: boolean;
35
36
  readonly detours: boolean;
@@ -37,6 +38,38 @@ export interface BriefReport {
37
38
  };
38
39
  readonly trails: number;
39
40
  readonly events: number;
41
+ readonly services: number;
42
+ }
43
+
44
+ export interface SurveyListReport {
45
+ readonly count: number;
46
+ readonly entries: readonly {
47
+ readonly examples: number;
48
+ readonly id: string;
49
+ readonly kind: string;
50
+ readonly safety: string;
51
+ }[];
52
+ readonly serviceCount: number;
53
+ readonly services: readonly {
54
+ readonly description: string | null;
55
+ readonly health: 'available' | 'none';
56
+ readonly id: string;
57
+ readonly kind: 'service';
58
+ readonly lifetime: 'singleton';
59
+ readonly usedBy: readonly string[];
60
+ }[];
61
+ }
62
+
63
+ export interface TrailDetailReport {
64
+ readonly description: string | null;
65
+ readonly detours: Trail<unknown, unknown>['detours'] | null;
66
+ readonly examples: readonly unknown[];
67
+ readonly follow: readonly string[];
68
+ readonly id: string;
69
+ readonly intent: 'read' | 'write' | 'destroy';
70
+ readonly kind: string;
71
+ readonly safety: string;
72
+ readonly services: readonly string[];
40
73
  }
41
74
 
42
75
  /** Check if a trail has a specific feature. */
@@ -50,7 +83,12 @@ const trailHas = (raw: Record<string, unknown>, key: string): boolean => {
50
83
  /** Detect which features are used across trails. */
51
84
  const detectFeatures = (
52
85
  app: Topo
53
- ): { hasDetours: boolean; hasExamples: boolean; hasOutputSchemas: boolean } => {
86
+ ): {
87
+ hasDetours: boolean;
88
+ hasExamples: boolean;
89
+ hasOutputSchemas: boolean;
90
+ hasServices: boolean;
91
+ } => {
54
92
  const trails = [...app.trails.values()].map(
55
93
  (item) => item as unknown as Record<string, unknown>
56
94
  );
@@ -58,12 +96,17 @@ const detectFeatures = (
58
96
  hasDetours: trails.some((r) => trailHas(r, 'detours')),
59
97
  hasExamples: trails.some((r) => trailHas(r, 'examples')),
60
98
  hasOutputSchemas: trails.some((r) => trailHas(r, 'output')),
99
+ hasServices: trails.some(
100
+ (r) =>
101
+ Array.isArray(r['services']) && (r['services'] as unknown[]).length > 0
102
+ ),
61
103
  };
62
104
  };
63
105
 
64
106
  /** Generate a compact capability report for the given topo. */
65
107
  export const generateBriefReport = (app: Topo): BriefReport => {
66
- const { hasDetours, hasExamples, hasOutputSchemas } = detectFeatures(app);
108
+ const { hasDetours, hasExamples, hasOutputSchemas, hasServices } =
109
+ detectFeatures(app);
67
110
 
68
111
  return {
69
112
  contractVersion: '2026-03',
@@ -73,8 +116,10 @@ export const generateBriefReport = (app: Topo): BriefReport => {
73
116
  events: app.events.size > 0,
74
117
  examples: hasExamples,
75
118
  outputSchemas: hasOutputSchemas,
119
+ services: hasServices,
76
120
  },
77
121
  name: app.name,
122
+ services: app.services.size,
78
123
  trails: app.trails.size,
79
124
  version: '0.1.0',
80
125
  };
@@ -96,7 +141,45 @@ const safetyLabel = (entry: {
96
141
  return '-';
97
142
  };
98
143
 
99
- const formatTrailList = (app: Topo): object => {
144
+ const buildServiceUsage = (
145
+ app: Topo
146
+ ): ReadonlyMap<string, readonly string[]> => {
147
+ const usage = new Map<string, string[]>();
148
+
149
+ for (const trailDef of app.list()) {
150
+ for (const declaredService of trailDef.services) {
151
+ const users = usage.get(declaredService.id) ?? [];
152
+ users.push(trailDef.id);
153
+ usage.set(declaredService.id, users);
154
+ }
155
+ }
156
+
157
+ return new Map(
158
+ [...usage.entries()].map(([id, users]) => [id, users.toSorted()] as const)
159
+ );
160
+ };
161
+
162
+ const serviceHealthStatus = (service: {
163
+ health?: unknown;
164
+ }): 'available' | 'none' =>
165
+ service.health === undefined ? 'none' : 'available';
166
+
167
+ const formatServiceList = (app: Topo): SurveyListReport['services'] => {
168
+ const usage = buildServiceUsage(app);
169
+ return app
170
+ .listServices()
171
+ .map((service) => ({
172
+ description: service.description ?? null,
173
+ health: serviceHealthStatus(service),
174
+ id: service.id,
175
+ kind: service.kind,
176
+ lifetime: 'singleton' as const,
177
+ usedBy: usage.get(service.id) ?? [],
178
+ }))
179
+ .toSorted((a, b) => a.id.localeCompare(b.id));
180
+ };
181
+
182
+ export const generateSurveyList = (app: Topo): SurveyListReport => {
100
183
  const items = app.list();
101
184
  const entries = items.map((item) => {
102
185
  const safety = safetyLabel(
@@ -116,7 +199,14 @@ const formatTrailList = (app: Topo): object => {
116
199
  };
117
200
  });
118
201
 
119
- return { count: items.length, entries };
202
+ const services = formatServiceList(app);
203
+
204
+ return {
205
+ count: items.length,
206
+ entries,
207
+ serviceCount: services.length,
208
+ services,
209
+ };
120
210
  };
121
211
 
122
212
  /**
@@ -126,7 +216,9 @@ const formatTrailList = (app: Topo): object => {
126
216
  * surface-map entry. The two serve different audiences (human display vs
127
217
  * machine-diffable surface map) so they are kept separate.
128
218
  */
129
- const formatTrailDetail = (item: Trail<unknown, unknown>): object => {
219
+ export const generateTrailDetail = (
220
+ item: Trail<unknown, unknown>
221
+ ): TrailDetailReport => {
130
222
  const safety = safetyLabel(
131
223
  item as unknown as { intent?: 'read' | 'write' | 'destroy' }
132
224
  );
@@ -135,9 +227,26 @@ const formatTrailDetail = (item: Trail<unknown, unknown>): object => {
135
227
  description: item.description ?? null,
136
228
  detours: item.detours ?? null,
137
229
  examples: item.examples ?? [],
230
+ follow: item.follow.toSorted(),
138
231
  id: item.id,
232
+ intent: item.intent,
139
233
  kind: item.kind,
140
234
  safety,
235
+ services: item.services.map((service) => service.id).toSorted(),
236
+ };
237
+ };
238
+
239
+ const formatServiceDetail = (app: Topo, serviceId: string): object => {
240
+ const item = app.getService(serviceId);
241
+ const usedBy = buildServiceUsage(app).get(serviceId) ?? [];
242
+
243
+ return {
244
+ description: item?.description ?? null,
245
+ health: item ? serviceHealthStatus(item) : 'none',
246
+ id: serviceId,
247
+ kind: 'service',
248
+ lifetime: 'singleton',
249
+ usedBy,
141
250
  };
142
251
  };
143
252
 
@@ -180,10 +289,13 @@ const buildSurveyDetail = (
180
289
  trailId: string
181
290
  ): Result<object, Error> => {
182
291
  const item = app.get(trailId);
183
- if (!item) {
184
- return Result.err(new Error(`Trail not found: ${trailId}`));
292
+ if (item) {
293
+ return Result.ok(generateTrailDetail(item as Trail<unknown, unknown>));
294
+ }
295
+ if (app.getService(trailId)) {
296
+ return Result.ok(formatServiceDetail(app, trailId));
185
297
  }
186
- return Result.ok(formatTrailDetail(item as Trail<unknown, unknown>));
298
+ return Result.err(new Error(`Trail or service not found: ${trailId}`));
187
299
  };
188
300
 
189
301
  const buildSurveyGenerate = async (
@@ -231,7 +343,7 @@ const surveyHandlers: Record<SurveyMode, SurveyHandler> = {
231
343
  detail: (app, input) => buildSurveyDetail(app, input.trailId ?? ''),
232
344
  diff: (app, input) => buildSurveyDiff(app, input.breakingOnly),
233
345
  generate: (app) => buildSurveyGenerate(app),
234
- list: (app) => Result.ok(formatTrailList(app)),
346
+ list: (app) => Result.ok(generateSurveyList(app)),
235
347
  openapi: (app) => Result.ok(generateOpenApiSpec(app)),
236
348
  };
237
349
 
@@ -298,6 +410,17 @@ export const surveyTrail = trail('survey', {
298
410
  safety: z.string(),
299
411
  })
300
412
  ),
413
+ serviceCount: z.number(),
414
+ services: z.array(
415
+ z.object({
416
+ description: z.string().nullable(),
417
+ health: z.enum(['available', 'none']),
418
+ id: z.string(),
419
+ kind: z.literal('service'),
420
+ lifetime: z.literal('singleton'),
421
+ usedBy: z.array(z.string()),
422
+ })
423
+ ),
301
424
  }),
302
425
  z.object({
303
426
  contractVersion: z.string(),
@@ -307,8 +430,10 @@ export const surveyTrail = trail('survey', {
307
430
  events: z.boolean(),
308
431
  examples: z.boolean(),
309
432
  outputSchemas: z.boolean(),
433
+ services: z.boolean(),
310
434
  }),
311
435
  name: z.string(),
436
+ services: z.number(),
312
437
  trails: z.number(),
313
438
  version: z.string(),
314
439
  }),
@@ -322,9 +447,20 @@ export const surveyTrail = trail('survey', {
322
447
  description: z.unknown().nullable(),
323
448
  detours: z.unknown().nullable(),
324
449
  examples: z.array(z.unknown()),
450
+ follow: z.array(z.string()),
325
451
  id: z.string(),
452
+ intent: z.enum(['read', 'write', 'destroy']),
326
453
  kind: z.string(),
327
454
  safety: z.string(),
455
+ services: z.array(z.string()),
456
+ }),
457
+ z.object({
458
+ description: z.string().nullable(),
459
+ health: z.enum(['available', 'none']),
460
+ id: z.string(),
461
+ kind: z.literal('service'),
462
+ lifetime: z.literal('singleton'),
463
+ usedBy: z.array(z.string()),
328
464
  }),
329
465
  z.object({
330
466
  hash: z.string(),