@synap-core/workspace-templates 0.10.0 → 0.12.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.
package/package.json CHANGED
@@ -1,13 +1,11 @@
1
1
  {
2
2
  "name": "@synap-core/workspace-templates",
3
- "version": "0.10.0",
3
+ "version": "0.12.0",
4
4
  "type": "module",
5
- "packageManager": "pnpm@9.15.4",
6
5
  "description": "Canonical workspace definitions — YAML source + typed exports for browser, backend, CLI, and marketplace",
7
6
  "exports": {
8
7
  ".": {
9
8
  "types": "./dist/index.d.ts",
10
- "node": "./dist/index.js",
11
9
  "default": "./dist/index.js"
12
10
  }
13
11
  },
@@ -15,16 +13,6 @@
15
13
  "dist",
16
14
  "src"
17
15
  ],
18
- "scripts": {
19
- "type-check": "tsc --noEmit",
20
- "build:yaml": "node --experimental-strip-types build.mjs",
21
- "check:converters": "node scripts/check-converters.mjs",
22
- "check:concepts": "node scripts/check-concepts.mjs",
23
- "build": "node --experimental-strip-types build.mjs && tsup && node scripts/check-converters.mjs && node scripts/check-concepts.mjs",
24
- "provision": "node scripts/provision.mjs",
25
- "eval": "node scripts/eval-standard.mjs",
26
- "prepublishOnly": "node --experimental-strip-types build.mjs && tsup && node scripts/check-converters.mjs && node scripts/check-concepts.mjs"
27
- },
28
16
  "dependencies": {
29
17
  "zod": "^3.0.0",
30
18
  "js-yaml": "^4.1.0"
@@ -37,15 +25,18 @@
37
25
  },
38
26
  "publishConfig": {
39
27
  "registry": "https://registry.npmjs.org",
40
- "access": "public",
41
- "main": "./dist/index.js",
42
- "module": "./dist/index.js",
43
- "types": "./dist/index.d.ts",
44
- "exports": {
45
- ".": {
46
- "types": "./dist/index.d.ts",
47
- "default": "./dist/index.js"
48
- }
49
- }
50
- }
51
- }
28
+ "access": "public"
29
+ },
30
+ "scripts": {
31
+ "type-check": "tsc --noEmit",
32
+ "build:yaml": "node --experimental-strip-types build.mjs",
33
+ "check:converters": "node scripts/check-converters.mjs",
34
+ "check:concepts": "node scripts/check-concepts.mjs",
35
+ "build": "node --experimental-strip-types build.mjs && tsup && node scripts/check-converters.mjs && node scripts/check-concepts.mjs",
36
+ "provision": "node scripts/provision.mjs",
37
+ "eval": "node scripts/eval-standard.mjs"
38
+ },
39
+ "main": "./dist/index.js",
40
+ "module": "./dist/index.js",
41
+ "types": "./dist/index.d.ts"
42
+ }
@@ -8,14 +8,14 @@
8
8
  * seeders be deleted (Wave 4) without silently changing what every workspace
9
9
  * gets.
10
10
  *
11
- * FIDELITY MECHANISM. The report prompts are the product — a single altered
12
- * character is a regression. Importing `REPORT_AUTOMATION_FLOW` from
13
- * `@synap/database` into this frontend package would be an upward layering
14
- * violation, so instead the byte-for-byte guardrail is a COMMITTED, GENERATED
15
- * fixture (`__fixtures__/report-automation-flow.json`) snapshotted from the
16
- * backend const at authoring time, never hand-typed. The chain the test proves:
17
- * compiled base flow === fixture (=== REPORT_AUTOMATION_FLOW at capture).
18
- * Same for the 23-ish relation defs via `__fixtures__/default-relation-defs.json`.
11
+ * FIDELITY MECHANISM. base.yaml is now the SINGLE source of the report flow —
12
+ * the backend `REPORT_AUTOMATION_FLOW` const it was once snapshotted against was
13
+ * retired, and with it the `__fixtures__/report-automation-flow.json` copy-pin
14
+ * (a copy pinned to a copy caught no upstream drift). The report flow is guarded
15
+ * here directly instead: the prompt-fragment smoke check below proves the product
16
+ * content survived, and `templates-freshness.test.ts` pins templates.ts == the
17
+ * YAML source. The 23-ish relation defs KEEP their byte-for-byte fixture
18
+ * guardrail (`__fixtures__/default-relation-defs.json`).
19
19
  */
20
20
 
21
21
  import { readFileSync } from "fs";
@@ -55,18 +55,13 @@ describe("Task A — report automation, byte-faithful", () => {
55
55
  expect(automation?.description).toMatch(/Gather this workspace's state/);
56
56
  });
57
57
 
58
- it("flow BYTE-MATCHES the REPORT_AUTOMATION_FLOW snapshot", () => {
59
- const fixture = readFixture("report-automation-flow.json");
60
- // deep-equal: the compiled template's flow is JSON.stringify(parsed YAML), so
61
- // a deep-equal against the JSON snapshot of the source const proves the
62
- // transcription is lossless (nodes, edges, and every prompt string).
63
- expect(automation?.flow).toEqual(fixture);
64
- });
65
-
66
58
  it("preserves the load-bearing hardened prompt fragments (v13)", () => {
67
- // A coarse smoke check that the specific product content survived — the
68
- // deep-equal above is authoritative, but these named fragments make a
69
- // regression legible if the fixture is ever regenerated wrongly.
59
+ // The primary guardrail that the report flow's product content survived in
60
+ // base.yaml. (A byte-for-byte fixture pin used to sit above this, snapshotting
61
+ // the retired backend `REPORT_AUTOMATION_FLOW` const; with that const gone
62
+ // base.yaml is the single source, so a copy-pinned-to-a-copy proved nothing
63
+ // and was dropped. `templates-freshness.test.ts` pins templates.ts == the
64
+ // YAML source.)
70
65
  const flowStr = JSON.stringify(automation?.flow);
71
66
  for (const marker of [
72
67
  "COUNTS — the exact number of records gathered",
@@ -77,6 +72,17 @@ describe("Task A — report automation, byte-faithful", () => {
77
72
  expect(flowStr).toContain(marker);
78
73
  }
79
74
  });
75
+
76
+ it("carries the v15 retry policy on all four AI rounds (regression guard)", () => {
77
+ // v15's fix for the 2026-08-03 finishReason=length incident: each of the four
78
+ // ai.generate rounds (analyze/relate/assemble/summarize) gets ONE retry with a
79
+ // 5s delay. This field dropped SILENTLY once — the 700→2000 fix ported maxTokens
80
+ // into base.yaml but not the retry policy, so it went missing on the reconcile-
81
+ // winning path. Assert it here (base.yaml is the SSOT now) so it can't drift again.
82
+ const flowStr = JSON.stringify(automation?.flow);
83
+ expect((flowStr.match(/"retryDelay":5000/g) ?? []).length).toBe(4);
84
+ expect((flowStr.match(/"maxRetries":1(?=[,}])/g) ?? []).length).toBe(4);
85
+ });
80
86
  });
81
87
 
82
88
  describe("Task B — relation defs, full coverage", () => {
package/src/base.yaml CHANGED
@@ -455,9 +455,11 @@ automations:
455
455
  Notes — {{steps.gather-notes.output.count}} found: {{steps.project-notes.output.result}}
456
456
  People — {{steps.gather-people.output.count}} found: {{steps.project-people.output.result}}
457
457
  Companies — {{steps.gather-companies.output.count}} found: {{steps.project-companies.output.result}}
458
- maxTokens: "700"
458
+ maxTokens: "2000"
459
459
  errorHandling:
460
460
  continueOnError: true
461
+ maxRetries: 1
462
+ retryDelay: 5000
461
463
  - id: relate
462
464
  type: capability
463
465
  position:
@@ -537,9 +539,11 @@ automations:
537
539
  is busy, a company with people but no tasks. Write only observations
538
540
  that span TWO OR MORE records or kinds. If there are none, say what you
539
541
  checked and did not find — never answer with nothing.
540
- maxTokens: "700"
542
+ maxTokens: "2000"
541
543
  errorHandling:
542
544
  continueOnError: true
545
+ maxRetries: 1
546
+ retryDelay: 5000
543
547
  - id: assemble
544
548
  type: capability
545
549
  position:
@@ -724,6 +728,9 @@ automations:
724
728
  RELATE ROUND MATERIAL:
725
729
  {{steps.relate.output}}
726
730
  maxTokens: "2000"
731
+ errorHandling:
732
+ maxRetries: 1
733
+ retryDelay: 5000
727
734
  - id: summarize
728
735
  type: capability
729
736
  position:
@@ -742,6 +749,9 @@ automations:
742
749
  Material to summarize (may be an error object if the round failed):
743
750
  {{steps.analyze.output}}
744
751
  maxTokens: "500"
752
+ errorHandling:
753
+ maxRetries: 1
754
+ retryDelay: 5000
745
755
  - id: guard-assembled
746
756
  type: guard
747
757
  position:
@@ -47,6 +47,10 @@ profiles:
47
47
  description: A liquidity provider — makes markets and provides depth for a token.
48
48
  scope: shared
49
49
  profileKind: role
50
+ # Shared supply-role category → the intro-matcher's roster spans ALL roles
51
+ # tagged "provider" via entity.query { roleCategory: "provider" } (no per-role
52
+ # hardcoding). Keep this string in sync with networking.yaml's solution-provider.
53
+ roleCategory: provider
50
54
  applicableKinds:
51
55
  - company
52
56
  - person
@@ -81,6 +85,10 @@ profiles:
81
85
  description: A security firm or individual that audits smart contracts and protocols.
82
86
  scope: shared
83
87
  profileKind: role
88
+ # Shared supply-role category → the intro-matcher's roster spans ALL roles
89
+ # tagged "provider" via entity.query { roleCategory: "provider" } (no per-role
90
+ # hardcoding). Keep this string in sync with networking.yaml's solution-provider.
91
+ roleCategory: provider
84
92
  applicableKinds:
85
93
  - company
86
94
  - person
@@ -115,6 +123,10 @@ profiles:
115
123
  description: An outsourced engineering studio or build partner.
116
124
  scope: shared
117
125
  profileKind: role
126
+ # Shared supply-role category → the intro-matcher's roster spans ALL roles
127
+ # tagged "provider" via entity.query { roleCategory: "provider" } (no per-role
128
+ # hardcoding). Keep this string in sync with networking.yaml's solution-provider.
129
+ roleCategory: provider
118
130
  applicableKinds:
119
131
  - company
120
132
  - person
@@ -149,6 +161,10 @@ profiles:
149
161
  description: A brand, growth, or content marketing agency.
150
162
  scope: shared
151
163
  profileKind: role
164
+ # Shared supply-role category → the intro-matcher's roster spans ALL roles
165
+ # tagged "provider" via entity.query { roleCategory: "provider" } (no per-role
166
+ # hardcoding). Keep this string in sync with networking.yaml's solution-provider.
167
+ roleCategory: provider
152
168
  applicableKinds:
153
169
  - company
154
170
  - person
@@ -183,6 +199,10 @@ profiles:
183
199
  description: A performance / paid-media buying agency.
184
200
  scope: shared
185
201
  profileKind: role
202
+ # Shared supply-role category → the intro-matcher's roster spans ALL roles
203
+ # tagged "provider" via entity.query { roleCategory: "provider" } (no per-role
204
+ # hardcoding). Keep this string in sync with networking.yaml's solution-provider.
205
+ roleCategory: provider
186
206
  applicableKinds:
187
207
  - company
188
208
  - person
@@ -217,6 +237,10 @@ profiles:
217
237
  description: A key opinion leader or influencer who amplifies reach.
218
238
  scope: shared
219
239
  profileKind: role
240
+ # Shared supply-role category → the intro-matcher's roster spans ALL roles
241
+ # tagged "provider" via entity.query { roleCategory: "provider" } (no per-role
242
+ # hardcoding). Keep this string in sync with networking.yaml's solution-provider.
243
+ roleCategory: provider
220
244
  applicableKinds:
221
245
  - company
222
246
  - person
@@ -243,6 +267,88 @@ profiles:
243
267
  valueType: string
244
268
  inputType: textarea
245
269
 
270
+ # ── Grant Program (the per-chain funding PROGRAM — parent of a round) ───────
271
+ # The 3-layer grants spine is PROGRAM ≠ ROUND ≠ APPLICATION. `round` (in
272
+ # grants.yaml) is the funding-cycle/deadline carrier; a program is the
273
+ # long-lived, chain-scoped funder above it (e.g. Stellar SCF, Optimism RetroPGF).
274
+ # It lives HERE, with the web3 vocabulary, because a program IS a blockchain-
275
+ # ecosystem thing (it carries a `blockchain` and program mechanics), and it is
276
+ # a real-world entity referenced from every workspace — hence pod-wide, mirroring
277
+ # company/person in ecosystem.yaml (`entityScope: pod`). `scope: shared` makes
278
+ # the profile DEF pod-wide too, so a REUSING template (grants.yaml `require`s this
279
+ # overlay) resolves `grant-program` for its `round → belongs_to → grant-program`
280
+ # edge and deadline view. `defaultPlaybook` is a text ref for now — Wave 2 will
281
+ # create the actual per-chain playbook and bind it.
282
+ - slug: grant-program
283
+ displayName: Grant Program
284
+ icon: landmark
285
+ color: emerald
286
+ description: A per-chain funding program (e.g. Stellar SCF, Optimism RetroPGF) — the long-lived funder above a round.
287
+ profileKind: kind
288
+ entityScope: pod
289
+ scope: shared
290
+ properties:
291
+ - slug: blockchain
292
+ label: Blockchain / Chain
293
+ valueType: string
294
+ inputType: text
295
+ - slug: programName
296
+ label: Program Name
297
+ valueType: string
298
+ inputType: text
299
+ - slug: programType
300
+ label: Program Type
301
+ valueType: string
302
+ inputType: select
303
+ enumValues:
304
+ - direct
305
+ - retroactive
306
+ - quadratic
307
+ - rfp
308
+ - slug: status
309
+ label: Status
310
+ valueType: string
311
+ inputType: select
312
+ enumValues:
313
+ - active
314
+ - paused
315
+ - retired
316
+ - slug: url
317
+ label: Program URL
318
+ valueType: string
319
+ inputType: url
320
+ - slug: cadence
321
+ label: Cadence
322
+ valueType: string
323
+ inputType: text
324
+ placeholder: every 6 weeks
325
+ - slug: nextDeadline
326
+ label: Next Deadline
327
+ valueType: date
328
+ inputType: date
329
+ - slug: submissionTracks
330
+ label: Submission Tracks
331
+ valueType: array
332
+ inputType: text
333
+ - slug: trancheModel
334
+ label: Tranche Model
335
+ valueType: string
336
+ inputType: text
337
+ placeholder: 4 tranches / 90 days
338
+ - slug: defaultPlaybook
339
+ label: Default Playbook
340
+ valueType: string
341
+ inputType: text
342
+ # Defaults to the BASE grant playbook (grants.yaml `Grant Process`), the
343
+ # data-driven process that reads THIS program's cadence/tracks/tranche
344
+ # model at runtime. Override per-program only for a differently-shaped chain.
345
+ constraints:
346
+ defaultValue: Grant Process
347
+ - slug: description
348
+ label: Description
349
+ valueType: string
350
+ inputType: textarea
351
+
246
352
  # Web3 actor↔actor relations (company→company). `company` resolves from the
247
353
  # Ecosystem base — not redeclared here.
248
354
  entityLinks:
package/src/builder.yaml CHANGED
@@ -1,6 +1,17 @@
1
1
  # Builder workspace template
2
2
  # Developer + agent intelligence layer.
3
- # Generated from synap-app/apps/devplane/lib/builderWorkspaceDefinition.ts (source of truth).
3
+ #
4
+ # ✅ SINGLE SOURCE OF TRUTH for the Builder workspace, on every door.
5
+ # Both doors now derive from this file:
6
+ # • apps/devplane/lib/builderWorkspaceDefinition.ts → the devplane onboarding
7
+ # path (trpc.workspaces.createFromDefinition) — since 2026-09-05 that module
8
+ # is a five-line `toWorkspaceDefinition("builder-workspace")` derivation
9
+ # • the registry / marketplace install path — same seam
10
+ # Until 2026-09-05 the TS file held a ~1,150-line hand-typed PEER copy of this
11
+ # workspace and nothing compared the two, which produced five real divergences
12
+ # (four capabilities, a dead-Coolify seed pair, defaultApp/primarySurface, an
13
+ # unprefixed calendar dateField, and this file's playbooks/onboarding). Edit the
14
+ # schema HERE; there is no second copy to keep in sync.
4
15
 
5
16
  meta:
6
17
  slug: builder-workspace
@@ -23,6 +34,13 @@ workspace:
23
34
  - builder.deployments
24
35
  - builder.agents
25
36
  - builder.packages
37
+ # Tier-1 signal for `resolveWorkspaceForScope` — must match the DevPlane app
38
+ # manifest's `defaultScope.capability` (browser/…/apps/manifest.ts) and the
39
+ # `workspaceCapabilities` list the devplane door derives from this block.
40
+ # Without it a workspace created through THIS door fails all four resolver tiers
41
+ # (no capability · subtype "builder" ≠ kind "devplane" · name "Builder" ≠ "DevPlane")
42
+ # and the workspace press falls silently to a setup card.
43
+ - devplane.workspace
26
44
 
27
45
  profiles:
28
46
  - slug: devplane_app