@app-vitals/agent-admin 0.15.0 → 0.16.1

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,7 +1,7 @@
1
1
  /**
2
2
  * admin/src/specialty-manifest-loader.unit.test.ts
3
3
  * Unit tests for SpecialtyRegistry — the disk-backed resolver from an agent's
4
- * specialty to its parsed manifest, with the unknown-type "coding" fallback.
4
+ * specialty to its parsed manifest, with the unknown-type default-type fallback.
5
5
  *
6
6
  * Pure logic against an injected reader/warner — no filesystem, no DB. See
7
7
  * docs/testing.md for the unit-layer contract.
@@ -9,10 +9,10 @@
9
9
 
10
10
  import { describe, expect, it } from "bun:test";
11
11
  import {
12
- SpecialtyRegistry,
13
12
  DEFAULT_SPECIALTY_NAME,
14
- resolveSpecialtiesDir,
15
13
  resolveDefaultSpecialtyName,
14
+ resolveSpecialtiesDir,
15
+ SpecialtyRegistry,
16
16
  } from "./specialty-manifest-loader.ts";
17
17
 
18
18
  /** A minimal valid Specialty manifest YAML for a given type name. */
@@ -49,17 +49,17 @@ describe("SpecialtyRegistry", () => {
49
49
  defaultTypeName: DEFAULT_SPECIALTY_NAME,
50
50
  read: (t) => manifestYaml(t),
51
51
  });
52
- const manifest = registry.getManifest("coding");
53
- expect(manifest.metadata.name).toBe("coding");
54
- expect(manifest.crons[0]?.name).toBe("coding-cron");
52
+ const manifest = registry.getManifest("specialist");
53
+ expect(manifest.metadata.name).toBe("specialist");
54
+ expect(manifest.crons[0]?.name).toBe("specialist-cron");
55
55
  });
56
56
 
57
- it("falls back to the coding manifest with a warning for an unknown specialty", () => {
57
+ it("falls back to the default (specialist) manifest with a warning for an unknown specialty", () => {
58
58
  const warnings: string[] = [];
59
59
  const registry = new SpecialtyRegistry({
60
60
  defaultTypeName: DEFAULT_SPECIALTY_NAME,
61
61
  read: (t) => {
62
- if (t === DEFAULT_SPECIALTY_NAME) return manifestYaml("coding");
62
+ if (t === DEFAULT_SPECIALTY_NAME) return manifestYaml("specialist");
63
63
  throw new Error(`ENOENT: no manifest for ${t}`);
64
64
  },
65
65
  warn: (m) => warnings.push(m),
@@ -67,51 +67,49 @@ describe("SpecialtyRegistry", () => {
67
67
 
68
68
  const manifest = registry.getManifest("renamed-type");
69
69
 
70
- expect(manifest.metadata.name).toBe("coding");
70
+ expect(manifest.metadata.name).toBe("specialist");
71
71
  expect(warnings).toHaveLength(1);
72
72
  expect(warnings[0]).toContain("renamed-type");
73
- expect(warnings[0]).toContain("coding");
74
- });
75
-
76
- it("throws (does not silently no-op) when the coding fallback itself cannot load", () => {
77
- const registry = new SpecialtyRegistry({
78
- defaultTypeName: DEFAULT_SPECIALTY_NAME,
79
- read: () => {
80
- throw new Error("ENOENT: coding manifest missing");
81
- },
82
- warn: () => {},
83
- });
84
-
85
- // Requesting the default type when it can't load must surface a hard error
86
- // rather than degrade into an empty cron set that would strip system crons.
87
- expect(() => registry.getManifest("coding")).toThrow();
73
+ expect(warnings[0]).toContain("specialist");
88
74
  });
89
75
 
90
- it("names SHIPWRIGHT_SPECIALTIES_DIR and SHIPWRIGHT_DEFAULT_AGENT_TYPE when the default specialty is missing", () => {
76
+ it("never throws: an unloadable default degrades to an empty-crons manifest with warnings naming the env knobs", () => {
77
+ const warnings: string[] = [];
91
78
  const registry = new SpecialtyRegistry({
92
79
  read: () => {
93
80
  throw new Error("ENOENT: no manifest");
94
81
  },
95
- warn: () => {},
82
+ warn: (m) => warnings.push(m),
96
83
  typesDir: "/nonexistent/specialties",
97
84
  defaultTypeName: "absent",
98
85
  });
99
86
 
100
87
  for (const specialty of ["absent", "some-other-type"]) {
101
- let message = "";
102
- try {
103
- registry.getManifest(specialty);
104
- } catch (err) {
105
- message = (err as Error).message;
106
- }
107
- expect(message).toContain('default specialty "absent"');
108
- expect(message).toContain("/nonexistent/specialties");
109
- expect(message).toContain("SHIPWRIGHT_SPECIALTIES_DIR");
110
- expect(message).toContain("SHIPWRIGHT_DEFAULT_AGENT_TYPE");
111
- expect(message).not.toContain("startup-time bug");
88
+ warnings.length = 0;
89
+ const manifest = registry.getManifest(specialty);
90
+ expect(manifest.crons).toEqual([]);
91
+ expect(manifest.plugins).toEqual([]);
92
+ const all = warnings.join("\n");
93
+ expect(all).toContain('default specialty "absent"');
94
+ expect(all).toContain("/nonexistent/specialties");
95
+ expect(all).toContain("SHIPWRIGHT_SPECIALTIES_DIR");
96
+ expect(all).toContain("SHIPWRIGHT_DEFAULT_AGENT_TYPE");
112
97
  }
113
98
  });
114
99
 
100
+ it("uses the default manifest silently when the default type itself is requested and loads", () => {
101
+ const warnings: string[] = [];
102
+ const registry = new SpecialtyRegistry({
103
+ defaultTypeName: DEFAULT_SPECIALTY_NAME,
104
+ read: (t) => manifestYaml(t),
105
+ warn: (m) => warnings.push(m),
106
+ });
107
+ expect(registry.getManifest(DEFAULT_SPECIALTY_NAME).metadata.name).toBe(
108
+ "specialist",
109
+ );
110
+ expect(warnings).toEqual([]);
111
+ });
112
+
115
113
  it("caches a resolved manifest — the reader is called once per type", () => {
116
114
  let reads = 0;
117
115
  const registry = new SpecialtyRegistry({
@@ -122,15 +120,15 @@ describe("SpecialtyRegistry", () => {
122
120
  },
123
121
  });
124
122
 
125
- registry.getManifest("coding");
126
- registry.getManifest("coding");
123
+ registry.getManifest("specialist");
124
+ registry.getManifest("specialist");
127
125
 
128
126
  expect(reads).toBe(1);
129
127
  });
130
128
 
131
129
  // ─── tryGetManifest — non-fallback lookup ────────────────────────────────
132
130
  //
133
- // Unlike getManifest(), tryGetManifest() must NOT fall back to "coding" for
131
+ // Unlike getManifest(), tryGetManifest() must NOT fall back to the default type for
134
132
  // an unknown specialty — it returns undefined instead. This is what lets
135
133
  // POST /agents distinguish "resolved to the exact requested type" from
136
134
  // "silently degraded", which the cron-reconcile boot path (getManifest())
@@ -141,8 +139,8 @@ describe("SpecialtyRegistry", () => {
141
139
  defaultTypeName: DEFAULT_SPECIALTY_NAME,
142
140
  read: (t) => manifestYaml(t),
143
141
  });
144
- const manifest = registry.tryGetManifest("coding");
145
- expect(manifest?.metadata.name).toBe("coding");
142
+ const manifest = registry.tryGetManifest("specialist");
143
+ expect(manifest?.metadata.name).toBe("specialist");
146
144
  });
147
145
 
148
146
  it("tryGetManifest returns undefined for an unknown specialty — no fallback, no warning", () => {
@@ -150,7 +148,7 @@ describe("SpecialtyRegistry", () => {
150
148
  const registry = new SpecialtyRegistry({
151
149
  defaultTypeName: DEFAULT_SPECIALTY_NAME,
152
150
  read: (t) => {
153
- if (t === DEFAULT_SPECIALTY_NAME) return manifestYaml("coding");
151
+ if (t === DEFAULT_SPECIALTY_NAME) return manifestYaml("specialist");
154
152
  throw new Error(`ENOENT: no manifest for ${t}`);
155
153
  },
156
154
  warn: (m) => warnings.push(m),
@@ -172,8 +170,8 @@ describe("SpecialtyRegistry", () => {
172
170
  },
173
171
  });
174
172
 
175
- registry.getManifest("coding");
176
- registry.tryGetManifest("coding");
173
+ registry.getManifest("specialist");
174
+ registry.tryGetManifest("specialist");
177
175
 
178
176
  expect(reads).toBe(1);
179
177
  });
@@ -273,9 +271,9 @@ voice: true
273
271
  });
274
272
 
275
273
  describe("specialties option resolution", () => {
276
- it("defaults to coding and the repo-root specialties/ with no env set", () => {
277
- expect(resolveDefaultSpecialtyName({})).toBe("coding");
278
- expect(resolveSpecialtiesDir({})).toMatch(/specialties$/);
274
+ it("defaults to specialist and the shipped agent-types/ with no env set", () => {
275
+ expect(resolveDefaultSpecialtyName({})).toBe("specialist");
276
+ expect(resolveSpecialtiesDir({})).toMatch(/agent-types$/);
279
277
  });
280
278
 
281
279
  it("honours SHIPWRIGHT_DEFAULT_AGENT_TYPE and SHIPWRIGHT_SPECIALTIES_DIR", () => {