@synap-core/workspace-templates 0.30.0 → 0.32.0

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/README.md +13 -5
  2. package/agents/hermes.agent.yaml +3 -0
  3. package/dist/{define-DqoItRiR.d.ts → define-DHDi8C33.d.ts} +19 -18
  4. package/dist/index.d.ts +2 -2
  5. package/dist/index.js +428 -239
  6. package/dist/index.js.map +1 -1
  7. package/dist/wire.d.ts +1 -1
  8. package/package.json +1 -1
  9. package/src/brand-library.yaml +0 -6
  10. package/src/builder-dev-fold.template.test.ts +2 -3
  11. package/src/builder-dev-playbooks.template.test.ts +7 -5
  12. package/src/builder.yaml +60 -42
  13. package/src/business-model.yaml +20 -1
  14. package/src/civic-france.yaml +9 -0
  15. package/src/content-creation.yaml +1 -1
  16. package/src/content-os.yaml +22 -18
  17. package/src/crm-absorbs-outreach.template.test.ts +1 -1
  18. package/src/crm-build-plan.template.test.ts +1 -1
  19. package/src/crm.yaml +5 -14
  20. package/src/decisions-wave.template.test.ts +7 -5
  21. package/src/define.primary-surface.test.ts +9 -0
  22. package/src/define.ts +4 -1
  23. package/src/finance.yaml +10 -6
  24. package/src/foundation.yaml +1 -2
  25. package/src/hr.yaml +3 -2
  26. package/src/intent.test.ts +43 -2
  27. package/src/internal-runbook.yaml +0 -1
  28. package/src/kanban-grouping.test.ts +72 -0
  29. package/src/legal.yaml +4 -4
  30. package/src/lifecycle-coverage.test.ts +7 -64
  31. package/src/lifecycle-declarations.test.ts +70 -74
  32. package/src/marketing.yaml +4 -3
  33. package/src/meetings-knowledge.yaml +1 -2
  34. package/src/operations.yaml +13 -9
  35. package/src/process-contract.validate.test.ts +40 -55
  36. package/src/project-management.yaml +2 -2
  37. package/src/publish-official-lib.test.ts +3 -2
  38. package/src/research-base.yaml +12 -5
  39. package/src/research-decision.yaml +18 -2
  40. package/src/ship-a-change.template.test.ts +3 -2
  41. package/src/social.yaml +4 -1
  42. package/src/start.test.ts +22 -0
  43. package/src/templates.ts +324 -183
  44. package/src/types.ts +28 -18
  45. package/src/validate.ts +143 -77
@@ -18,7 +18,6 @@ meta:
18
18
  label: Client delivery
19
19
  promise: Every client engagement and where it stands.
20
20
  suggestFor: [consultant]
21
- profile: engagement
22
21
  views: [engagements]
23
22
  example:
24
23
  - { title: "Website redesign for Maison Dupont", values: { engagement-status: active, engagement-value: 18000 } }
@@ -35,6 +34,8 @@ meta:
35
34
  # What this space PRODUCES (homes): the purpose word, then its kinds/roles.
36
35
  intent:
37
36
  label: Delivery
37
+ # The ONE primary output (One model decision 3): a delivered engagement (Deliver a client engagement).
38
+ primary: engagement
38
39
  produces:
39
40
  - profile: engagement
40
41
  - profile: deliverable
@@ -304,19 +305,28 @@ views:
304
305
  slug: engagements
305
306
  type: kanban
306
307
  scopeProfileSlug: engagement
307
- groupBy: engagement-status
308
+ config:
309
+ groupByField: engagement-status
308
310
  description: "Client engagements on a board by status."
309
311
  - name: Deliverables
310
312
  slug: deliverables
311
313
  type: kanban
312
314
  scopeProfileSlug: deliverable
313
- groupBy: deliverable-status
315
+ config:
316
+ groupByField: deliverable-status
314
317
  description: "Deliverables on a board by status."
315
318
  - name: Contracts
316
319
  slug: contracts
317
320
  type: table
318
321
  scopeProfileSlug: contract
319
322
  description: "Contracts with their status, value, and term."
323
+ - name: Contract Board
324
+ slug: contract-board
325
+ type: kanban
326
+ scopeProfileSlug: contract
327
+ description: "Contracts by status — the flow board of Get a contract signed."
328
+ config:
329
+ groupByField: contract-status
320
330
  - name: Client Briefings
321
331
  slug: client-briefings
322
332
  type: list
@@ -938,8 +948,6 @@ playbooks:
938
948
  call.
939
949
  subjectProfile:
940
950
  profileSlug: engagement
941
- statusProperty: engagement-status
942
- humanOnlyStatuses: [on-hold, cancelled]
943
951
  activators:
944
952
  - { on: created, mode: propose }
945
953
  params:
@@ -1018,8 +1026,6 @@ playbooks:
1018
1026
  close bills it.
1019
1027
  subjectProfile:
1020
1028
  profileSlug: deliverable
1021
- statusProperty: deliverable-status
1022
- humanOnlyStatuses: [not-started, cancelled]
1023
1029
  activators:
1024
1030
  - { on: enters_status, status: in-progress, mode: propose }
1025
1031
  params:
@@ -1095,8 +1101,6 @@ playbooks:
1095
1101
  document. Expiry and termination are a person's call.
1096
1102
  subjectProfile:
1097
1103
  profileSlug: contract
1098
- statusProperty: contract-status
1099
- humanOnlyStatuses: [expired, terminated]
1100
1104
  activators:
1101
1105
  - { on: created, mode: propose }
1102
1106
  params:
@@ -2,11 +2,11 @@
2
2
  * The process contract a playbook declares is checked at authoring time
3
3
  * (`checkProcessContract` in validate.ts), because every one of these mistakes
4
4
  * is STORED by the pod and then silently never fires:
5
- * - a `statusProperty` that is not a select of the subject kind → the stage's
6
- * status write lands on a field nobody reads;
7
- * - a `subjectStatus` / activator `status` / `humanOnlyStatuses` value that is
8
- * not one of the enum values → a write the select refuses, or a rule on a
9
- * value no entity ever enters;
5
+ * - a `statusProperty` / `onEnd` / `humanOnlyStatuses` → retired (One model
6
+ * decision 4): the process follows the kind's declared lifecycle;
7
+ * - a `subjectStatus` / activator `status` that is not an option of the
8
+ * kind's declared lifecycle (or a kind with none) → a write the select
9
+ * refuses, or a rule on a value no entity ever enters;
10
10
  * - an `onFail.toStage` naming no stage → a rejected gate with nowhere to go;
11
11
  * - a `relationToSubject` naming no relation → an edge type nothing renders.
12
12
  *
@@ -24,21 +24,17 @@ import type { WorkspaceYaml } from "./types.js";
24
24
  import { readYamlTemplates } from "./generate.mjs";
25
25
 
26
26
  const PROCESS_RULES = new Set([
27
- "playbook-status-property-no-subject",
28
- "playbook-status-property-unknown",
29
- "playbook-status-property-not-select",
27
+ "playbook-field-retired",
30
28
  "stage-subject-status-unknown",
31
- "stage-subject-status-unknown-no-property",
29
+ "stage-subject-status-unknown-no-lifecycle",
32
30
  "stage-onfail-unknown",
33
31
  "activator-on-invalid",
34
32
  "activator-mode-invalid",
35
33
  "activator-no-subject",
36
34
  "activator-status-required",
37
35
  "activator-status-unknown",
38
- "activator-status-unknown-no-property",
36
+ "activator-status-unknown-no-lifecycle",
39
37
  "activator-status-unexpected",
40
- "human-only-status-unknown",
41
- "human-only-status-unknown-no-property",
42
38
  "output-relation-no-subject",
43
39
  "output-relation-unknown",
44
40
  "output-optional-not-boolean",
@@ -46,10 +42,19 @@ const PROCESS_RULES = new Set([
46
42
  ]);
47
43
 
48
44
  /**
49
- * `owner` OWNS the shared kind `widget` (with its lifecycle select). `proc`
45
+ * `owner` OWNS the shared kind `widget` (with its declared lifecycle). `proc`
50
46
  * requires it and only re-declares `widget` with no properties — so resolving
51
- * `widget-stage` proves the status property is found ACROSS the corpus.
47
+ * `widget-stage` proves the lifecycle is found ACROSS the corpus.
52
48
  */
49
+ const WIDGET_LIFECYCLE = {
50
+ property: "widget-stage",
51
+ options: [
52
+ { key: "New", category: "open" },
53
+ { key: "Built", category: "active" },
54
+ { key: "Shipped", category: "done" },
55
+ { key: "Archived", category: "lost", humanOnly: true },
56
+ ],
57
+ };
53
58
  const owner = {
54
59
  meta: { slug: "owner", name: "Owner", description: "d", icon: "x", color: "gray" },
55
60
  workspace: { name: "Owner", description: "d" },
@@ -58,6 +63,7 @@ const owner = {
58
63
  slug: "widget",
59
64
  displayName: "Widget",
60
65
  scope: "shared",
66
+ lifecycle: WIDGET_LIFECYCLE,
61
67
  properties: [
62
68
  { slug: "widget-stage", label: "Stage", valueType: "string", inputType: "select", enumValues: ["New", "Built", "Shipped", "Archived"] },
63
69
  { slug: "widget-notes", label: "Notes", valueType: "string", inputType: "text" },
@@ -72,8 +78,7 @@ function proc(mutate: (pb: Record<string, any>) => void = () => {}): WorkspaceYa
72
78
  name: "Build Widget",
73
79
  goalTemplate: "Build {widget}",
74
80
  params: [{ name: "widget", type: "entity", required: true }],
75
- subjectProfile: { profileSlug: "widget", statusProperty: "widget-stage" },
76
- humanOnlyStatuses: ["New", "Archived"],
81
+ subjectProfile: { profileSlug: "widget" },
77
82
  activators: [{ on: "enters_status", status: "Built", mode: "propose" }],
78
83
  expectedOutputs: [{ kind: "widget", label: "Spin-off", relationToSubject: "spawned_from" }],
79
84
  stages: [
@@ -112,26 +117,16 @@ describe("process contract — the clean fixture is clean", () => {
112
117
  });
113
118
 
114
119
  describe("process contract — each rule fires on its own fixture", () => {
115
- it("statusProperty that the kind does not carry", () => {
116
- expect(rulesOf(proc((pb) => (pb.subjectProfile.statusProperty = "widget-phase")))).toEqual([
117
- "playbook-status-property-unknown",
120
+ // Negative control: each assertion goes red with the `playbook-field-retired`
121
+ // loop removed from checkProcessContract.
122
+ it("each retired field is refused (statusProperty, onEnd, humanOnlyStatuses)", () => {
123
+ expect(rulesOf(proc((pb) => (pb.subjectProfile.statusProperty = "widget-stage")))).toEqual([
124
+ "playbook-field-retired",
118
125
  ]);
119
- });
120
-
121
- it("statusProperty that is not a select", () => {
122
- expect(
123
- rulesOf(
124
- proc((pb) => {
125
- pb.subjectProfile.statusProperty = "widget-notes";
126
- // Without a resolvable vocabulary the statuses are not re-reported.
127
- })
128
- )
129
- ).toEqual(["playbook-status-property-not-select"]);
130
- });
131
-
132
- it("statusProperty with no subject kind", () => {
133
- const rules = rulesOf(proc((pb) => (pb.subjectProfile = { statusProperty: "widget-stage" })));
134
- expect(rules).toContain("playbook-status-property-no-subject");
126
+ expect(rulesOf(proc((pb) => (pb.subjectProfile.onEnd = { cancelled: "New" })))).toEqual([
127
+ "playbook-field-retired",
128
+ ]);
129
+ expect(rulesOf(proc((pb) => (pb.humanOnlyStatuses = ["New"])))).toEqual(["playbook-field-retired"]);
135
130
  });
136
131
 
137
132
  it("a stage subjectStatus outside the enum", () => {
@@ -140,17 +135,14 @@ describe("process contract — each rule fires on its own fixture", () => {
140
135
  ]);
141
136
  });
142
137
 
143
- it("a stage subjectStatus with no statusProperty to belong to", () => {
144
- const rules = rulesOf(
145
- proc((pb) => {
146
- delete pb.subjectProfile.statusProperty;
147
- delete pb.humanOnlyStatuses;
148
- pb.activators = [{ on: "created", mode: "propose" }];
149
- })
150
- );
151
- expect(rules).toEqual([
152
- "stage-subject-status-unknown-no-property",
153
- "stage-subject-status-unknown-no-property",
138
+ it("a stage subjectStatus on a kind that declares no lifecycle", () => {
139
+ const noLifecycle = structuredClone(owner) as unknown as { profiles: Array<Record<string, unknown>> };
140
+ delete noLifecycle.profiles[0]!.lifecycle;
141
+ const tpl = proc((pb) => (pb.activators = [{ on: "created", mode: "propose" }]));
142
+ const { errors } = validateTemplate(tpl, { corpus: [noLifecycle as unknown as WorkspaceYaml, tpl] });
143
+ expect(errors.filter((e) => PROCESS_RULES.has(e.rule)).map((e) => e.rule)).toEqual([
144
+ "stage-subject-status-unknown-no-lifecycle",
145
+ "stage-subject-status-unknown-no-lifecycle",
154
146
  ]);
155
147
  });
156
148
 
@@ -218,7 +210,6 @@ describe("process contract — each rule fires on its own fixture", () => {
218
210
  proc((pb) => {
219
211
  delete pb.subjectProfile;
220
212
  delete pb.stages;
221
- delete pb.humanOnlyStatuses;
222
213
  delete pb.expectedOutputs;
223
214
  pb.activators = [{ on: "created", mode: "propose" }];
224
215
  })
@@ -226,12 +217,6 @@ describe("process contract — each rule fires on its own fixture", () => {
226
217
  expect(rules).toEqual(["activator-no-subject"]);
227
218
  });
228
219
 
229
- it("a humanOnlyStatuses value outside the enum", () => {
230
- expect(rulesOf(proc((pb) => (pb.humanOnlyStatuses = ["New", "Retired"])))).toEqual([
231
- "human-only-status-unknown",
232
- ]);
233
- });
234
-
235
220
  it("a playbook-level relationToSubject the corpus does not define", () => {
236
221
  expect(rulesOf(proc((pb) => (pb.expectedOutputs[0].relationToSubject = "spawned_by")))).toEqual([
237
222
  "output-relation-unknown",
@@ -277,7 +262,6 @@ describe("process contract — each rule fires on its own fixture", () => {
277
262
  const strip = (pb: Record<string, any>) => {
278
263
  delete pb.subjectProfile;
279
264
  delete pb.stages;
280
- delete pb.humanOnlyStatuses;
281
265
  delete pb.activators;
282
266
  };
283
267
  expect(rulesOf(proc(strip))).toEqual(["output-relation-no-subject"]);
@@ -340,7 +324,8 @@ describe("process contract — the shipped corpus", () => {
340
324
  const corpus = readYamlTemplates().map((t) => t.data as WorkspaceYaml);
341
325
  const contentOs = corpus.find((t) => t.meta.slug === "content-os")!;
342
326
  const produce = contentOs.playbooks!.find((p) => p.name === "Produce Content")!;
343
- expect(produce.subjectProfile?.statusProperty).toBe("post-status");
327
+ expect(produce.subjectProfile).toEqual({ profileSlug: "post" });
328
+ expect(contentOs.profiles.find((p) => p.slug === "post")?.lifecycle?.property).toBe("post-status");
344
329
  expect(produce.stages!.filter((s) => s.subjectStatus).length).toBe(6);
345
330
  const errs = corpus.flatMap((t) =>
346
331
  validateTemplate(t, { corpus })
@@ -17,6 +17,8 @@ meta:
17
17
  # What this space PRODUCES (homes): the purpose word, then its kinds/roles.
18
18
  intent:
19
19
  label: Planning
20
+ # The ONE primary output (One model decision 3): a finished sprint, its one produced kind.
21
+ primary: sprint
20
22
  produces:
21
23
  - profile: sprint
22
24
 
@@ -192,7 +194,6 @@ views:
192
194
  slug: sprint-board
193
195
  type: kanban
194
196
  scopeProfileSlug: task
195
- groupBy: task-status
196
197
  description: "Active sprint tasks in Backlog / Todo / In Progress / Review / Done lanes"
197
198
  config:
198
199
  groupByField: task-status
@@ -276,7 +277,6 @@ views:
276
277
  slug: retrospective-board
277
278
  type: kanban
278
279
  scopeProfileSlug: sprint
279
- groupBy: sprint-status
280
280
  description: "Sprint retrospectives — completed sprints with retrospective notes attached as documents"
281
281
  config:
282
282
  groupByField: sprint-status
@@ -77,7 +77,7 @@ describe("plan → execute — the wire", () => {
77
77
  .playbooks as any[];
78
78
  const produce = yamlPlaybooks.find((p) => p.name === "Produce Content");
79
79
  // Non-vacuity: the YAML really declares the contract fields compared below.
80
- expect(produce.subjectProfile.statusProperty).toBe("post-status");
80
+ expect(produce.subjectProfile).toEqual({ profileSlug: "post" });
81
81
  expect(produce.activators).toEqual([{ on: "enters_status", status: "Briefed", mode: "propose" }]);
82
82
  expect(produce.stages.find((s: any) => s.key === "review").onFail).toEqual({ toStage: "draft" });
83
83
  expect(
@@ -98,7 +98,8 @@ describe("plan → execute — the wire", () => {
98
98
  "Scheduled",
99
99
  "Published",
100
100
  ]);
101
- expect(postedProduce.humanOnlyStatuses).toEqual(["Idea", "Archived"]);
101
+ // Retired from the catalog (One model decision 4): nothing to carry.
102
+ expect(postedProduce.humanOnlyStatuses).toBeUndefined();
102
103
  });
103
104
 
104
105
  it("reports a stamp the CP did not store", async () => {
@@ -29,6 +29,8 @@ meta:
29
29
  # What this space PRODUCES (homes): the purpose word, then its kinds/roles.
30
30
  intent:
31
31
  label: Research
32
+ # The ONE primary output (One model decision 3): a finding is what research yields (questions are its input).
33
+ primary: research-finding
32
34
  produces:
33
35
  - profile: hypothesis
34
36
  - profile: research-finding
@@ -60,6 +62,15 @@ profiles:
60
62
  icon: help-circle
61
63
  color: blue
62
64
  description: An open question driving an investigation — the thing research answers.
65
+ # The pod seeds this lifecycle on every pod (ensure-system-lifecycles.ts);
66
+ # declared here to match the seed exactly, so a playbook can follow it.
67
+ lifecycle:
68
+ property: questionStatus
69
+ options:
70
+ - { key: open, category: open }
71
+ - { key: exploring, category: active }
72
+ - { key: answered, category: done }
73
+ - { key: abandoned, category: lost }
63
74
  properties:
64
75
  - slug: questionStatus
65
76
  label: Status
@@ -280,10 +291,9 @@ views:
280
291
  slug: research-hypotheses
281
292
  type: kanban
282
293
  scopeProfileSlug: hypothesis
283
- groupBy: stance
284
294
  description: Candidate theories, grouped by where the evidence has left them.
285
295
  config:
286
- groupBy: stance
296
+ groupByField: stance
287
297
  - name: Findings
288
298
  slug: research-findings
289
299
  type: table
@@ -420,8 +430,6 @@ playbooks:
420
430
  proposal-gated and reviewable.
421
431
  subjectProfile:
422
432
  profileSlug: question
423
- statusProperty: questionStatus
424
- humanOnlyStatuses: [abandoned]
425
433
  activators:
426
434
  - { on: created, mode: propose }
427
435
  params:
@@ -487,7 +495,6 @@ playbooks:
487
495
  support.
488
496
  subjectProfile:
489
497
  profileSlug: research-finding
490
- statusProperty: status
491
498
  activators:
492
499
  - { on: enters_status, status: validated, mode: propose }
493
500
  params:
@@ -45,6 +45,15 @@ profiles:
45
45
  # Pod-wide `question` is a first-class KIND (Decision 1 — the manifest retired
46
46
  # w4.convert.question to a keep no-op). Declared as a plain kind to match the
47
47
  # seed + research-base and reuse the existing pod-wide row.
48
+ # The pod seeds this lifecycle on every pod (ensure-system-lifecycles.ts);
49
+ # declared here to match the seed exactly, so a playbook can follow it.
50
+ lifecycle:
51
+ property: questionStatus
52
+ options:
53
+ - { key: open, category: open }
54
+ - { key: exploring, category: active }
55
+ - { key: answered, category: done }
56
+ - { key: abandoned, category: lost }
48
57
  properties:
49
58
  - slug: questionStatus
50
59
  label: Status
@@ -83,6 +92,15 @@ profiles:
83
92
  # Pod-wide `decision` is a first-class KIND (Decision 1 — w4.convert.decision
84
93
  # retired to a keep no-op). Declared as a plain kind to match the seed; the
85
94
  # `resolves` / `considered_in` links resolve against the reused pod-wide row.
95
+ # The pod seeds this lifecycle on every pod (ensure-system-lifecycles.ts);
96
+ # declared here to match the seed exactly, so a playbook can follow it.
97
+ lifecycle:
98
+ property: decisionStatus
99
+ options:
100
+ - { key: proposed, category: open }
101
+ - { key: accepted, category: done }
102
+ - { key: superseded, category: lost }
103
+ - { key: rejected, category: lost }
86
104
  properties:
87
105
  - slug: summary
88
106
  label: Summary
@@ -168,8 +186,6 @@ playbooks:
168
186
  superseded.
169
187
  subjectProfile:
170
188
  profileSlug: decision
171
- statusProperty: decisionStatus
172
- humanOnlyStatuses: [superseded, rejected]
173
189
  activators:
174
190
  - { on: created, mode: propose }
175
191
  params:
@@ -67,8 +67,9 @@ const outputs = (type: string) =>
67
67
  );
68
68
 
69
69
  describe("Ship a change — the process follows the feature's lifecycle", () => {
70
- it("names the lifecycle property, and every status it writes or reads is an option of it", () => {
71
- expect(pb.subjectProfile.statusProperty).toBe(feature.lifecycle.property);
70
+ it("follows the feature's lifecycle (names no statusProperty), and every status it writes or reads is an option of it", () => {
71
+ expect(pb.subjectProfile.statusProperty).toBeUndefined();
72
+ expect(pb.subjectProfile.profileSlug).toBe("devplane_feature");
72
73
  const options = new Set(feature.lifecycle.options.map((o) => o.key));
73
74
  const used = [
74
75
  ...pb.stages.map((s) => s.subjectStatus).filter(Boolean),
package/src/social.yaml CHANGED
@@ -16,6 +16,8 @@ meta:
16
16
  # What this space PRODUCES (homes): the purpose word, then its kinds/roles.
17
17
  intent:
18
18
  label: Distribution
19
+ # The ONE primary output (One model decision 3): a published post, its one produced kind.
20
+ primary: post
19
21
  produces:
20
22
  - profile: post
21
23
 
@@ -297,7 +299,8 @@ views:
297
299
  slug: content-calendar
298
300
  type: kanban
299
301
  scopeProfileSlug: post
300
- groupBy: post-status
302
+ config:
303
+ groupByField: post-status
301
304
  description: "Posts on a board by status — idea to published."
302
305
  - name: Platforms
303
306
  slug: platforms
package/src/start.test.ts CHANGED
@@ -130,6 +130,28 @@ describe("meta.start — validator rules", () => {
130
130
  });
131
131
  });
132
132
 
133
+ describe("meta.start — the kind defaults to the primary output (one source)", () => {
134
+ const withPrimary = (start: Partial<TemplateStart>) => {
135
+ const t = make("t", start);
136
+ t.meta.intent = { label: "Sales", primary: "deal", produces: [{ profile: "deal" }] };
137
+ return t;
138
+ };
139
+ // Negative control: goes red with the start-profile-* block removed from checkStart.
140
+ it("an omitted profile reads meta.intent.primary, and the wire carries it", () => {
141
+ const t = withPrimary({ profile: undefined });
142
+ expect(startErrors(t)).toEqual([]);
143
+ expect(toPackageDefinition(t).start?.profile).toBe("deal");
144
+ });
145
+
146
+ it("start-profile-redundant: naming the primary again is a second copy", () => {
147
+ expect(startErrors(withPrimary({ profile: "deal" }))).toEqual(["start-profile-redundant"]);
148
+ });
149
+
150
+ it("start-profile-missing: no profile and no primary to default to", () => {
151
+ expect(startErrors(make("t", { profile: undefined }))).toEqual(["start-profile-missing"]);
152
+ });
153
+ });
154
+
133
155
  describe("meta.start — package door", () => {
134
156
  it("carries a top-level start, never in _meta", () => {
135
157
  const def = toPackageDefinition(make("t"));