@synap-core/workspace-templates 0.12.0 → 0.23.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 (245) hide show
  1. package/README.md +483 -0
  2. package/automations/bookmark-enrichment.automation.json +321 -0
  3. package/automations/github-pr-events.automation.json +505 -0
  4. package/automations/mail-feed.automation.json +86 -0
  5. package/automations/ship-a-change-checks-green.automation.json +106 -0
  6. package/automations/ship-a-change-checks-red.automation.json +106 -0
  7. package/automations/ship-a-change-deployed.automation.json +56 -0
  8. package/automations/ship-a-change-pr-merged.automation.json +110 -0
  9. package/automations/ship-a-change-pr-opened.automation.json +104 -0
  10. package/automations/url-bookmark-capture.automation.json +132 -0
  11. package/capabilities/agency-skills.capability.json +157 -0
  12. package/capabilities/apify.capability.json +265 -0
  13. package/capabilities/apollo.capability.json +120 -0
  14. package/capabilities/arch-backend.capability.json +97 -0
  15. package/capabilities/arch-client-intelligence.capability.json +401 -0
  16. package/capabilities/backblaze-b2.capability.json +205 -0
  17. package/capabilities/cal-com.capability.json +266 -0
  18. package/capabilities/canva.capability.json +274 -0
  19. package/capabilities/claude-code-routines.capability.json +172 -0
  20. package/capabilities/claude-managed-agents.capability.json +215 -0
  21. package/capabilities/client-comms-report.capability.json +246 -0
  22. package/capabilities/content-idea-to-production.capability.json +29 -0
  23. package/capabilities/datagouv.capability.json +315 -0
  24. package/capabilities/discord-bot.capability.json +157 -0
  25. package/capabilities/dropbox.capability.json +281 -0
  26. package/capabilities/exa.capability.json +109 -0
  27. package/capabilities/fal-ai.capability.json +83 -0
  28. package/capabilities/fal-media-lab.capability.json +158 -0
  29. package/capabilities/fireflies.capability.json +191 -0
  30. package/capabilities/freellmapi.capability.json +114 -0
  31. package/capabilities/generic-apikey.capability.json +92 -0
  32. package/capabilities/github-merge.capability.json +99 -0
  33. package/capabilities/github-read.capability.json +325 -0
  34. package/capabilities/governance-tighten-recommender.capability.json +81 -0
  35. package/capabilities/jev.capability.json +226 -0
  36. package/capabilities/lead-conversion-enrollment.capability.json +246 -0
  37. package/capabilities/mail-feed.capability.json +277 -0
  38. package/capabilities/mailgun.capability.json +52 -0
  39. package/capabilities/nango-google.capability.json +639 -0
  40. package/capabilities/notion.capability.json +417 -0
  41. package/capabilities/omniroute.capability.json +137 -0
  42. package/capabilities/posthog-analytics.capability.json +258 -0
  43. package/capabilities/postiz.capability.json +467 -0
  44. package/capabilities/proactive-digest.capability.json +190 -0
  45. package/capabilities/proton.capability.json +288 -0
  46. package/capabilities/replicate-rvm.capability.json +97 -0
  47. package/capabilities/research-methods.capability.json +57 -0
  48. package/capabilities/shopify.capability.json +395 -0
  49. package/capabilities/slack.capability.json +369 -0
  50. package/capabilities/stripe.capability.json +420 -0
  51. package/capabilities/telegram-bridge.capability.json +56 -0
  52. package/capabilities/treg.capability.json +102 -0
  53. package/capabilities/unipile-linkedin.capability.json +471 -0
  54. package/capabilities/vercel.capability.json +424 -0
  55. package/dist/define-CPS0n8BV.d.ts +1331 -0
  56. package/dist/index.d.ts +174 -900
  57. package/dist/index.js +26148 -23931
  58. package/dist/index.js.map +1 -1
  59. package/dist/wire.d.ts +222 -0
  60. package/dist/wire.js +77 -0
  61. package/dist/wire.js.map +1 -0
  62. package/package.json +27 -4
  63. package/packs/bookmark-views-pack.json +33 -0
  64. package/packs/crm-pack.json +76 -0
  65. package/packs/customer-profile.json +28 -0
  66. package/packs/deep-research.playbook.json +30 -0
  67. package/packs/event-views-pack.json +33 -0
  68. package/packs/habit-profile.json +27 -0
  69. package/packs/hr-pack.json +54 -0
  70. package/packs/invoice-profile.json +28 -0
  71. package/packs/knowledge-base-pack.json +56 -0
  72. package/packs/meeting-profile.json +29 -0
  73. package/packs/product-profile.json +30 -0
  74. package/packs/project-management-pack.json +49 -0
  75. package/packs/recipe-profile.json +29 -0
  76. package/packs/task-views-pack.json +39 -0
  77. package/retired.yaml +136 -0
  78. package/skills/app-factory-build-slice/SKILL.md +70 -0
  79. package/skills/app-factory-build-slice.catalog.json +17 -0
  80. package/skills/app-factory-design-canvas/SKILL.md +96 -0
  81. package/skills/app-factory-design-canvas.catalog.json +17 -0
  82. package/skills/app-factory-design-review/SKILL.md +99 -0
  83. package/skills/app-factory-design-review.catalog.json +18 -0
  84. package/skills/app-factory-research/SKILL.md +123 -0
  85. package/skills/app-factory-research.catalog.json +17 -0
  86. package/skills/bookmark-enrichment/SKILL.md +40 -0
  87. package/skills/carousel-format/carousel-format.skill.json +18 -0
  88. package/skills/collage-broll/collage-broll.skill.json +20 -0
  89. package/skills/content-taste/content-taste.skill.json +18 -0
  90. package/skills/creative-director/creative-director.skill.json +18 -0
  91. package/skills/design-critique/SKILL.md +135 -0
  92. package/skills/design-critique.catalog.json +12 -0
  93. package/skills/designing-with-real-components/SKILL.md +140 -0
  94. package/skills/designing-with-real-components.catalog.json +12 -0
  95. package/skills/edit-loop/edit-loop.skill.json +25 -0
  96. package/skills/facecam-edit/facecam-edit.skill.json +24 -0
  97. package/skills/mapping-the-server/SKILL.md +54 -0
  98. package/skills/math-explainer-manimgx/math-explainer-manimgx.skill.json +20 -0
  99. package/skills/motion-quality-gates/motion-quality-gates.skill.json +20 -0
  100. package/skills/open-design-edit/open-design-edit.skill.json +24 -0
  101. package/skills/reading-client-progress/SKILL.md +65 -0
  102. package/skills/routing-to-channels/SKILL.md +54 -0
  103. package/skills/stellar-scf-grants-advisor/ASSOCIATE-CHEATSHEET.md +133 -0
  104. package/skills/stellar-scf-grants-advisor/ASSOCIATE-ONBOARDING-GUIDE.md +761 -0
  105. package/skills/stellar-scf-grants-advisor/CLIENT-FAQ.md +181 -0
  106. package/skills/stellar-scf-grants-advisor/PARTNER-HANDOVER.md +130 -0
  107. package/skills/stellar-scf-grants-advisor/SKILL.md +102 -0
  108. package/skills/stellar-scf-grants-advisor/examples/README.md +43 -0
  109. package/skills/stellar-scf-grants-advisor/examples/chat-summary-templates.md +158 -0
  110. package/skills/stellar-scf-grants-advisor/examples/corpus-comparable-rows.md +136 -0
  111. package/skills/stellar-scf-grants-advisor/examples/q10-team-rewrites.md +109 -0
  112. package/skills/stellar-scf-grants-advisor/examples/q2-description-rewrites.md +88 -0
  113. package/skills/stellar-scf-grants-advisor/examples/q6-integration-rewrites.md +107 -0
  114. package/skills/stellar-scf-grants-advisor/examples/tranche-structures.md +176 -0
  115. package/skills/stellar-scf-grants-advisor/reference/01-methodology.md +96 -0
  116. package/skills/stellar-scf-grants-advisor/reference/02-scoring-framework.md +199 -0
  117. package/skills/stellar-scf-grants-advisor/reference/03-rejection-patterns.md +241 -0
  118. package/skills/stellar-scf-grants-advisor/reference/04-winner-patterns.md +120 -0
  119. package/skills/stellar-scf-grants-advisor/reference/05-comeback-patterns.md +92 -0
  120. package/skills/stellar-scf-grants-advisor/reference/06-budget-benchmarks.md +125 -0
  121. package/skills/stellar-scf-grants-advisor/reference/07-integration-list.md +147 -0
  122. package/skills/stellar-scf-grants-advisor/reference/08-abstract-fields.md +258 -0
  123. package/skills/stellar-scf-grants-advisor/reference/09-build-fields.md +259 -0
  124. package/skills/stellar-scf-grants-advisor/reference/10-corpus-winners.md +135 -0
  125. package/skills/stellar-scf-grants-advisor/reference/11-corpus-rejects.md +267 -0
  126. package/skills/stellar-scf-grants-advisor/reference/12-client-communication.md +131 -0
  127. package/skills/stellar-scf-grants-advisor/reference/13-excel-output-spec.md +199 -0
  128. package/skills/stellar-scf-grants-advisor/reference/14-worked-examples.md +160 -0
  129. package/skills/stellar-scf-grants-advisor/templates/abstract-review-builder.py +253 -0
  130. package/skills/stellar-scf-grants-advisor/templates/build-review-builder.py +213 -0
  131. package/skills/video-direction/video-direction.skill.json +20 -0
  132. package/skills/video-style-vocabulary/video-style-vocabulary.skill.json +20 -0
  133. package/skills/video-toolbox/video-toolbox.skill.json +20 -0
  134. package/src/__fixtures__/lifecycle-presets.flat-before.json +205 -0
  135. package/src/agency-os.yaml +122 -0
  136. package/src/agent-os-skill-slugs.test.ts +65 -0
  137. package/src/apify-social-reader.test.ts +317 -0
  138. package/src/base.yaml +85 -39
  139. package/src/blockchain-ecosystem.yaml +1 -0
  140. package/src/brand-library.yaml +525 -14
  141. package/src/builder-dev-fold.template.test.ts +113 -0
  142. package/src/builder.yaml +779 -91
  143. package/src/business-model-catalog-description.test.ts +47 -0
  144. package/src/business-model-grp.template.test.ts +214 -0
  145. package/src/business-model.yaml +296 -49
  146. package/src/catalog-safety.test.ts +198 -0
  147. package/src/catalog.template-items.test.ts +75 -0
  148. package/src/catalog.ts +66 -2
  149. package/src/concept-guard.test.ts +28 -8
  150. package/src/concept-guard.ts +2 -2
  151. package/src/content-creation.template.test.ts +52 -0
  152. package/src/content-creation.yaml +192 -0
  153. package/src/content-os.edit-loop.template.test.ts +210 -0
  154. package/src/content-os.publish-due-posts.template.test.ts +261 -0
  155. package/src/content-os.template.test.ts +322 -0
  156. package/src/content-os.video-takes.template.test.ts +338 -0
  157. package/src/content-os.work-a-take.template.test.ts +125 -0
  158. package/src/content-os.yaml +1830 -39
  159. package/src/content-studio-retired.template.test.ts +82 -0
  160. package/src/corpus-flow-validity.test.ts +4 -5
  161. package/src/crm-absorbs-outreach.template.test.ts +117 -0
  162. package/src/crm-build-plan.template.test.ts +128 -0
  163. package/src/crm-lists.template.test.ts +96 -0
  164. package/src/crm-messages-cadence.template.test.ts +164 -0
  165. package/src/crm-one-send-door.template.test.ts +65 -0
  166. package/src/crm.yaml +1456 -181
  167. package/src/decisions-wave.template.test.ts +186 -0
  168. package/src/define.ts +130 -17
  169. package/src/design-to-implementation.template.test.ts +94 -0
  170. package/src/domain-template-type.test.ts +75 -0
  171. package/src/ecosystem.yaml +27 -66
  172. package/src/enterprise-os.yaml +10 -134
  173. package/src/facecam-edit.audio-studio.test.ts +188 -0
  174. package/src/fal-generate-image.test.ts +200 -0
  175. package/src/fal-media-lab-pod-files.test.ts +180 -0
  176. package/src/finance.yaml +254 -1
  177. package/src/foundation.yaml +78 -38
  178. package/src/frame-host-modules.d.mts +6 -0
  179. package/src/frame-host-modules.mjs +58 -0
  180. package/src/generate.mjs +9 -3
  181. package/src/github-merge.test.ts +210 -0
  182. package/src/grants.yaml +46 -17
  183. package/src/hr.yaml +39 -6
  184. package/src/index.ts +36 -1
  185. package/src/intent.test.ts +161 -0
  186. package/src/internal-runbook.yaml +19 -1
  187. package/src/jev.test.ts +356 -0
  188. package/src/legal.yaml +33 -1
  189. package/src/life-os.yaml +51 -240
  190. package/src/lifecycle-coverage.test.ts +124 -0
  191. package/src/lifecycle-declarations.test.ts +252 -0
  192. package/src/lifecycle-presets.test.ts +468 -0
  193. package/src/lifecycle-presets.ts +494 -0
  194. package/src/lifecycle-stuck.test.ts +46 -0
  195. package/src/lifecycle-waits.parity.test.ts +319 -0
  196. package/src/marketing.yaml +108 -4
  197. package/src/meetings-knowledge.yaml +18 -0
  198. package/src/networking.yaml +23 -2
  199. package/src/operations.yaml +304 -4
  200. package/src/pipeline-views.template.test.ts +88 -0
  201. package/src/playbook-goal-placeholders.test.ts +57 -0
  202. package/src/postiz-contract.test.ts +134 -0
  203. package/src/presentation-declarations.test.ts +174 -0
  204. package/src/process-contract.validate.test.ts +352 -0
  205. package/src/project-management.yaml +39 -740
  206. package/src/publish-body.d.mts +29 -0
  207. package/src/publish-body.mjs +74 -0
  208. package/src/publish-body.test.ts +74 -0
  209. package/src/publish-official-lib.test.ts +176 -0
  210. package/src/replicate-rvm.test.ts +504 -0
  211. package/src/research-base.yaml +119 -20
  212. package/src/research-decision.yaml +60 -3
  213. package/src/reserved-profile-slugs.test.ts +83 -0
  214. package/src/retired-goal-kinds.test.ts +58 -0
  215. package/src/role-reference-and-step-domain.test.ts +192 -0
  216. package/src/seed-obligation.test.ts +1 -6
  217. package/src/send-message-maps.test.ts +227 -0
  218. package/src/shared-role-ssot.test.ts +40 -2
  219. package/src/ship-a-change.template.test.ts +177 -0
  220. package/src/skills.test.ts +170 -0
  221. package/src/social.yaml +85 -27
  222. package/src/space-packages.freshness.test.ts +97 -0
  223. package/src/space-packages.generated.ts +40 -0
  224. package/src/space-packages.test.ts +125 -0
  225. package/src/space-packages.ts +73 -0
  226. package/src/start.test.ts +188 -0
  227. package/src/task-intents.test.ts +293 -0
  228. package/src/templates.ts +20851 -21496
  229. package/src/types.ts +443 -60
  230. package/src/unipile-publish-post.test.ts +162 -0
  231. package/src/validate-flow.drift.test.ts +2 -3
  232. package/src/validate-flow.ts +22 -2
  233. package/src/validate-onboarding.test.ts +59 -0
  234. package/src/validate.test.ts +71 -4
  235. package/src/validate.ts +1335 -7
  236. package/src/wire-core.ts +231 -0
  237. package/src/wire.test.ts +159 -0
  238. package/src/wire.ts +107 -0
  239. package/src/agent-fleet.yaml +0 -1645
  240. package/src/business-developer.template.test.ts +0 -351
  241. package/src/business-developer.yaml +0 -860
  242. package/src/content-studio.yaml +0 -361
  243. package/src/dev-dashboard.yaml +0 -990
  244. package/src/outreach-comms.yaml +0 -486
  245. package/src/the-arch.yaml +0 -201
package/README.md ADDED
@@ -0,0 +1,483 @@
1
+ # The Synap catalog
2
+
3
+ ONE home for every official installable template: workspaces, capabilities, skills,
4
+ packs and automations. The team sees, edits, validates and publishes them all from
5
+ this folder, through one lib and one CLI. The live catalog is the Control Plane's
6
+ `synap_packages` table, and it has one write door: `POST /api/packages` →
7
+ `publishPackageCore`.
8
+
9
+ > **Name today:** `synap-app/packages/workspace-templates/`, npm
10
+ > `@synap-core/workspace-templates`. **Planned, not done:** a rename to
11
+ > `packages/catalog/`, `@synap-core/catalog`, with the old name kept as a re-export
12
+ > shim for one release. See [Coordinated rename](#5-coordinated-rename-planned).
13
+
14
+ How installs, reconciles and tiers work is in the `synap-market` skill
15
+ (`synap-backend/skills/synap-market/`), which is also the agent door.
16
+
17
+ ---
18
+
19
+ ## 1. What lives here, by kind
20
+
21
+ | Dir | Kind | Format | Published today? |
22
+ |---|---|---|---|
23
+ | `src/*.yaml` | `workspace` (and `pack`, for a suite) | Legacy workspace YAML (`meta:` plus body). A thin adapter reads it as `{kind: workspace, meta, spec}` | Yes. 31 entries, CI on every push to main |
24
+ | `capabilities/<key>.capability.json` | `capability` | Manifest (below). `meta` carries the listing and visibility; `spec` is the CapabilityDefinition | Yes, by hand. CI also publishes it once `SYNAP_CATALOG_LIVE_KINDS` includes `capability` (§3) |
25
+ | `skills/<slug>/` | `skill` | Either a manifest `<slug>.skill.json` (`creative-director`), or an Agent-Skills folder (`SKILL.md` plus references) with a header `skills/<slug>.catalog.json` beside it | The manifest: yes. A folder: only with its header, and only when it holds no reference files (a skill package carries no documents). Today one folder has a header (`designing-with-real-components`); the five moved folders have none and are **staged** |
26
+ | `packs/*.json` | `pack` (CP profile/view fragment) | CP `_meta` fragment | **Staged** (founder decision pending: what `pack` means). The CP's insert-only `seed-templates.ts` still owns them |
27
+ | `automations/<slug>.automation.json` | `automation` | bridge-setup `flowDefinition` form, plus a header `automations/<slug>.catalog.json` beside it | Only with its header, and only without apply-time placeholders. Today none has a header, so all three are **staged** (`mail-feed` also carries two placeholders bridge-setup fills) |
28
+ | `schema/` | | Zod manifest schema: `header.ts`, plus one body per kind (`workspace`, `capability`, `skill`, `automation`, `playbook`) | |
29
+ | `standards/*.standard.yaml` | | Quality standard per workspace (`pnpm eval`) | |
30
+ | `scripts/catalog-lib.mjs` | | **The ONE lib**: `loadCatalog` → `validateCatalog` → `diffCatalog` → `publishCatalog` | |
31
+ | `src/*.ts` | | The typed bundle (`dist/`) that clients ship as the zero-network fallback | |
32
+
33
+ A staged file is read, validated and counted, and the problem list carries a
34
+ `staged` warning for it. It cannot reach `POST /api/packages`. Today the
35
+ whole-catalog validate prints `✓ 73 catalog entries validated (selected: 31
36
+ workspace · 41 capability · 1 skill · 0 pack · 0 automation)` (41 = 35 seeded on
37
+ the CP + 6 authored here first).
38
+
39
+ **What stays elsewhere:**
40
+ - **System skills** (`system/*`) stay in `synap-backend/skills/`. They are the pod's
41
+ teaching substrate, seeded at boot, and are not marketplace items. Catalog files
42
+ may reference `system/*` slugs; the validator checks those exist when the
43
+ sibling repo is checked out, and says it skipped when it is not.
44
+ - **CLI runtime skills** (`authoring-*`, `coordinating-*`) stay in `synap-cli/templates/skills/`.
45
+
46
+ ## 2. The one manifest shape
47
+
48
+ ```jsonc
49
+ {
50
+ "apiVersion": "synap/v1",
51
+ "kind": "capability", // workspace | capability | skill | pack | automation
52
+ "meta": { // the header: same limits as the CP publishSchema
53
+ "slug": "apify", // kebab-case; dots only for a capability whose slug === spec.key
54
+ "name": "Apify — Web Scraping",
55
+ "description": "Find and run any Apify actor to collect data from public web pages and social posts. …",
56
+ "tags": ["capability"], "isPublic": true, "syncByDefault": true
57
+ // also: icon, color, domain, requiredTier, version?, owner ("template-owner" ⇔ private)
58
+ },
59
+ "spec": { "key": "apify", "skills": [ … ] }, // the per-kind body
60
+ "dependencies": [] // { kind, slug } — what an install pulls in
61
+ }
62
+ ```
63
+
64
+ One pure compiler, `toPublishBody(manifest)`, turns a manifest into today's
65
+ `POST /api/packages` body. The CP door and its `PACKAGE_TYPES` are unchanged. A
66
+ workspace publishes as category `workspace`, a capability as `capability`, a skill
67
+ as `skill`, an automation as `workflow`.
68
+
69
+ Playbook goals use `{name}` placeholders. `@{arg:}` is refused at authoring; the
70
+ runtime still reads it.
71
+
72
+ ## 3. Add or update, per kind
73
+
74
+ **Workspace.** Edit or add `src/<slug>.yaml`, along with its `*.template.test.ts`
75
+ when the template has one. Then run `pnpm --filter @synap-core/workspace-templates build`.
76
+ That regenerates `src/templates.ts` and `dist/`. The publish refuses a stale
77
+ `dist/`. **Bump `package.json` `version` whenever content changes**: the official
78
+ publish stamps `definition.sourcePackage.version`, and clients let a remote row win
79
+ only when it is strictly newer than their bundle.
80
+
81
+ **Capability.** Edit `capabilities/<key>.capability.json`. The listing and
82
+ visibility go in `meta`; the definition goes in `spec`.
83
+ - **Transition:** an **existing** capability also has a CP copy,
84
+ `synap-control-plane-api/src/seeds/capability-templates/<key>.capability.json`,
85
+ whose content must equal `spec`. Its header lives in `capability-listings.ts` and
86
+ `CAPABILITY_VISIBILITY` in the seeder, and must equal `meta`. Change both sides.
87
+ The CP tripwire `catalog-mirror.tripwire.test.ts` turns red otherwise.
88
+ - A **new** capability is authored here only: catalog-only files are allowed.
89
+ - **A reader** is an ordinary verb that reads one link: `intent: "read_webpage"`, `metadata.readOnly: true`,
90
+ a required `url` parameter, and `urls: [...]` declaring the links it handles (`host[/path]`, `*.host`,
91
+ or `"*"`; the grammar lives in `@synap-core/types/url-read`). The pod's one read door routes by those
92
+ patterns; nothing else names a vendor. It returns a `UrlRead` (`status`, `text`, `media`, ...), saves
93
+ nothing, and an account-billed one runs only on the person's tap. Reference: `apify`'s
94
+ `capture_social_post`. `synap catalog validate` enforces it (`skill-urls`). The agents' own door to
95
+ reading a link is the pod's built-in `web.read` verb (Synap Core), not a catalog capability: the old
96
+ `web.read` pack is retired.
97
+
98
+ **Skill.** Either a manifest `skills/<slug>/<slug>.skill.json` (`kind: skill`,
99
+ `spec` = the CP's `definition.skill`), or an Agent-Skills folder
100
+ `skills/<slug>/SKILL.md`. A folder stays in the shape `synap bridge-setup` reads; to
101
+ publish it, add a header BESIDE it (never inside: bridge-setup imports every file in
102
+ the folder as a document):
103
+
104
+ ```jsonc
105
+ // skills/<slug>.catalog.json — a manifest without spec
106
+ { "apiVersion": "synap/v1", "kind": "skill",
107
+ "meta": { "slug": "<slug>", "name": "Display name", "description": "listing copy", "tags": [], "isPublic": true },
108
+ "dependencies": [] }
109
+ ```
110
+
111
+ It compiles to `definition.skill = { slug: <frontmatter name>, name: meta.name,
112
+ description: <frontmatter description>, kind: "instruction", scope: "pod", body }`,
113
+ the same slug/description/body bridge-setup imports. Refused: reference files in the
114
+ folder (`skill-documents`), `metadata.auto_load: true` (`skill-auto-load`), a
115
+ frontmatter name that is not `meta.slug` (`skill-slug`). Without a header the folder
116
+ is staged. Deciding which folders get a header (their listing and visibility) is a
117
+ content decision; `reading-client-progress` and `stellar-scf-grants-advisor` are
118
+ client-specific.
119
+ - **Transition:** the five moved folders (`bookmark-enrichment`,
120
+ `mapping-the-server`, `reading-client-progress`, `routing-to-channels`,
121
+ `stellar-scf-grants-advisor`) must stay byte-identical to
122
+ `synap-cli/templates/skills/<slug>/`, because `bridge-setup` still reads them
123
+ there. The guard is the CLI's `test/catalog-mirror.tripwire.test.ts`.
124
+
125
+ **Pack.** `packs/*.json` must stay byte-identical to the CP's `templates/packs/`
126
+ (CP tripwire).
127
+
128
+ **Automation.** Either a manifest (`kind: automation`, `spec` in the authoring
129
+ `flow` form), or a raw `automations/<slug>.automation.json` in the `flowDefinition`
130
+ form bridge-setup reads, published once a header `automations/<slug>.catalog.json`
131
+ (`kind: "automation"`, no `spec`) sits beside it. It publishes as category
132
+ `workflow`: `definition = { profiles: [], automations: [{ key: <slug>, name,
133
+ description?, triggerType, triggerConfig, flowDefinition, status }] }`, i.e. the raw
134
+ file minus its `metadata` notes. Refused: an apply-time placeholder
135
+ (`apply-time-placeholder`: an empty or `<…>` connection id, a `<…>` output channel,
136
+ a skill node without `skillId`) and any raw key a package cannot carry (`state`).
137
+ Transition: the three raw files must stay byte-identical to
138
+ `synap-cli/templates/automations/` (CLI tripwire).
139
+
140
+ **Every kind.** Run the gate before you push:
141
+
142
+ ```bash
143
+ cd synap-app
144
+ node_modules/.bin/vitest run packages/workspace-templates # schema, parity, lifecycle, goal grammar
145
+ pnpm --filter @synap-core/workspace-templates type-check:catalog # schema/ + scripts/
146
+ synap catalog validate # the whole corpus, offline
147
+ ```
148
+
149
+ The lib needs Node ≥ 22.18, because it imports `schema/*.ts` through native type
150
+ stripping. An older Node refuses with `unsupported-runtime`.
151
+
152
+ ## Retiring a template
153
+
154
+ A template that leaves the catalog is **not** just deleted: it gets a tombstone in
155
+ `retired.yaml` (schema `schema/retired.ts`), so a pod can tell "retired, replaced by
156
+ X" from "never heard of it" and offer the person a safe move to X.
157
+
158
+ 1. Delete the file (`src/<slug>.yaml`, `capabilities/<key>.capability.json`, …).
159
+ 2. Add an entry to `retired.yaml`:
160
+ ```yaml
161
+ - slug: radar
162
+ kind: workspace # default; capability · skill · pack · automation
163
+ replacedBy: ecosystem # a LIVE slug of the same kind, or omit when nothing replaces it
164
+ reason: Radar was merged into Ecosystem, which tracks competitors and trends in one place.
165
+ since: "2026-07-16"
166
+ ```
167
+ 3. `synap catalog validate`: `replacedBy` must be live and not retired, no slug may be
168
+ both live and retired **in the same category** (the CP keys rows on slug + category,
169
+ so a retired workspace `posthog-analytics` beside a live capability of that slug is
170
+ fine), and the reason must be one plain sentence a person reads.
171
+ 4. Publish as usual. `publishCatalog` sends each tombstone of the selected kinds to
172
+ `POST /api/packages/retire` through the catalog door, **after** the live publishes
173
+ (a replacement published in the same run exists by then; a tombstone whose
174
+ replacement failed is not sent). The preview prints `retire radar → ecosystem` with
175
+ its status: `new` (no CP row, a tombstone row is created), `changed` (the live row is
176
+ retired, or its retirement updated), `unchanged` (not sent). A CP without the endpoint
177
+ is reported as such, never skipped silently.
178
+
179
+ Removing an entry from `retired.yaml` does not un-retire the CP row. The evidence
180
+ behind each seeded entry is in the pod file `00c1b2fe-ef98-41fa-86cf-801dfab12cd1` ("Template lifecycle: decisions wave + lane catalog").
181
+
182
+ ## Removing a kind from a live template
183
+
184
+ `retired.yaml` tombstones a whole template, not a kind inside one. There is no
185
+ kind-level tombstone: remove the `profiles:` entry and everything that names it
186
+ (views, relations, entity bento, onboarding `collect`, standards), bump the
187
+ `package.json` version (minor for a removed kind), and republish. A pod that already
188
+ installed the template keeps its profile and entities (profiles are never removed from
189
+ an installed pod); only new installs lose the kind. The kinds removed this way are
190
+ listed in `src/retired-goal-kinds.test.ts`, which fails if a template declares one again
191
+ (Life OS `goal`, Enterprise OS `objective`, Project Management `okr`: "Goal" is the
192
+ pod's track, decision D2, 2026-10-23).
193
+
194
+ ## Writing a process (a playbook that follows a kind's lifecycle)
195
+
196
+ The status belongs to the **kind or role**, never to a playbook. Its profile declares the lifecycle ONCE, in its
197
+ template:
198
+
199
+ ```yaml
200
+ lifecycle:
201
+ property: post-status
202
+ options:
203
+ - { key: Idea, category: open }
204
+ - { key: Published, category: done }
205
+ - { key: Archived, category: lost, humanOnly: true }
206
+ ```
207
+
208
+ Every option has one category (`open · active · paused · done · lost`); rules read the category, names stay free.
209
+ `humanOnly` marks an option no agent sets; an agent's move into `done` or `lost` proposes. Every move goes through
210
+ the one status door (`moveStatus`; an agent sends `update_entity` with `expectedStatus`).
211
+
212
+ A step that can still move may also say when it is late and what it waits on, by the pod's own rules
213
+ (`lifecycleOptionWaitsProblems` in `src/validate.ts`; never on a done or lost option):
214
+
215
+ ```yaml
216
+ - key: sent
217
+ category: active
218
+ dateBinding: dueDate # late once this date passes while it stands here
219
+ waits: { on: external, waitKind: payment, refFrom: clientId, dateBinding: dueDate }
220
+ ```
221
+
222
+ `dateBinding` and `waits.refFrom` / `waits.dateBinding` are property slugs. `waits.on` is a hold's `on` (person ·
223
+ agent · external · thing · decision · date · nobody); `waitKind` one of the pod's `WAIT_KINDS`.
224
+
225
+ A new lifecycle should adopt one of the ten bases (`src/lifecycle-presets.ts`: work-item, run, decision, asset,
226
+ publication, agreement, pipeline, relationship, offering, initiative) and say only how it differs:
227
+
228
+ ```yaml
229
+ lifecycle:
230
+ property: reference-status
231
+ adopts: asset
232
+ rename: { in-review: studied, deprecated: archived } # base key → this kind's key
233
+ drop: [] # base keys this kind does not use
234
+ add: [] # { key, category, after | before }
235
+ humanOnly: {} # per step; false removes a base's mark
236
+ stuckAfterDays: {} # per step
237
+ dateBinding: {} # per step: { sent: dueDate }
238
+ waits: {} # per step: { sent: { on: external } }
239
+ ```
240
+
241
+ The build compiles it into the flat `options` above and writes the select's `enumValues` when the YAML leaves them
242
+ out; nothing new reaches the pod. A lifecycle whose order the bases cannot express stays flat.
243
+
244
+ A playbook that works that lifecycle is a **process**: it FOLLOWS it. Its stages name options of it, its workers
245
+ start on it, and a person decides at the moments that commit the business. It never keeps a second status field.
246
+ The reference is Content OS "Produce Content" (`synap-app/packages/workspace-templates/src/content-os.yaml`).
247
+
248
+ **Official vs pod-local.** An official template or capability lives ONLY in the catalog
249
+ (`synap-app/packages/workspace-templates/`, by kind) and is published with
250
+ `synap catalog publish --official` (CI on main). A playbook, kind or automation an agent creates on a pod
251
+ (`synap_create_playbook`, `synap_define_kind`, …) is **pod-local**: it never reaches the catalog. To make it
252
+ official, write it into the catalog file and publish.
253
+
254
+ | Field | Where | Rule |
255
+ |---|---|---|
256
+ | `subjectProfile.profileSlug` | playbook | The kind or role the process works. |
257
+ | `subjectProfile.statusProperty` | playbook | Omit it: the pod follows the profile's declared lifecycle. If you name it, name that same property. A status worker on a role with no declared lifecycle is refused. |
258
+ | `stages[].subjectStatus` | stage | Entering the stage moves the subject to this option, through the door. Must be an **existing option** of the lifecycle — never invent one. |
259
+ | `stages[].gate` | stage | `{kind: human}` at every commitment: send to a client, sign, pay, publish, won/lost, anything irreversible. `{kind: check}` only where the stage's own criteria can be verified. A gated stage writes its status only when the gate passes. A check gate judges the stage being LEFT, so never use it to guard an irreversible status. |
260
+ | `stages[].onFail` | stage | `{toStage: <key>}` — where a rejected gate sends the run (rework loop). |
261
+ | `stages[].criteria` | stage | 2–5 binary statements; `check.kind` evidence (cheapest) · capability · judge · human. |
262
+ | `expectedOutputs[]` | stage or playbook | What the step must produce: `kind` + `label` + `relationToSubject` (an existing relation slug, or `none`). The UI shows what was produced from this. |
263
+ | `activators[]` (workers) | playbook | `on`: `created` · `enters_status` (+ `status`) · `count` (+ `status`, `min` ≥ 2) · `schedule` (+ `cron`, UTC) · `manual`. `size`: `per_thing` (default) · `per_moment` (count and schedule always are). `mode`: `propose` (default) · `run` only when cheap and safe. `maxPendingApprovals` (default 20) throttles. Compiled to a governed rule the owner can pause; it fires once per thing per transition. |
264
+ | `humanOnlyStatuses` | playbook | Legacy: the options no stage owns. Mark a person-only option `humanOnly` on the lifecycle instead; while `statusProperty` is set, `synap catalog validate` still checks that stages and this list cover every option. |
265
+ | `goalTemplate` | playbook | Placeholders are `{paramName}`. Never `@{arg:…}` (read-only legacy). |
266
+ | `params` | playbook | The subject is a `type: entity` param. A required param left unanswered becomes a question owed by the person, not an empty string. |
267
+
268
+ Human moments use an `ask` on an owed slot: `confirm` / `choose` (one `recommended`) / `form` / `act` / `provide`
269
+ (connection, file or secret through the vault). Never ask for a secret in a form.
270
+
271
+ Validation: `synap catalog validate` (process contract, goal grammar, lifecycle coverage ratchet — the count of
272
+ lifecycle kinds with no process may only go down).
273
+
274
+ ## Intent: what a space produces
275
+
276
+ A space is named by its PURPOSE first ("Product · Dev", "Sales · CRM") and is read through the THINGS it makes.
277
+ The template that owns the space says both, in `meta.intent`:
278
+
279
+ ```yaml
280
+ meta:
281
+ intent:
282
+ label: Product # the purpose word, ≤ 24 characters
283
+ produces: # ordered; kinds or roles
284
+ - profile: devplane_feature
285
+ - profile: devplane_story
286
+ - profile: ui-design
287
+ ```
288
+
289
+ - **Several kinds, never one only.** List what a person would come to the space to see move, in the order a home
290
+ shows them.
291
+ - **No `produces` (or `[]`) = a library.** Brand, Strategy, Ecosystem, Knowledge… are read, not moved: they carry
292
+ a label only.
293
+ - **Each produced profile declares a lifecycle** — a home counts things by lifecycle category, so a kind without
294
+ one cannot be counted (`intent-no-lifecycle`, naming it). Declare the lifecycle first (see "Writing a process").
295
+ - **Each produced profile is the template's own or one it depends on** (`require`/`compose` closure), e.g. CRM
296
+ produces the `lead` and `client` roles whose lifecycles `foundation` declares (`intent-profile-unknown`).
297
+ - **Only a template that owns a space declares it.** An overlay (`compose`) layers onto its base's space and a
298
+ pack (`suite`) owns none, so neither carries an intent (`intent-no-workspace`); `base` carries none either
299
+ (it is seeded into every space).
300
+ - **On the pod.** Both install doors forward it as a top-level `intent` (never in `_meta`, which the CP rebuilds
301
+ on serve); the pod keeps it in `workspaces.settings.intent`. A space with no declared intent gets one inferred at
302
+ read time (the profiles there that declare a lifecycle and hold things), never stored.
303
+
304
+ ## 4. The ONE command set
305
+
306
+ ```bash
307
+ synap catalog validate [--kind k] [slug...] # offline: per-kind schema, cross-kind refs, goal grammar, duplicates
308
+ synap catalog diff [--kind k] [slug...] # vs the live CP: new / changed / unchanged (+ warnings)
309
+ synap catalog publish --official [--kind k] [slug...] [--dry-run] [--yes] [--json]
310
+ synap market publish --official [slug...] [--dry-run] # alias of: catalog publish --official --kind workspace
311
+ ```
312
+
313
+ - `publish` requires `--official` and a `synap login` session that owns the
314
+ `synap-official` vendor. The CLI checks the publisher (`checkPublisher`) **before**
315
+ it shows the diff. A non-official account is refused up front. A failed read is
316
+ reported as `cp-unreachable`, never as "not official". `diff` warns instead of
317
+ refusing.
318
+ - `publish` validates the **whole corpus** and stops on any error. It then shows the
319
+ diff, asks y/N, and writes only after you confirm. `--dry-run` stops after the
320
+ preview. Warnings never block.
321
+ - A selection (`--kind`, slugs) narrows what gets planned and published. Validation
322
+ always covers the whole corpus, so a workspace's `dependencies` still resolve.
323
+ - The CLI finds the checkout by package name (`@synap-core/catalog` first, then
324
+ `@synap-core/workspace-templates`), or by `--catalog-dir`. Every error code maps
325
+ to a single next step (`hint` in `--json`).
326
+
327
+ **CI** (`synap-app/.github/workflows/publish-official-templates.yml`, through the
328
+ wrapper `scripts/publish-official.mjs`, which uses the same lib):
329
+
330
+ | Event | What runs |
331
+ |---|---|
332
+ | PR touching the catalog | Job `validate` only: build, validate every kind, print the **offline** preview (each entry and the version the CP will derive, where knowable offline). It holds **no secret and no `id-token`**: a PR runs its own lockfile and scripts. Guarded by `scripts/publish-workflow.guard.test.ts`. |
333
+ | Push to `main` (or manual) | `validate`, then job `publish` (environment `catalog-publish`): a read-only preview against the live CP, then publish live `--kind ${SYNAP_CATALOG_LIVE_KINDS:-workspace}`, then npm publish, only if the `package.json` version is new on npm. |
334
+
335
+ `SYNAP_CATALOG_LIVE_KINDS` is a repository **variable**. While it is unset, main
336
+ publishes workspaces only: every kind is still validated and previewed, but only
337
+ workspaces are written. Set it to `workspace,capability,skill,pack,automation`
338
+ only after runbook §5 A. Secrets: `SYNAP_CP_URL`, `SYNAP_CP_PUBLISH_TOKEN`
339
+ (a CP PAT of the `synap-official` owner; humans use `synap login`), `NPM_TOKEN`.
340
+
341
+ **One-time setup of the `catalog-publish` environment** (GitHub → synap-app →
342
+ Settings → Environments):
343
+ 1. Create the environment `catalog-publish`.
344
+ 2. Deployment branches and tags: **Selected branches → `main`** only.
345
+ 3. Add the environment secrets `SYNAP_CP_PUBLISH_TOKEN` and `SYNAP_CP_URL`.
346
+ 4. **Delete** the repository-level `SYNAP_CP_PUBLISH_TOKEN` (and `SYNAP_CP_URL`)
347
+ secrets, so no other job, and no workflow a branch pusher writes, can read them.
348
+ `NPM_TOKEN` may stay a repository secret (it is read only by the same job).
349
+ Until step 3 is done, the `publish` job has no token: it validates and leaves a
350
+ `::warning::`, it never publishes.
351
+
352
+ **The CP never reseeds on deploy.** `deploy.sh`, `update.sh` and ops `apply` no
353
+ longer run `seed:capabilities` or `seed:templates`. Both seeders are **insert-only
354
+ bootstraps** for an empty CP, run by hand: they skip any slug that already has a
355
+ row. A tripwire (`no-template-reseed-on-deploy-tripwire.test.ts`) guards this.
356
+ **Official capability keys are reserved:** a non-official publish of an official
357
+ key gets 409 `OFFICIAL_CAPABILITY_KEY_RESERVED`, and the CP serves the official row
358
+ first for a key.
359
+
360
+ **Transition mirrors** (each retires in the same change that deletes its copies; see §5).
361
+ These guards are **local-only**: each skips when the sibling repo is not checked out,
362
+ and every repo's CI checks out itself alone, so CI does not enforce them. Run them
363
+ on a full monorepo checkout before merging a change to either side. The same holds
364
+ for `schema/header.mirror.tripwire.test.ts` (header limits ↔ the CP `publishSchema`)
365
+ and `scripts/catalog-lib.columns.tripwire.test.ts` (the preview's columns and
366
+ adoption rule ↔ the CP publish core); their CI-safe halves are snapshots.
367
+
368
+ | Copy | Guard | Retires when |
369
+ |---|---|---|
370
+ | CP `src/seeds/capability-templates/*.capability.json` + listings/visibility | CP `src/seeds/catalog-mirror.tripwire.test.ts` (spec- and meta-equal) | the CP pin is bumped and the seeder reads the package |
371
+ | CP `templates/packs/*.json` | same file (byte-equal) | same |
372
+ | CLI `templates/skills/<5 moved>/`, `templates/automations/*.automation.json` | CLI `test/catalog-mirror.tripwire.test.ts` (byte-equal, both directions) | the CLI's pin admits a package release that ships them: `bridge-setup` already reads the installed package first, and the CLI's retirement test fails while both exist |
373
+
374
+ ## 5. Founder runbook: deploy, publish, retire the copies
375
+
376
+ Do these in order. The runbook only names the commands; it does not run anything.
377
+
378
+ ### A. Turn on catalog-published capabilities
379
+
380
+ 1. **Pre-flight on the prod CP DB.** Both queries must return 0 rows. If either
381
+ does not, resolve the duplicates first.
382
+ ```sql
383
+ SELECT lower(definition->'capability'->>'key') k, count(*)
384
+ FROM packages WHERE category = 'capability' GROUP BY 1 HAVING count(*) > 1;
385
+ SELECT slug, count(*)
386
+ FROM packages WHERE category = 'capability' GROUP BY 1 HAVING count(*) > 1;
387
+ ```
388
+ 2. **Deploy the backend first:** `26638d5e` ("an unanswered optional declared param
389
+ renders empty") must be live on the pods before any playbook goal written as
390
+ `{name}` reaches them; otherwise a literal `{programName}` lands in agent prompts.
391
+ Order: **backend → CP → dry run → publish.**
392
+ 3. **Deploy the CP** with all of these commits in that one deploy:
393
+ - `b669f90a`: posthog listing.
394
+ - `7ff44bf9`: capabilities publish onto their seeded row (official adoption, `syncByDefault`, dotted slug, description up to 10,000, full-column compare).
395
+ - `dac1da23`: reserved official keys, official-first read.
396
+ - `fe56bdf6`: reseed retired, seeder is insert-only.
397
+ - `d7a3f401`: ops drift is a human step.
398
+ - `0b0be3c8`: a capability key's owner is resolved before visibility; a key is a string equal to its slug.
399
+ - `a1401509`: an official publish adopts by the adoption rule, never a row of another kind.
400
+ - `6417b9f9`: `GET /api/packages/:slug?category=` serves the row a publish writes (the preview reads it).
401
+
402
+ Never ship `fe56bdf6` without `7ff44bf9` and `dac1da23`: on the old door, a catalog
403
+ publish forks every seeded row. This deploy no longer reseeds, so the rows stay
404
+ as the last reseed left them.
405
+ 4. **Dry run**, signed in as the official publisher (a private row is invisible to
406
+ an anonymous read): `synap catalog publish --official --kind capability --dry-run`.
407
+ The preview compares each body with the row the publish WRITES (the CP's adoption
408
+ rule) on the content version AND every column the CP writes, and prints a
409
+ content change as `~ definition: new content version` and each changed column
410
+ as `~ column: before → after`.
411
+ 5. **Read the diff.** Expected, and NOT a reason to stop:
412
+ - The **seeded capabilities** each show `CHANGED` with the same `h-` version and
413
+ only `~ source: "file-seed" → "api"`: the first publish writes each row once.
414
+ - **arch-client-intelligence** and **proton** show `CHANGED` with a new version (3 playbooks: Propose Grant Submission, Call Prep Brief, Draft Reply):
415
+ their playbook goals moved to the `{name}` grammar (`84363910`), which the
416
+ retired reseed never shipped.
417
+ - **posthog-analytics** shows `CHANGED` on its listing columns (`b669f90a`).
418
+ - **canva, dropbox, notion, slack, vercel** show `CHANGED` with a new version:
419
+ they have been official rows since 2026-10-01 (published from the old root
420
+ copies), and the catalog copy is newer (a derived `provides`, `72dd8d66`, and
421
+ reconciled descriptions). Their `~ tags` line shows only the AUTHORED tags
422
+ (`["capability"]`): the CP appends the search tokens it derives from the
423
+ definition, so the row keeps its search words and gains `capability` first.
424
+ - **content-idea-to-production** shows `CHANGED` on its definition and
425
+ description: the "Retired" listing the reseed never shipped.
426
+ - **claude-code-routines** is `NEW`. Publish it early: until an official row
427
+ exists, its key can be claimed by anyone.
428
+ - Any other `changed` row, or a warning that the CP ignores `?category=` (an
429
+ older CP): **STOP** and look.
430
+ 6. **Publish:** `synap catalog publish --official --kind capability`. Confirm at the
431
+ prompt.
432
+ 7. **Widen CI:** set the repo variable
433
+ `SYNAP_CATALOG_LIVE_KINDS=workspace,capability,skill,automation`. A skill folder
434
+ or raw automation publishes only once it has a header; `pack` stays out until
435
+ its meaning is decided.
436
+ 8. On a **brand-new, empty** CP only: run
437
+ `docker compose -f docker-compose.prod.yml run --rm control-plane pnpm seed:capabilities`
438
+ (and `seed:templates`) once, then step 6.
439
+
440
+ ### B. Publish the package and retire the mirrors
441
+
442
+ 1. **Publish the package to npm.** Merge a version bump to main, and CI publishes
443
+ it. Local is `0.13.0` and npm has `0.12.0`, so `check-publish-freshness` is red
444
+ until then. Never run `npm publish` by hand.
445
+ 2. **Bump the pins** and refresh each repo's own lockfile:
446
+ - **CP:** `^0.12.0` → `>=0.13.0 <1.0.0`. The caret excludes 0.13. Do this only
447
+ once the CP `pnpm-lock.yaml` is no longer peer-dirty.
448
+ - **CLI:** `>=0.12.0 <1.0.0`, which already admits 0.13. Refresh with
449
+ `pnpm install --lockfile-only --ignore-workspace` so the root lockfile is not
450
+ touched.
451
+ - **Backend:** `packages/api` **and** `apps/api` (`>=0.11.0 <1.0.0`). Move the
452
+ floor up only if a consumer needs the new content.
453
+ 3. **Point the readers at the package:**
454
+ - the CP seeder and capability resolver (`capability-template-dir.ts`) read
455
+ `node_modules/@synap-core/workspace-templates/capabilities/`, plus `packs/` for
456
+ `seed-templates.ts`;
457
+ - `synap bridge-setup` installs the published skill and automation entries
458
+ through the pod instead of reading `templates/`.
459
+ 4. **Delete the copies and their tripwires in the same change:**
460
+ - CP: `src/seeds/capability-templates/`, `templates/packs/`, the listing and
461
+ visibility literals, and `catalog-mirror.tripwire.test.ts`;
462
+ - CLI: the five moved skill folders, `templates/automations/`, and
463
+ `test/catalog-mirror.tripwire.test.ts`.
464
+
465
+ ### C. Coordinated rename (planned)
466
+
467
+ Rename `packages/workspace-templates` to `packages/catalog` (`@synap-core/catalog`)
468
+ in one change, at a quiet moment, because every tree's `node_modules` links move:
469
+
470
+ 1. `git mv synap-app/packages/workspace-templates synap-app/packages/catalog`.
471
+ 2. Update **both** workspace files: the root `pnpm-workspace.yaml`
472
+ (`synap-app/packages/workspace-templates`) and `synap-app/pnpm-workspace.yaml`
473
+ (`packages/workspace-templates`).
474
+ 3. Change the `package.json` `name` to `@synap-core/catalog`. Ship a thin
475
+ `@synap-core/workspace-templates` shim that re-exports it for one release. The
476
+ backend pins `>=0.11 <1`, the CP `^0.12`, the CLI `>=0.12 <1`, and the
477
+ synap-app/browser consumers use `workspace:*`.
478
+ 4. Update the workflow paths and `--filter` in `publish-official-templates.yml`,
479
+ and the `ci.yml` freshness step.
480
+ 5. Run `pnpm install` in each tree: the monorepo root, `synap-app/`, and each
481
+ sibling that installs on its own. Then run every consumer's typecheck.
482
+ 6. The CLI and both tripwires already resolve `packages/catalog` first. Remove the
483
+ `workspace-templates` fallback once the shim is retired.