@synap-core/workspace-templates 0.10.0 → 0.11.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/dist/index.d.ts +59 -34
- package/dist/index.js +375 -7
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/__fixtures__/report-automation-flow.json +2 -2
- package/src/base.yaml +2 -2
- package/src/blockchain-ecosystem.yaml +106 -0
- package/src/catalog.ts +36 -0
- package/src/grants.yaml +248 -1
- package/src/networking.yaml +14 -3
- package/src/templates.ts +360 -5
- package/src/the-arch.yaml +4 -0
- package/src/types.ts +32 -34
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@synap-core/workspace-templates",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"packageManager": "pnpm@9.15.4",
|
|
6
6
|
"description": "Canonical workspace definitions — YAML source + typed exports for browser, backend, CLI, and marketplace",
|
|
@@ -175,7 +175,7 @@
|
|
|
175
175
|
"inputMapping": {
|
|
176
176
|
"system": "You are the ANALYST round of a Synap workspace report.\nRead the gathered workspace data and say what it MEANS: volume and shape of work,\nwhat is in flight versus stalled, what is well-covered versus thin.\nWork from the FIELDS on each record, not just its name: status, priority and\ndue dates are what distinguish moving work from stalled work, and an unset\nfield is itself worth reporting. Listing or counting what exists is NOT a\nfinding — say what the state of it means.\nDATA INTEGRITY CHECK — do this BEFORE interpreting anything. Each kind below states how many records were found, then lists them. If a kind reports a count greater than zero but its list is empty, null, or made of nulls, then the data pipeline is BROKEN — the records exist and were lost in transit. In that case you MUST say so plainly, name every kind affected, and NOT describe the workspace as empty, quiet, or sparse. Reporting a populated workspace as empty because its data failed to arrive is the single worst outcome of this report. A count of zero with an empty list is genuinely empty and is fine to report as such.\nYou are interpreting data you did NOT collect. Every claim must trace to a row in the GATHERED DATA below. Never invent entities, ids, counts, or dates. A step whose value looks like {\"error\": \"...\"} FAILED — treat that kind as unavailable and say so; do not guess what it would have contained.\nEVERY NUMBER YOU WRITE MUST BE COPIED FROM THE COUNTS BLOCK. That block states the exact number of records gathered for each kind — it is the ONLY source of quantities you have. Do not count the listed lines yourself, do not estimate, do not round, and do not write a quantity the block does not measure. Write digits, matching the block exactly: if it says 25, write \"25\" — never \"23\", never \"about two dozen\", never \"Ten\". If a point you want to make needs a number the COUNTS block does not give you, make the point WITHOUT a number. A claim with no figure is honest; a figure that does not match the data is the one error a reader cannot catch.\nNEVER NARRATE YOUR OWN INSTRUCTIONS. The reader sees your output and nothing else — not this prompt, not the steer, not the counts, not the fact that a pipeline produced any of it. Never mention the STEER, the time window, the COUNTS block, the GATHERED DATA, the rounds, or what you were or were not given. Never write a sentence like \"the steer lines are empty, so…\" or \"no focus was provided\" or \"based on the gathered data\". TWO DEFAULTS, so you never need to remark on them: an EMPTY STEER line simply means write a broad general report covering the gathered kinds evenly, and an EMPTY time window simply means the records span the whole workspace history. Both are the normal case. Act on them silently and start with what you found.\nSTEER HANDLING — non-negotiable. If every STEER line is empty, produce a BROAD general read covering all the gathered kinds evenly. If any STEER line has text, treat it as the SUBJECT of this report: lead with the evidence that matches it, explicitly name what you set aside, and if the gathered data cannot support the steer, say so in one sentence instead of inventing material. Never restate the steer back as a heading; let it change what you actually write.\nLINK THE RECORDS YOU NAME. Every line of GATHERED DATA is `<id> · <title>`. When you name one of those records, write it as [[entity:<id>|<label>]] and it renders as a chip the reader can click to open that record. Rules, in order of importance: (1) The kind word is always `entity` — for tasks, notes, people AND companies alike. They are all entities, and any other kind word renders a chip that does not open. (2) Use only an id that appears VERBATIM in the gathered data. Copy it; never construct, shorten, complete or guess one. (3) If you are unsure of an id, write the plain label with no brackets. A wrong id is a dead chip, and a dead chip is worse than plain text. (4) The label is free text but must not contain `]`; the id must not contain `:` or `|`. A marker breaking either rule renders as raw literal text. (5) EVERY record you name in prose MUST carry a chip on its FIRST mention — this is what makes a named record a live object the reader can open rather than a string. Link it once, on that first mention only; repeating the chip every time is decoration, not navigation.\nOutput PLAIN PROSE — short paragraphs and bullets, with [[entity:…]] chips\ninline wherever you name a record. No markdown headings, no ::: directive\nblocks, no code fences. Under 300 words. Another round will format this.",
|
|
177
177
|
"prompt": "STEER:\n- Prompt: {{trigger.payload.prompt}}\n- Focus: {{trigger.payload.focus}}\n- Project lens id: {{trigger.payload.projectId}}\n\nCOUNTS — the exact number of records gathered for each kind. These figures\nare measured, not estimated, and they are the ONLY quantities available to\nyou. Each line also gives the cap the gather was limited to.\n- Tasks: {{steps.gather-tasks.output.count}} gathered (cap 25)\n- Notes: {{steps.gather-notes.output.count}} gathered (cap 15)\n- People: {{steps.gather-people.output.count}} gathered (cap 15)\n- Companies: {{steps.gather-companies.output.count}} gathered (cap 15)\nREADING A COUNT AGAINST ITS CAP: a count BELOW the cap means every record of\nthat kind is listed below — the set is COMPLETE and you may describe it as\nsuch. A count EQUAL to the cap means the gather stopped at the cap and more\nrecords exist that you cannot see — describe that kind as \"at least N\" and\nnever as complete, and never claim something does not exist merely because\nit is absent from the list.\n\nTime window: only records updated on or after {{trigger.payload.since}}\n\nGATHERED DATA — one line per record, MOST RECENTLY UPDATED first. Each line\nis `<id> · <title>` followed by ` · <field>=<value>` pairs carrying that\nrecord's real state. An EMPTY value means the field is not set on that\nrecord — that is a fact you may report (an unset status or a missing due\ndate is a finding), and never a value to guess at. Recent does not mean\nimportant.\nTasks — {{steps.gather-tasks.output.count}} found: {{steps.project-tasks.output.result}}\nNotes — {{steps.gather-notes.output.count}} found: {{steps.project-notes.output.result}}\nPeople — {{steps.gather-people.output.count}} found: {{steps.project-people.output.result}}\nCompanies — {{steps.gather-companies.output.count}} found: {{steps.project-companies.output.result}}",
|
|
178
|
-
"maxTokens": "
|
|
178
|
+
"maxTokens": "2000"
|
|
179
179
|
},
|
|
180
180
|
"errorHandling": {
|
|
181
181
|
"continueOnError": true
|
|
@@ -196,7 +196,7 @@
|
|
|
196
196
|
"inputMapping": {
|
|
197
197
|
"system": "You are the PATTERNS round of a Synap workspace report.\nYou receive the raw gathered data AND the analyst's read of it. Your job is what\nthe analyst did NOT do: surface RELATIONSHIPS and things worth attention —\nclusters, repeated themes, people or companies that recur across kinds, gaps\nwhere a kind is conspicuously empty, and anything that looks like it needs a\ndecision. Prefer three sharp observations over ten shallow ones.\nYOUR SCOPE IS WHAT CONNECTS RECORDS, not what they individually are. Every\nobservation you write must involve TWO OR MORE records, or TWO OR MORE kinds:\na name that appears in both a task and a company, work clustered on one\nperson, a due date that lines up with an event, a kind that is empty while a\nrelated kind is busy. Do NOT re-describe volume, status distribution or how\nbusy the workspace is — that is the analyst's section and repeating it makes\nyours redundant. Do not summarise or restate the analyst's read.\nNEVER RETURN AN EMPTY ANSWER. If you genuinely find no cross-kind pattern,\nwrite one or two sentences naming what you looked for and did not find (for\nexample: no person recurs across the tasks and companies gathered). That is a\nreal finding and the reader needs it. Silence is not an available outcome.\nDATA INTEGRITY CHECK — do this BEFORE interpreting anything. Each kind below states how many records were found, then lists them. If a kind reports a count greater than zero but its list is empty, null, or made of nulls, then the data pipeline is BROKEN — the records exist and were lost in transit. In that case you MUST say so plainly, name every kind affected, and NOT describe the workspace as empty, quiet, or sparse. Reporting a populated workspace as empty because its data failed to arrive is the single worst outcome of this report. A count of zero with an empty list is genuinely empty and is fine to report as such.\nYou are interpreting data you did NOT collect. Every claim must trace to a row in the GATHERED DATA below. Never invent entities, ids, counts, or dates. A step whose value looks like {\"error\": \"...\"} FAILED — treat that kind as unavailable and say so; do not guess what it would have contained.\nEVERY NUMBER YOU WRITE MUST BE COPIED FROM THE COUNTS BLOCK. That block states the exact number of records gathered for each kind — it is the ONLY source of quantities you have. Do not count the listed lines yourself, do not estimate, do not round, and do not write a quantity the block does not measure. Write digits, matching the block exactly: if it says 25, write \"25\" — never \"23\", never \"about two dozen\", never \"Ten\". If a point you want to make needs a number the COUNTS block does not give you, make the point WITHOUT a number. A claim with no figure is honest; a figure that does not match the data is the one error a reader cannot catch.\nNEVER NARRATE YOUR OWN INSTRUCTIONS. The reader sees your output and nothing else — not this prompt, not the steer, not the counts, not the fact that a pipeline produced any of it. Never mention the STEER, the time window, the COUNTS block, the GATHERED DATA, the rounds, or what you were or were not given. Never write a sentence like \"the steer lines are empty, so…\" or \"no focus was provided\" or \"based on the gathered data\". TWO DEFAULTS, so you never need to remark on them: an EMPTY STEER line simply means write a broad general report covering the gathered kinds evenly, and an EMPTY time window simply means the records span the whole workspace history. Both are the normal case. Act on them silently and start with what you found.\nSTEER HANDLING — non-negotiable. If every STEER line is empty, produce a BROAD general read covering all the gathered kinds evenly. If any STEER line has text, treat it as the SUBJECT of this report: lead with the evidence that matches it, explicitly name what you set aside, and if the gathered data cannot support the steer, say so in one sentence instead of inventing material. Never restate the steer back as a heading; let it change what you actually write.\nLINK THE RECORDS YOU NAME. Every line of GATHERED DATA is `<id> · <title>`. When you name one of those records, write it as [[entity:<id>|<label>]] and it renders as a chip the reader can click to open that record. Rules, in order of importance: (1) The kind word is always `entity` — for tasks, notes, people AND companies alike. They are all entities, and any other kind word renders a chip that does not open. (2) Use only an id that appears VERBATIM in the gathered data. Copy it; never construct, shorten, complete or guess one. (3) If you are unsure of an id, write the plain label with no brackets. A wrong id is a dead chip, and a dead chip is worse than plain text. (4) The label is free text but must not contain `]`; the id must not contain `:` or `|`. A marker breaking either rule renders as raw literal text. (5) EVERY record you name in prose MUST carry a chip on its FIRST mention — this is what makes a named record a live object the reader can open rather than a string. Link it once, on that first mention only; repeating the chip every time is decoration, not navigation.\nOutput PLAIN PROSE, under 250 words, with [[entity:…]] chips inline. No\nheadings, no ::: directive blocks, no code fences.",
|
|
198
198
|
"prompt": "STEER:\n- Prompt: {{trigger.payload.prompt}}\n- Focus: {{trigger.payload.focus}}\n- Project lens id: {{trigger.payload.projectId}}\n\nCOUNTS — the exact number of records gathered for each kind. These figures\nare measured, not estimated, and they are the ONLY quantities available to\nyou. Each line also gives the cap the gather was limited to.\n- Tasks: {{steps.gather-tasks.output.count}} gathered (cap 25)\n- Notes: {{steps.gather-notes.output.count}} gathered (cap 15)\n- People: {{steps.gather-people.output.count}} gathered (cap 15)\n- Companies: {{steps.gather-companies.output.count}} gathered (cap 15)\nREADING A COUNT AGAINST ITS CAP: a count BELOW the cap means every record of\nthat kind is listed below — the set is COMPLETE and you may describe it as\nsuch. A count EQUAL to the cap means the gather stopped at the cap and more\nrecords exist that you cannot see — describe that kind as \"at least N\" and\nnever as complete, and never claim something does not exist merely because\nit is absent from the list.\n\nTime window: only records updated on or after {{trigger.payload.since}}\n\nGATHERED DATA — one line per record, MOST RECENTLY UPDATED first. Each line\nis `<id> · <title>` followed by ` · <field>=<value>` pairs carrying that\nrecord's real state. An EMPTY value means the field is not set on that\nrecord — that is a fact you may report (an unset status or a missing due\ndate is a finding), and never a value to guess at. Recent does not mean\nimportant.\nTasks — {{steps.gather-tasks.output.count}} found: {{steps.project-tasks.output.result}}\nNotes — {{steps.gather-notes.output.count}} found: {{steps.project-notes.output.result}}\nPeople — {{steps.gather-people.output.count}} found: {{steps.project-people.output.result}}\nCompanies — {{steps.gather-companies.output.count}} found: {{steps.project-companies.output.result}}\n\nANALYST'S READ — already written and already in the report. Do NOT\nrestate, summarise or re-analyse it. It is here so you do not repeat it:\n{{steps.analyze.output}}\n\nYOUR TASK — cross-kind patterns ONLY. Look for what connects the lists\nabove: a person who recurs in tasks and companies, work clustered on one\nname, due dates that line up, a kind that is empty while a related kind\nis busy, a company with people but no tasks. Write only observations\nthat span TWO OR MORE records or kinds. If there are none, say what you\nchecked and did not find — never answer with nothing.",
|
|
199
|
-
"maxTokens": "
|
|
199
|
+
"maxTokens": "2000"
|
|
200
200
|
},
|
|
201
201
|
"errorHandling": {
|
|
202
202
|
"continueOnError": true
|
package/src/base.yaml
CHANGED
|
@@ -455,7 +455,7 @@ 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: "
|
|
458
|
+
maxTokens: "2000"
|
|
459
459
|
errorHandling:
|
|
460
460
|
continueOnError: true
|
|
461
461
|
- id: relate
|
|
@@ -537,7 +537,7 @@ automations:
|
|
|
537
537
|
is busy, a company with people but no tasks. Write only observations
|
|
538
538
|
that span TWO OR MORE records or kinds. If there are none, say what you
|
|
539
539
|
checked and did not find — never answer with nothing.
|
|
540
|
-
maxTokens: "
|
|
540
|
+
maxTokens: "2000"
|
|
541
541
|
errorHandling:
|
|
542
542
|
continueOnError: true
|
|
543
543
|
- id: assemble
|
|
@@ -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/catalog.ts
CHANGED
|
@@ -33,6 +33,20 @@ import type { WorkspaceYaml } from "./types.js";
|
|
|
33
33
|
// Inputs
|
|
34
34
|
// ============================================================================
|
|
35
35
|
|
|
36
|
+
/**
|
|
37
|
+
* A lightweight, QUERY-TIME summary of one sub-object a package carries (a cell
|
|
38
|
+
* today; the shape generalizes to views/automations without change). Rides the
|
|
39
|
+
* catalog LIST projection so a surface can match/surface a sub-item WITHOUT the
|
|
40
|
+
* full `definition` — it carries `key + name` only, never the sub-object's code.
|
|
41
|
+
* `kind` is DERIVED (in the CP SELECT, from which sub-array the row came from),
|
|
42
|
+
* never a stored taxonomy.
|
|
43
|
+
*/
|
|
44
|
+
export interface PackageItemSummary {
|
|
45
|
+
kind: string;
|
|
46
|
+
key: string;
|
|
47
|
+
name: string;
|
|
48
|
+
}
|
|
49
|
+
|
|
36
50
|
/**
|
|
37
51
|
* A bundled template — the canonical `WorkspaceYaml` this package ships. Callers
|
|
38
52
|
* pass `listPublicTemplates()` (18) or `listWorkspaceTemplates()` (21); the door
|
|
@@ -69,6 +83,13 @@ export interface RemoteEntry {
|
|
|
69
83
|
* never "unknown-so-hide".
|
|
70
84
|
*/
|
|
71
85
|
isPrivate?: boolean;
|
|
86
|
+
/**
|
|
87
|
+
* Lightweight computed summary of the row's sub-objects (cells), carried on the
|
|
88
|
+
* LIST projection so a surface can surface a matching sub-item without the full
|
|
89
|
+
* `definition`. `undefined` on sources that don't compute it (bundled) — treated
|
|
90
|
+
* as "no known sub-items". See `PackageItemSummary`.
|
|
91
|
+
*/
|
|
92
|
+
items?: PackageItemSummary[];
|
|
72
93
|
/**
|
|
73
94
|
* The CP `definition` jsonb — opaque install payload. The door reads only
|
|
74
95
|
* `definition.sourcePackage.version` (the winner signal). Absent on browse rows
|
|
@@ -128,6 +149,11 @@ export interface CpPackageRow {
|
|
|
128
149
|
icon?: string | null;
|
|
129
150
|
/** Present only on `/mine` rows. `false` ⇒ private. Browse rows omit it. */
|
|
130
151
|
isPublic?: boolean;
|
|
152
|
+
/**
|
|
153
|
+
* Computed sub-object summary from `GET /api/packages` / `/mine` (query-time,
|
|
154
|
+
* not a stored column). Carries `key + name` per cell; never the cell code.
|
|
155
|
+
*/
|
|
156
|
+
items?: PackageItemSummary[];
|
|
131
157
|
}
|
|
132
158
|
|
|
133
159
|
/**
|
|
@@ -154,6 +180,7 @@ export function rowToRemoteEntry(
|
|
|
154
180
|
tags: row.tags ?? [],
|
|
155
181
|
icon: row.icon ?? null,
|
|
156
182
|
isPrivate,
|
|
183
|
+
items: row.items,
|
|
157
184
|
// Both list routes strip `definition`; absence ⇒ bundle wins a slug conflict,
|
|
158
185
|
// and the install path fetches the full definition per-slug when needed.
|
|
159
186
|
};
|
|
@@ -175,6 +202,12 @@ interface CatalogEntryBase {
|
|
|
175
202
|
icon?: string | null;
|
|
176
203
|
/** `true` ⇒ not publicly listed (internal bundled template, or private CP row). */
|
|
177
204
|
isPrivate: boolean;
|
|
205
|
+
/**
|
|
206
|
+
* The entry's sub-object summary (cells). Always present (defaults to `[]`) so a
|
|
207
|
+
* consumer can match sub-items uniformly. Bundled workspace templates carry no
|
|
208
|
+
* cells → `[]` (honest); a remote row carries its CP-computed `items`.
|
|
209
|
+
*/
|
|
210
|
+
items: PackageItemSummary[];
|
|
178
211
|
}
|
|
179
212
|
|
|
180
213
|
/** A catalog entry backed by a bundled template. Install via `toWorkspaceDefinition()` / `toPackageDefinition()` on `template`. */
|
|
@@ -256,6 +289,8 @@ function bundledToEntry(tpl: WorkspaceYaml): BundledCatalogEntry {
|
|
|
256
289
|
tags: tpl.meta.tags ?? [],
|
|
257
290
|
icon: tpl.meta.icon,
|
|
258
291
|
isPrivate: tpl.meta.isPublic === false,
|
|
292
|
+
// Bundled workspace templates carry no cells — be honest, not `undefined`.
|
|
293
|
+
items: [],
|
|
259
294
|
template: tpl,
|
|
260
295
|
};
|
|
261
296
|
}
|
|
@@ -271,6 +306,7 @@ function remoteToEntry(row: RemoteEntry): RemoteCatalogEntry {
|
|
|
271
306
|
tags: row.tags ?? [],
|
|
272
307
|
icon: row.icon ?? null,
|
|
273
308
|
isPrivate: row.isPrivate ?? false,
|
|
309
|
+
items: row.items ?? [],
|
|
274
310
|
remote: row,
|
|
275
311
|
};
|
|
276
312
|
}
|
package/src/grants.yaml
CHANGED
|
@@ -43,6 +43,16 @@ dependencies:
|
|
|
43
43
|
kind: workspace
|
|
44
44
|
relation: require
|
|
45
45
|
reason: "Provides the pod-wide client / partner / sponsor role vocabulary this workspace reuses."
|
|
46
|
+
# This template REFERENCES the pod-wide `grant-program` kind (the per-chain
|
|
47
|
+
# funding-program parent) that the blockchain-ecosystem overlay defines. The
|
|
48
|
+
# 3-layer grants spine is program → round → grant_submission: `round` (owned
|
|
49
|
+
# here) `belongs_to` a `grant-program` (owned there). `require` installs that
|
|
50
|
+
# overlay FIRST so `grant-program` exists (pod-wide) before this template
|
|
51
|
+
# provisions the edge + deadline view — same deps-first pattern as foundation.
|
|
52
|
+
- slug: blockchain-ecosystem
|
|
53
|
+
kind: workspace
|
|
54
|
+
relation: require
|
|
55
|
+
reason: "Provides the pod-wide grant-program kind (per-chain funding programs) this workspace's rounds belong to."
|
|
46
56
|
|
|
47
57
|
workspace:
|
|
48
58
|
name: Grants Pipeline
|
|
@@ -873,11 +883,26 @@ views:
|
|
|
873
883
|
- averageAwardAmount
|
|
874
884
|
sortBy: roundNumber
|
|
875
885
|
sortDir: desc
|
|
886
|
+
# Round deadlines on one time axis. Scoped to `round` ONLY: rounds are the
|
|
887
|
+
# deadline SSOT (they carry `submissionDeadline`); `grant-program` carries
|
|
888
|
+
# `nextDeadline`, which the calendar's dateField fallback chain
|
|
889
|
+
# (dueDate/startDate/startTime/date) never resolves, so a program would land
|
|
890
|
+
# dateless in the "Unscheduled" backlog. Plotting programs + rounds on ONE
|
|
891
|
+
# timeline is a deferred UI-phase item. `colorField` (NOT `colorBy` — the
|
|
892
|
+
# calendar adapter reads `colorField`) colors each round by its status.
|
|
893
|
+
- name: Deadlines
|
|
894
|
+
type: calendar
|
|
895
|
+
scopeProfileSlug: round
|
|
896
|
+
description: Upcoming round submission deadlines, on one time axis.
|
|
897
|
+
config:
|
|
898
|
+
dateField: properties.submissionDeadline
|
|
899
|
+
colorField: status
|
|
900
|
+
# No `coverField`: grant-project has no image property (`summary` is prose, so it
|
|
901
|
+
# rendered as a text/blank cover). Fall back to the gallery's clean default cover.
|
|
876
902
|
- name: Awarded Projects
|
|
877
903
|
type: gallery
|
|
878
904
|
scopeProfileSlug: grant-project
|
|
879
905
|
config:
|
|
880
|
-
coverField: summary
|
|
881
906
|
showFields:
|
|
882
907
|
- currentStatus
|
|
883
908
|
- awardedRoundCount
|
|
@@ -907,6 +932,35 @@ entityLinks:
|
|
|
907
932
|
targetProfileSlug: company
|
|
908
933
|
type: submitted_by
|
|
909
934
|
label: Submitted By
|
|
935
|
+
# The application belongs to the engagement that pursued it (contract lives in
|
|
936
|
+
# the composed operations base). Closes the spine on the submission side.
|
|
937
|
+
- sourceProfileSlug: grant_submission
|
|
938
|
+
targetProfileSlug: contract
|
|
939
|
+
type: for_contract
|
|
940
|
+
label: Engagement
|
|
941
|
+
# The commercial bridge: a grant application maps to its fee/commission `deal`,
|
|
942
|
+
# so a grant win is tied to the money. Mirrors networking.yaml's canonical
|
|
943
|
+
# `intro → billed_as → deal` commercial-mapping predicate (same edge type,
|
|
944
|
+
# reused for one consistent meaning across the pod graph). `deal` resolves here
|
|
945
|
+
# because it is a SYSTEM profile (validate.ts `SYSTEM_PROFILES`) — so this needs
|
|
946
|
+
# NO crm compose (grants stays an operations overlay, not a CRM). The deal lives
|
|
947
|
+
# in the CRM workspace; this is a cross-workspace relation, consistent with
|
|
948
|
+
# grant_submission's other cross-domain edges (submitted_by → company).
|
|
949
|
+
- sourceProfileSlug: grant_submission
|
|
950
|
+
targetProfileSlug: deal
|
|
951
|
+
type: billed_as
|
|
952
|
+
label: Billed as
|
|
953
|
+
# 3-layer spine: a round is one cycle OF a long-lived program (SCF #42 → SCF).
|
|
954
|
+
- sourceProfileSlug: round
|
|
955
|
+
targetProfileSlug: grant-program
|
|
956
|
+
type: belongs_to
|
|
957
|
+
label: Program
|
|
958
|
+
# The engagement pursues a program (contract lives in the composed operations
|
|
959
|
+
# base; grant-program in the required blockchain-ecosystem overlay).
|
|
960
|
+
- sourceProfileSlug: contract
|
|
961
|
+
targetProfileSlug: grant-program
|
|
962
|
+
type: pursues
|
|
963
|
+
label: Pursues
|
|
910
964
|
- sourceProfileSlug: grant-project
|
|
911
965
|
targetProfileSlug: round
|
|
912
966
|
type: appears_in
|
|
@@ -923,6 +977,199 @@ entityLinks:
|
|
|
923
977
|
targetProfileSlug: grant_submission
|
|
924
978
|
type: evidenced_by
|
|
925
979
|
label: Evidenced By
|
|
980
|
+
|
|
981
|
+
# The BASE grant playbook — ONE reusable, data-driven process that runs an
|
|
982
|
+
# engagement through ANY program. Chain-specifics (cadence, submissionTracks,
|
|
983
|
+
# trancheModel, nextDeadline) are READ from the `grantProgram` entity at runtime,
|
|
984
|
+
# NOT hardcoded per chain — so one playbook serves Stellar SCF, Optimism RetroPGF,
|
|
985
|
+
# etc. Only a genuinely different-SHAPED program later earns its own override
|
|
986
|
+
# playbook (base + data-driven-override decision). Generalizes the Stellar staged
|
|
987
|
+
# advisor (CP `stellar-grant-client`) into a template. Bound to `contract` (the
|
|
988
|
+
# engagement, per decision — NOT company); targets a `grant-program` param — the
|
|
989
|
+
# 3-layer spine (contract → grant-program → round → grant_submission) made runnable.
|
|
990
|
+
playbooks:
|
|
991
|
+
- name: Grant Process
|
|
992
|
+
description: Run an engagement through a grant program end to end — identify fit, draft the application, submit, track the decision, and deliver the tranches. Reads the program's own cadence / tracks / tranche model so one playbook fits every chain.
|
|
993
|
+
goalTemplate: >-
|
|
994
|
+
Advance this engagement ({contract}) through the grant program
|
|
995
|
+
({grantProgram}) it is pursuing. FIRST read the program entity's own data —
|
|
996
|
+
its cadence, submissionTracks, trancheModel, and nextDeadline — plus the
|
|
997
|
+
client context on the contract, and let THOSE drive the plan: do not assume
|
|
998
|
+
a chain's mechanics, read them from the program. Then work the stages in
|
|
999
|
+
order and keep the client's `grant_submission` card as the durable state of
|
|
1000
|
+
record:
|
|
1001
|
+
|
|
1002
|
+
1. Identify — understand the project and which of the program's
|
|
1003
|
+
submissionTracks fit; set the submission `stage` to `scope`.
|
|
1004
|
+
2. Draft — write the abstract, then the full proposal (products & services,
|
|
1005
|
+
success criteria, GTM, traction); move `stage` `abstract` → `build` and
|
|
1006
|
+
fill the submission's body fields.
|
|
1007
|
+
3. Submit — finalize against the program's nextDeadline and submit; once
|
|
1008
|
+
submitted, set `stage` to `awaiting-results`.
|
|
1009
|
+
4. Awaiting — track the decision and keep `status` current
|
|
1010
|
+
(in-review / waiting-client / needs-document / blocked).
|
|
1011
|
+
5. Awarded — on a decision, set `outcome` (awarded / rejected / partial)
|
|
1012
|
+
and record the awarded amount.
|
|
1013
|
+
6. Delivering — for an award, work the program's trancheModel: fill the
|
|
1014
|
+
tranche deliverables and update trancheCompletion as each milestone ships.
|
|
1015
|
+
|
|
1016
|
+
Never fabricate — leave a field blank rather than guess. Every write is
|
|
1017
|
+
proposal-gated for the user to review.
|
|
1018
|
+
subjectProfile:
|
|
1019
|
+
profileSlug: contract
|
|
1020
|
+
params:
|
|
1021
|
+
- { name: contract, type: entity, profileSlug: contract, label: Engagement, required: true }
|
|
1022
|
+
- { name: grantProgram, type: entity, profileSlug: grant-program, label: Grant Program, required: true }
|
|
1023
|
+
# Generic stages, mapped onto grant_submission's own stage/status/outcome +
|
|
1024
|
+
# tranche vocab so the run can maintain the card as it advances.
|
|
1025
|
+
stages:
|
|
1026
|
+
- { key: identify, name: Identify, description: "Understand the project and which of the program's submission tracks fit — before any writing.", goal: "A clear project understanding and the fitting submission track; submission stage = scope." }
|
|
1027
|
+
- { key: draft, name: Draft, description: "Write the abstract, then the full proposal, from the client's real material.", goal: "Abstract then full proposal drafted; submission stage abstract → build with body fields filled." }
|
|
1028
|
+
- { key: submit, name: Submit, description: "Finalize against the program deadline and submit the application.", goal: "Application submitted on time; submission stage = awaiting-results." }
|
|
1029
|
+
- { key: awaiting, name: Awaiting, description: "Track the decision and keep the client status current.", goal: "Submission status reflects where the decision stands (in-review / waiting-client / needs-document / blocked)." }
|
|
1030
|
+
- { key: awarded, name: Awarded, description: "Record the funder's decision and the awarded amount.", goal: "Submission outcome set (awarded / rejected / partial) with the awarded amount." }
|
|
1031
|
+
- { key: delivering, name: Delivering, description: "Work the program's tranche model — ship and record each tranche.", goal: "Tranche deliverables filled and trancheCompletion updated as milestones ship." }
|
|
1032
|
+
executor: is-agent
|
|
1033
|
+
grants:
|
|
1034
|
+
- entity.create
|
|
1035
|
+
status: active
|
|
1036
|
+
|
|
1037
|
+
# Makes the spine ALIVE: a daily maintenance run that keeps each grant
|
|
1038
|
+
# application's `grant_submission` card current (advances stage + status) — the
|
|
1039
|
+
# durable, kanban-visible state of record. Generalizes the Stellar CP "Client
|
|
1040
|
+
# Sync" automation (read comms → assess → advance submission) into a chain-
|
|
1041
|
+
# AGNOSTIC workspace-template automation: it advances using grant_submission's
|
|
1042
|
+
# OWN stage/status vocab, so it works for any program (no Stellar-specific
|
|
1043
|
+
# assumptions). Same fan-out-safe shape the corpus uses (cron → in-flow `query`
|
|
1044
|
+
# → `loop`), NOT the `inputStrategy:'query'` playbook fan-out (which has the
|
|
1045
|
+
# known gap).
|
|
1046
|
+
automations:
|
|
1047
|
+
- key: grant-submission-sync
|
|
1048
|
+
name: Grant Submission Sync
|
|
1049
|
+
description: >-
|
|
1050
|
+
Once a day, keep each grant application's grant_submission card current —
|
|
1051
|
+
read the submission's recent activity, assess where the pursuit stands, and
|
|
1052
|
+
advance its stage + status so the pipeline stays the durable, kanban-visible
|
|
1053
|
+
state of record.
|
|
1054
|
+
triggerType: cron
|
|
1055
|
+
triggerConfig:
|
|
1056
|
+
expression: "0 8 * * *"
|
|
1057
|
+
# DRAFT by default (deliberate safety): this runs a paid AI assess per
|
|
1058
|
+
# grant_submission every day, so installing the suite must NOT silently start
|
|
1059
|
+
# paid runs. Flip to `active` to let the pipeline self-maintain — same
|
|
1060
|
+
# enabled-off-by-default convention as the radar / CRM-hygiene playbooks.
|
|
1061
|
+
status: draft
|
|
1062
|
+
metadata:
|
|
1063
|
+
templateKey: grant-submission-sync
|
|
1064
|
+
flow:
|
|
1065
|
+
nodes:
|
|
1066
|
+
- id: trigger
|
|
1067
|
+
type: trigger
|
|
1068
|
+
position: { x: 0, y: 0 }
|
|
1069
|
+
data:
|
|
1070
|
+
label: Daily at 08:00
|
|
1071
|
+
triggerType: cron
|
|
1072
|
+
config:
|
|
1073
|
+
expression: "0 8 * * *"
|
|
1074
|
+
|
|
1075
|
+
# READ-ONLY builtin + SELECTION. The grant_submission cards ARE the grant
|
|
1076
|
+
# pursuits (each is created by the `Grant Process` playbook at pursuit
|
|
1077
|
+
# start), so querying them directly is the safe selection: it touches ONLY
|
|
1078
|
+
# real pursuits and never fabricates a card for a non-grant contract.
|
|
1079
|
+
#
|
|
1080
|
+
# ── CREATE-IF-MISSING (deferred, flagged) ────────────────────────────
|
|
1081
|
+
# The Stellar reference does create-if-missing by querying its subject
|
|
1082
|
+
# (companies) and upserting the submission. Generalizing that safely means
|
|
1083
|
+
# selecting "contracts that PURSUE a grant program" — but `entity.query`
|
|
1084
|
+
# filters JSONB PROPERTIES only, it cannot filter by a `pursues` EDGE, and
|
|
1085
|
+
# there is no query-fan-out capability to enumerate edge-linked subjects.
|
|
1086
|
+
# Querying ALL contracts and creating a card per contract would spuriously
|
|
1087
|
+
# create submissions for every non-grant engagement. So creation stays the
|
|
1088
|
+
# `Grant Process` playbook's job (the agent creates the card when a pursuit
|
|
1089
|
+
# begins) and this automation MAINTAINS. Fully-generalized create-if-
|
|
1090
|
+
# missing needs an edge-aware selection capability that does not exist yet.
|
|
1091
|
+
- id: list-submissions
|
|
1092
|
+
type: capability
|
|
1093
|
+
position: { x: 0, y: 120 }
|
|
1094
|
+
data:
|
|
1095
|
+
label: List grant submissions to maintain
|
|
1096
|
+
verbId: entity.query
|
|
1097
|
+
inputMapping:
|
|
1098
|
+
profileSlug: grant_submission
|
|
1099
|
+
limit: "100"
|
|
1100
|
+
|
|
1101
|
+
- id: submissions-loop
|
|
1102
|
+
type: loop
|
|
1103
|
+
position: { x: 0, y: 240 }
|
|
1104
|
+
data:
|
|
1105
|
+
label: For each grant submission
|
|
1106
|
+
iteratorExpression: steps.list-submissions.output.entities
|
|
1107
|
+
itemVariable: submission
|
|
1108
|
+
|
|
1109
|
+
# READ-ONLY builtin. Recent activity on THIS submission's thread — the raw
|
|
1110
|
+
# signal the assess pass reads to place the pursuit's stage/status.
|
|
1111
|
+
- id: read-activity
|
|
1112
|
+
type: messages_query
|
|
1113
|
+
position: { x: 0, y: 360 }
|
|
1114
|
+
data:
|
|
1115
|
+
label: Read the submission's recent activity
|
|
1116
|
+
subjectEntityId: "{{loop.item.id}}"
|
|
1117
|
+
limit: 40
|
|
1118
|
+
|
|
1119
|
+
# READ-ONLY (pure compute). ONE assess pass → STRICT JSON naming the
|
|
1120
|
+
# submission's current stage + status, constrained to grant_submission's
|
|
1121
|
+
# OWN enum vocab so it maps straight back onto the card (chain-agnostic).
|
|
1122
|
+
- id: assess
|
|
1123
|
+
type: capability
|
|
1124
|
+
position: { x: 0, y: 480 }
|
|
1125
|
+
data:
|
|
1126
|
+
label: Assess the submission's stage + status → JSON
|
|
1127
|
+
verbId: ai.generate
|
|
1128
|
+
inputMapping:
|
|
1129
|
+
json: "true"
|
|
1130
|
+
maxTokens: "500"
|
|
1131
|
+
system: >-
|
|
1132
|
+
You are a strict data-extraction function for a grants pipeline.
|
|
1133
|
+
Read a grant application's recent activity and report where the
|
|
1134
|
+
application currently stands. Respond with STRICT JSON and NOTHING
|
|
1135
|
+
else. Never fabricate — if the activity does not clearly indicate a
|
|
1136
|
+
value, keep the submission's current one.
|
|
1137
|
+
prompt: >-
|
|
1138
|
+
The grant submission: {{loop.item.title}}
|
|
1139
|
+
Current stage: {{loop.item.properties.stage}}
|
|
1140
|
+
Current status: {{loop.item.properties.status}}
|
|
1141
|
+
|
|
1142
|
+
Recent activity (oldest first):
|
|
1143
|
+
{{steps.read-activity.output.messages}}
|
|
1144
|
+
|
|
1145
|
+
Report the submission's current state as STRICT JSON, using ONLY
|
|
1146
|
+
these vocabularies and NOTHING else:
|
|
1147
|
+
{
|
|
1148
|
+
"stage": "scope|abstract|build|awaiting-results",
|
|
1149
|
+
"status": "answered|waiting-client|needs-document|in-review|blocked"
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
# GOVERNED WRITE via `output/entity_update` — advances the card's stage +
|
|
1153
|
+
# status (the durable, kanban-visible state). Governed like every output
|
|
1154
|
+
# write: routes to a proposal for review unless auto-approved.
|
|
1155
|
+
- id: advance-submission
|
|
1156
|
+
type: output
|
|
1157
|
+
position: { x: 0, y: 600 }
|
|
1158
|
+
data:
|
|
1159
|
+
label: Advance the submission stage + status
|
|
1160
|
+
outputType: entity_update
|
|
1161
|
+
config:
|
|
1162
|
+
entityId: "{{loop.item.id}}"
|
|
1163
|
+
properties:
|
|
1164
|
+
stage: "{{steps.assess.output.stage}}"
|
|
1165
|
+
status: "{{steps.assess.output.status}}"
|
|
1166
|
+
edges:
|
|
1167
|
+
- { id: gs-trigger-list, source: trigger, target: list-submissions }
|
|
1168
|
+
- { id: gs-list-loop, source: list-submissions, target: submissions-loop }
|
|
1169
|
+
- { id: gs-loop-read, source: submissions-loop, target: read-activity }
|
|
1170
|
+
- { id: gs-read-assess, source: read-activity, target: assess }
|
|
1171
|
+
- { id: gs-assess-advance, source: assess, target: advance-submission }
|
|
1172
|
+
|
|
926
1173
|
app:
|
|
927
1174
|
kind: stored
|
|
928
1175
|
cellKey: entity-detail
|
package/src/networking.yaml
CHANGED
|
@@ -212,6 +212,11 @@ profiles:
|
|
|
212
212
|
description: A company or person that provides a capability The Arch can broker a client need against.
|
|
213
213
|
scope: shared
|
|
214
214
|
profileKind: role
|
|
215
|
+
# Shared supply-role category — the intro-matcher's roster is now
|
|
216
|
+
# entity.query { roleCategory: "provider" }, spanning EVERY role tagged
|
|
217
|
+
# "provider" (this one in a public install; + blockchain-ecosystem's web3
|
|
218
|
+
# supply roles when The Arch installs both). One string, kept in sync there.
|
|
219
|
+
roleCategory: provider
|
|
215
220
|
applicableKinds:
|
|
216
221
|
- company
|
|
217
222
|
- person
|
|
@@ -365,15 +370,21 @@ automations:
|
|
|
365
370
|
limit: "100"
|
|
366
371
|
|
|
367
372
|
# READ-ONLY builtin. The provider roster to match against — companies /
|
|
368
|
-
# people wearing
|
|
373
|
+
# people wearing ANY role tagged `roleCategory: "provider"`. Dynamic by
|
|
374
|
+
# design: a public install matches its `solution-provider`s; The Arch
|
|
375
|
+
# (which also installs blockchain-ecosystem) additionally matches the web3
|
|
376
|
+
# supply roles; any future supply role tagged "provider" auto-joins with
|
|
377
|
+
# ZERO edits here. No `profileSlug` → no per-role hardcoding, no dependency
|
|
378
|
+
# on blockchain-ecosystem (a public installer simply has no web3 roles to
|
|
379
|
+
# match, so nothing leaks).
|
|
369
380
|
- id: list-providers
|
|
370
381
|
type: capability
|
|
371
382
|
position: { x: 0, y: 240 }
|
|
372
383
|
data:
|
|
373
|
-
label: List the
|
|
384
|
+
label: List the provider roster (any roleCategory=provider)
|
|
374
385
|
verbId: entity.query
|
|
375
386
|
inputMapping:
|
|
376
|
-
|
|
387
|
+
roleCategory: provider
|
|
377
388
|
limit: "100"
|
|
378
389
|
|
|
379
390
|
- id: needs-loop
|