@synap-core/workspace-templates 0.12.0 → 0.24.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/README.md +509 -0
- package/automations/bookmark-enrichment.automation.json +321 -0
- package/automations/github-pr-events.automation.json +505 -0
- package/automations/mail-feed.automation.json +86 -0
- package/automations/ship-a-change-checks-green.automation.json +106 -0
- package/automations/ship-a-change-checks-red.automation.json +106 -0
- package/automations/ship-a-change-deployed.automation.json +56 -0
- package/automations/ship-a-change-pr-merged.automation.json +110 -0
- package/automations/ship-a-change-pr-opened.automation.json +104 -0
- package/automations/url-bookmark-capture.automation.json +132 -0
- package/capabilities/agency-skills.capability.json +157 -0
- package/capabilities/apify.capability.json +265 -0
- package/capabilities/apollo.capability.json +120 -0
- package/capabilities/arch-backend.capability.json +97 -0
- package/capabilities/arch-client-intelligence.capability.json +401 -0
- package/capabilities/backblaze-b2.capability.json +205 -0
- package/capabilities/cal-com.capability.json +266 -0
- package/capabilities/canva.capability.json +274 -0
- package/capabilities/claude-code-routines.capability.json +172 -0
- package/capabilities/claude-managed-agents.capability.json +215 -0
- package/capabilities/client-comms-report.capability.json +246 -0
- package/capabilities/content-idea-to-production.capability.json +29 -0
- package/capabilities/datagouv.capability.json +315 -0
- package/capabilities/discord-bot.capability.json +157 -0
- package/capabilities/dropbox.capability.json +281 -0
- package/capabilities/exa.capability.json +109 -0
- package/capabilities/fal-ai.capability.json +83 -0
- package/capabilities/fal-media-lab.capability.json +158 -0
- package/capabilities/fireflies.capability.json +191 -0
- package/capabilities/freellmapi.capability.json +114 -0
- package/capabilities/generic-apikey.capability.json +92 -0
- package/capabilities/github-merge.capability.json +99 -0
- package/capabilities/github-read.capability.json +325 -0
- package/capabilities/governance-tighten-recommender.capability.json +81 -0
- package/capabilities/jev.capability.json +226 -0
- package/capabilities/lead-conversion-enrollment.capability.json +246 -0
- package/capabilities/mail-feed.capability.json +277 -0
- package/capabilities/mailgun.capability.json +52 -0
- package/capabilities/nango-google.capability.json +639 -0
- package/capabilities/notion.capability.json +417 -0
- package/capabilities/omniroute.capability.json +137 -0
- package/capabilities/posthog-analytics.capability.json +258 -0
- package/capabilities/postiz.capability.json +467 -0
- package/capabilities/proactive-digest.capability.json +190 -0
- package/capabilities/proton.capability.json +288 -0
- package/capabilities/replicate-rvm.capability.json +97 -0
- package/capabilities/research-methods.capability.json +57 -0
- package/capabilities/shopify.capability.json +395 -0
- package/capabilities/slack.capability.json +369 -0
- package/capabilities/stripe.capability.json +1496 -0
- package/capabilities/telegram-bridge.capability.json +56 -0
- package/capabilities/treg.capability.json +102 -0
- package/capabilities/unipile-linkedin.capability.json +494 -0
- package/capabilities/vercel.capability.json +424 -0
- package/dist/define-DqoItRiR.d.ts +1370 -0
- package/dist/index.d.ts +174 -900
- package/dist/index.js +26447 -24075
- package/dist/index.js.map +1 -1
- package/dist/wire.d.ts +222 -0
- package/dist/wire.js +77 -0
- package/dist/wire.js.map +1 -0
- package/package.json +27 -4
- package/retired.yaml +136 -0
- package/skills/app-factory-build-slice/SKILL.md +70 -0
- package/skills/app-factory-build-slice.catalog.json +17 -0
- package/skills/app-factory-design-canvas/SKILL.md +96 -0
- package/skills/app-factory-design-canvas.catalog.json +17 -0
- package/skills/app-factory-design-review/SKILL.md +105 -0
- package/skills/app-factory-design-review.catalog.json +18 -0
- package/skills/app-factory-research/SKILL.md +123 -0
- package/skills/app-factory-research.catalog.json +17 -0
- package/skills/bookmark-enrichment/SKILL.md +40 -0
- package/skills/carousel-format/carousel-format.skill.json +18 -0
- package/skills/collage-broll/collage-broll.skill.json +20 -0
- package/skills/content-taste/content-taste.skill.json +18 -0
- package/skills/creative-director/creative-director.skill.json +18 -0
- package/skills/design-critique/SKILL.md +138 -0
- package/skills/design-critique.catalog.json +12 -0
- package/skills/designing-with-real-components/SKILL.md +140 -0
- package/skills/designing-with-real-components.catalog.json +12 -0
- package/skills/edit-loop/edit-loop.skill.json +25 -0
- package/skills/facecam-edit/facecam-edit.skill.json +24 -0
- package/skills/mapping-the-server/SKILL.md +54 -0
- package/skills/math-explainer-manimgx/math-explainer-manimgx.skill.json +20 -0
- package/skills/motion-quality-gates/motion-quality-gates.skill.json +20 -0
- package/skills/open-design-edit/open-design-edit.skill.json +24 -0
- package/skills/reading-client-progress/SKILL.md +65 -0
- package/skills/routing-to-channels/SKILL.md +54 -0
- package/skills/stellar-scf-grants-advisor/ASSOCIATE-CHEATSHEET.md +133 -0
- package/skills/stellar-scf-grants-advisor/ASSOCIATE-ONBOARDING-GUIDE.md +761 -0
- package/skills/stellar-scf-grants-advisor/CLIENT-FAQ.md +181 -0
- package/skills/stellar-scf-grants-advisor/PARTNER-HANDOVER.md +130 -0
- package/skills/stellar-scf-grants-advisor/SKILL.md +102 -0
- package/skills/stellar-scf-grants-advisor/examples/README.md +43 -0
- package/skills/stellar-scf-grants-advisor/examples/chat-summary-templates.md +158 -0
- package/skills/stellar-scf-grants-advisor/examples/corpus-comparable-rows.md +136 -0
- package/skills/stellar-scf-grants-advisor/examples/q10-team-rewrites.md +109 -0
- package/skills/stellar-scf-grants-advisor/examples/q2-description-rewrites.md +88 -0
- package/skills/stellar-scf-grants-advisor/examples/q6-integration-rewrites.md +107 -0
- package/skills/stellar-scf-grants-advisor/examples/tranche-structures.md +176 -0
- package/skills/stellar-scf-grants-advisor/reference/01-methodology.md +96 -0
- package/skills/stellar-scf-grants-advisor/reference/02-scoring-framework.md +199 -0
- package/skills/stellar-scf-grants-advisor/reference/03-rejection-patterns.md +241 -0
- package/skills/stellar-scf-grants-advisor/reference/04-winner-patterns.md +120 -0
- package/skills/stellar-scf-grants-advisor/reference/05-comeback-patterns.md +92 -0
- package/skills/stellar-scf-grants-advisor/reference/06-budget-benchmarks.md +125 -0
- package/skills/stellar-scf-grants-advisor/reference/07-integration-list.md +147 -0
- package/skills/stellar-scf-grants-advisor/reference/08-abstract-fields.md +258 -0
- package/skills/stellar-scf-grants-advisor/reference/09-build-fields.md +259 -0
- package/skills/stellar-scf-grants-advisor/reference/10-corpus-winners.md +135 -0
- package/skills/stellar-scf-grants-advisor/reference/11-corpus-rejects.md +267 -0
- package/skills/stellar-scf-grants-advisor/reference/12-client-communication.md +131 -0
- package/skills/stellar-scf-grants-advisor/reference/13-excel-output-spec.md +199 -0
- package/skills/stellar-scf-grants-advisor/reference/14-worked-examples.md +160 -0
- package/skills/stellar-scf-grants-advisor/templates/abstract-review-builder.py +253 -0
- package/skills/stellar-scf-grants-advisor/templates/build-review-builder.py +213 -0
- package/skills/video-direction/video-direction.skill.json +20 -0
- package/skills/video-style-vocabulary/video-style-vocabulary.skill.json +20 -0
- package/skills/video-toolbox/video-toolbox.skill.json +20 -0
- package/src/__fixtures__/lifecycle-presets.flat-before.json +205 -0
- package/src/agency-os.yaml +122 -0
- package/src/agent-os-skill-slugs.test.ts +65 -0
- package/src/apify-social-reader.test.ts +317 -0
- package/src/base.yaml +85 -39
- package/src/blockchain-ecosystem.yaml +1 -0
- package/src/brand-library.yaml +525 -14
- package/src/builder-dev-fold.template.test.ts +113 -0
- package/src/builder.yaml +779 -91
- package/src/business-model-catalog-description.test.ts +47 -0
- package/src/business-model-grp.template.test.ts +214 -0
- package/src/business-model.yaml +296 -49
- package/src/catalog-safety.test.ts +198 -0
- package/src/catalog.template-items.test.ts +75 -0
- package/src/catalog.ts +66 -2
- package/src/concept-guard.test.ts +28 -8
- package/src/concept-guard.ts +2 -2
- package/src/content-creation.template.test.ts +52 -0
- package/src/content-creation.yaml +192 -0
- package/src/content-os.edit-loop.template.test.ts +210 -0
- package/src/content-os.publish-due-posts.template.test.ts +261 -0
- package/src/content-os.template.test.ts +349 -0
- package/src/content-os.video-takes.template.test.ts +338 -0
- package/src/content-os.work-a-take.template.test.ts +125 -0
- package/src/content-os.yaml +1873 -39
- package/src/content-studio-retired.template.test.ts +82 -0
- package/src/corpus-flow-validity.test.ts +4 -5
- package/src/crm-absorbs-outreach.template.test.ts +117 -0
- package/src/crm-build-plan.template.test.ts +128 -0
- package/src/crm-lists.template.test.ts +96 -0
- package/src/crm-messages-cadence.template.test.ts +246 -0
- package/src/crm-one-send-door.template.test.ts +65 -0
- package/src/crm.yaml +1563 -181
- package/src/decisions-wave.template.test.ts +186 -0
- package/src/define.ts +146 -17
- package/src/design-to-implementation.template.test.ts +94 -0
- package/src/domain-template-type.test.ts +75 -0
- package/src/ecosystem.yaml +27 -66
- package/src/enterprise-os.yaml +10 -134
- package/src/facecam-edit.audio-studio.test.ts +188 -0
- package/src/fal-generate-image.test.ts +200 -0
- package/src/fal-media-lab-pod-files.test.ts +180 -0
- package/src/finance.yaml +254 -1
- package/src/foundation.yaml +78 -38
- package/src/frame-host-modules.d.mts +6 -0
- package/src/frame-host-modules.mjs +58 -0
- package/src/generate.mjs +9 -3
- package/src/github-merge.test.ts +210 -0
- package/src/grants.yaml +46 -17
- package/src/hr.yaml +39 -6
- package/src/index.ts +36 -1
- package/src/intent.test.ts +161 -0
- package/src/internal-runbook.yaml +19 -1
- package/src/jev.test.ts +356 -0
- package/src/legal.yaml +33 -1
- package/src/life-os.yaml +51 -240
- package/src/lifecycle-coverage.test.ts +124 -0
- package/src/lifecycle-declarations.test.ts +252 -0
- package/src/lifecycle-presets.test.ts +468 -0
- package/src/lifecycle-presets.ts +494 -0
- package/src/lifecycle-stuck.test.ts +46 -0
- package/src/lifecycle-waits.parity.test.ts +319 -0
- package/src/marketing.yaml +108 -4
- package/src/meetings-knowledge.yaml +18 -0
- package/src/networking.yaml +23 -2
- package/src/operations.yaml +304 -4
- package/src/pipeline-views.template.test.ts +88 -0
- package/src/playbook-goal-placeholders.test.ts +57 -0
- package/src/postiz-contract.test.ts +134 -0
- package/src/presentation-declarations.test.ts +174 -0
- package/src/process-contract.validate.test.ts +352 -0
- package/src/project-management.yaml +39 -740
- package/src/publish-body.d.mts +29 -0
- package/src/publish-body.mjs +74 -0
- package/src/publish-body.test.ts +74 -0
- package/src/publish-official-lib.test.ts +176 -0
- package/src/replicate-rvm.test.ts +504 -0
- package/src/research-base.yaml +119 -20
- package/src/research-decision.yaml +60 -3
- package/src/reserved-profile-slugs.test.ts +81 -0
- package/src/retired-goal-kinds.test.ts +52 -0
- package/src/role-reference-and-step-domain.test.ts +192 -0
- package/src/seed-obligation.test.ts +1 -6
- package/src/send-message-maps.test.ts +227 -0
- package/src/shared-role-ssot.test.ts +40 -2
- package/src/ship-a-change.template.test.ts +177 -0
- package/src/skills.test.ts +170 -0
- package/src/social.yaml +85 -27
- package/src/space-packages.freshness.test.ts +97 -0
- package/src/space-packages.generated.ts +40 -0
- package/src/space-packages.test.ts +125 -0
- package/src/space-packages.ts +73 -0
- package/src/start.test.ts +188 -0
- package/src/stripe-capability.test.ts +192 -0
- package/src/task-intents.test.ts +293 -0
- package/src/template-documents.test.ts +158 -0
- package/src/templates.ts +20907 -21464
- package/src/types.ts +475 -61
- package/src/unipile-linkedin-find-profile.test.ts +183 -0
- package/src/unipile-publish-post.test.ts +162 -0
- package/src/validate-flow.drift.test.ts +2 -3
- package/src/validate-flow.ts +22 -2
- package/src/validate-onboarding.test.ts +59 -0
- package/src/validate.test.ts +71 -4
- package/src/validate.ts +1416 -7
- package/src/wire-core.ts +231 -0
- package/src/wire.test.ts +159 -0
- package/src/wire.ts +107 -0
- package/views/bookmark-views-pack.view.json +39 -0
- package/views/event-views-pack.view.json +45 -0
- package/views/task-views-pack.view.json +53 -0
- package/src/agent-fleet.yaml +0 -1645
- package/src/business-developer.template.test.ts +0 -351
- package/src/business-developer.yaml +0 -860
- package/src/content-studio.yaml +0 -361
- package/src/dev-dashboard.yaml +0 -990
- package/src/outreach-comms.yaml +0 -486
- package/src/the-arch.yaml +0 -201
package/src/foundation.yaml
CHANGED
|
@@ -7,15 +7,18 @@
|
|
|
7
7
|
meta:
|
|
8
8
|
slug: foundation
|
|
9
9
|
domain: strategy
|
|
10
|
-
name:
|
|
10
|
+
name: Strategy
|
|
11
11
|
description: The strategic DNA — mission, audience, positioning, and principles — that every workspace inherits.
|
|
12
12
|
icon: compass
|
|
13
13
|
color: "#4F46E5"
|
|
14
14
|
tags: [strategy, mission, positioning, audience]
|
|
15
15
|
isPublic: true
|
|
16
|
+
# A LIBRARY: it produces nothing a home counts (homes).
|
|
17
|
+
intent:
|
|
18
|
+
label: Strategy
|
|
16
19
|
|
|
17
20
|
workspace:
|
|
18
|
-
name:
|
|
21
|
+
name: Strategy
|
|
19
22
|
description: The company's strategic DNA — mission, vision, audiences, pains, positioning, and operating principles that downstream workspaces inherit.
|
|
20
23
|
proposalId: foundation-v1
|
|
21
24
|
subtype: foundation
|
|
@@ -246,8 +249,9 @@ profiles:
|
|
|
246
249
|
# every consumer must treat absence as "no commercial axis" rather than an
|
|
247
250
|
# empty required field. Deliberately thin: what it is, whether it is live, how
|
|
248
251
|
# it is priced, and the two identity rows it hangs off. Money AMOUNTS live on
|
|
249
|
-
# the deal/contract that references the offer, not here
|
|
250
|
-
#
|
|
252
|
+
# the deal/contract that references the offer, not here. The offer carries only
|
|
253
|
+
# a "starting at" list price (offer-list-price + currency + billing) so it can
|
|
254
|
+
# be advertised; the deal stays authoritative for what is actually paid.
|
|
251
255
|
# ═══════════════════════════════════════════════════════════════════════════
|
|
252
256
|
- slug: offer
|
|
253
257
|
displayName: Offer
|
|
@@ -283,6 +287,30 @@ profiles:
|
|
|
283
287
|
- usage-based
|
|
284
288
|
- free
|
|
285
289
|
- other
|
|
290
|
+
# "Starting at" list price. The deal/contract stays authoritative for what
|
|
291
|
+
# a buyer actually pays; these three only answer "what do we advertise".
|
|
292
|
+
- slug: offer-list-price
|
|
293
|
+
label: Starting at
|
|
294
|
+
valueType: number
|
|
295
|
+
inputType: number
|
|
296
|
+
placeholder: "List price, before any negotiation"
|
|
297
|
+
- slug: offer-currency
|
|
298
|
+
label: Currency
|
|
299
|
+
valueType: string
|
|
300
|
+
inputType: select
|
|
301
|
+
enumValues:
|
|
302
|
+
- EUR
|
|
303
|
+
- USD
|
|
304
|
+
- GBP
|
|
305
|
+
- CHF
|
|
306
|
+
constraints: { defaultValue: EUR }
|
|
307
|
+
- slug: offer-billing
|
|
308
|
+
label: Billing
|
|
309
|
+
valueType: string
|
|
310
|
+
inputType: select
|
|
311
|
+
enumValues:
|
|
312
|
+
- one-off
|
|
313
|
+
- monthly
|
|
286
314
|
- slug: offer-audience
|
|
287
315
|
label: Audience
|
|
288
316
|
valueType: entity_id
|
|
@@ -324,17 +352,17 @@ profiles:
|
|
|
324
352
|
applicableKinds:
|
|
325
353
|
- company
|
|
326
354
|
- person
|
|
355
|
+
# The ONE lifecycle (work model L1): the relationship base as it stands
|
|
356
|
+
# (prospect · active · paused · former · churned; lifecycle-presets.ts).
|
|
357
|
+
# The compiler writes the options and the select's enumValues.
|
|
358
|
+
lifecycle:
|
|
359
|
+
property: clientStatus
|
|
360
|
+
adopts: relationship
|
|
327
361
|
properties:
|
|
328
362
|
- slug: clientStatus
|
|
329
363
|
label: Status
|
|
330
364
|
valueType: string
|
|
331
365
|
inputType: select
|
|
332
|
-
enumValues:
|
|
333
|
-
- prospect
|
|
334
|
-
- active
|
|
335
|
-
- paused
|
|
336
|
-
- former
|
|
337
|
-
- churned
|
|
338
366
|
- slug: accountManager
|
|
339
367
|
label: Account Manager
|
|
340
368
|
valueType: string
|
|
@@ -379,6 +407,17 @@ profiles:
|
|
|
379
407
|
label: Drive folder ID
|
|
380
408
|
valueType: string
|
|
381
409
|
inputType: text
|
|
410
|
+
# A state is never a role: an ended relationship is `clientStatus:
|
|
411
|
+
# churned` on THIS facet, not a separate `churned` role (retired
|
|
412
|
+
# 2026-10-11, work model W12/W13). Its two fields moved here with it.
|
|
413
|
+
- slug: churnedDate
|
|
414
|
+
label: Churned Date
|
|
415
|
+
valueType: date
|
|
416
|
+
inputType: date
|
|
417
|
+
- slug: churnReason
|
|
418
|
+
label: Churn reason
|
|
419
|
+
valueType: string
|
|
420
|
+
inputType: textarea
|
|
382
421
|
|
|
383
422
|
# ── Partner (referral source + co-delivery collaborator) ──────────────────
|
|
384
423
|
- slug: partner
|
|
@@ -486,43 +525,41 @@ profiles:
|
|
|
486
525
|
applicableKinds:
|
|
487
526
|
- company
|
|
488
527
|
- person
|
|
528
|
+
# The ONE lifecycle (work model L1): every option's category. `converted`
|
|
529
|
+
# is a person's move (CRM build plan A4, 2026-10-22): it happens when a
|
|
530
|
+
# person records a booked meeting, which opens the deal; no agent sets it.
|
|
531
|
+
lifecycle:
|
|
532
|
+
property: leadStage
|
|
533
|
+
options:
|
|
534
|
+
- { key: new, category: open }
|
|
535
|
+
- { key: researching, category: open }
|
|
536
|
+
- { key: outreach, category: open }
|
|
537
|
+
- { key: engaged, category: active }
|
|
538
|
+
- { key: qualified, category: active }
|
|
539
|
+
- { key: nurturing, category: active }
|
|
540
|
+
- { key: converted, category: done, humanOnly: true }
|
|
541
|
+
- { key: disqualified, category: lost, humanOnly: true }
|
|
489
542
|
properties:
|
|
543
|
+
# The lead role's ONE lifecycle (work model W15, 2026-10-11):
|
|
544
|
+
# qualification only. `converted` = a person recorded a booked meeting
|
|
545
|
+
# and a deal was opened (the sale then lives on the deal's commercialStage);
|
|
546
|
+
# `disqualified` = dropped. Business Developer's `lifecycleStage` overlay
|
|
547
|
+
# folded into this; old `prospect` → new, `lost` → disqualified
|
|
548
|
+
# (conversions `w12.lead.*`).
|
|
490
549
|
- slug: leadStage
|
|
491
550
|
label: Stage
|
|
492
551
|
valueType: string
|
|
493
552
|
inputType: select
|
|
494
553
|
enumValues:
|
|
495
|
-
-
|
|
554
|
+
- new
|
|
555
|
+
- researching
|
|
556
|
+
- outreach
|
|
557
|
+
- engaged
|
|
496
558
|
- qualified
|
|
497
559
|
- nurturing
|
|
498
560
|
- converted
|
|
499
|
-
-
|
|
500
|
-
constraints: { defaultValue:
|
|
501
|
-
- slug: notes
|
|
502
|
-
label: Notes
|
|
503
|
-
valueType: string
|
|
504
|
-
inputType: textarea
|
|
505
|
-
|
|
506
|
-
# ── Churned (formerly-active relationship that ended) ──────────────────────
|
|
507
|
-
- slug: churned
|
|
508
|
-
displayName: Churned
|
|
509
|
-
icon: user-x
|
|
510
|
-
color: gray
|
|
511
|
-
description: A relationship (client, partner, or otherwise) that was active and has since ended.
|
|
512
|
-
scope: shared
|
|
513
|
-
profileKind: role
|
|
514
|
-
applicableKinds:
|
|
515
|
-
- company
|
|
516
|
-
- person
|
|
517
|
-
properties:
|
|
518
|
-
- slug: churnedDate
|
|
519
|
-
label: Churned Date
|
|
520
|
-
valueType: date
|
|
521
|
-
inputType: date
|
|
522
|
-
- slug: churnReason
|
|
523
|
-
label: Reason
|
|
524
|
-
valueType: string
|
|
525
|
-
inputType: textarea
|
|
561
|
+
- disqualified
|
|
562
|
+
constraints: { defaultValue: new }
|
|
526
563
|
- slug: notes
|
|
527
564
|
label: Notes
|
|
528
565
|
valueType: string
|
|
@@ -691,10 +728,12 @@ onboarding:
|
|
|
691
728
|
alternatives — in one breath, with no fluff.
|
|
692
729
|
collect:
|
|
693
730
|
- profileSlug: mission
|
|
731
|
+
min: 1
|
|
694
732
|
what: The one mission that anchors the company — what we serve, what we change, why now.
|
|
695
733
|
cardinality: one
|
|
696
734
|
keyFields: [statement, time-horizon]
|
|
697
735
|
- profileSlug: audience
|
|
736
|
+
min: 1
|
|
698
737
|
what: The ideal customer profiles the company is built for.
|
|
699
738
|
cardinality: few
|
|
700
739
|
keyFields: [segment, jobs-to-be-done]
|
|
@@ -703,6 +742,7 @@ onboarding:
|
|
|
703
742
|
cardinality: several
|
|
704
743
|
keyFields: [description, severity]
|
|
705
744
|
- profileSlug: positioning
|
|
745
|
+
min: 1
|
|
706
746
|
what: The market category, who we stand against, and why we win.
|
|
707
747
|
cardinality: one
|
|
708
748
|
keyFields: [category, against, because]
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** Types for `frame-host-modules.mjs` — the modules a space package's frame gets from its host. */
|
|
2
|
+
export declare const FRAME_HOST_MODULES: readonly string[];
|
|
3
|
+
export declare const FRAME_HOST_MODULE_RE: RegExp;
|
|
4
|
+
export declare function hostRequiresIn(code: string): string[];
|
|
5
|
+
export declare const FRAME_KIT_MODULE: string;
|
|
6
|
+
export declare function kitImportsIn(code: string): string[];
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The modules a space package's frame gets from its HOST, never from its
|
|
3
|
+
* bundle (`@synap-core/web/cell-runtime` frame-import-map.ts): one React, its
|
|
4
|
+
* JSX runtimes, one react-dom, the view SDK, and the shared kit
|
|
5
|
+
* (`@synap/kit`, built from `packages/web/src/space-packages/kit.ts`). Shared
|
|
6
|
+
* by the build (`scripts/build-space-packages.mjs`, which keeps them external
|
|
7
|
+
* and builds the React modules and the kit as the host's frame modules) and
|
|
8
|
+
* the freshness test (which re-checks the committed bundle).
|
|
9
|
+
*/
|
|
10
|
+
export const FRAME_HOST_MODULES = [
|
|
11
|
+
'react',
|
|
12
|
+
'react/jsx-runtime',
|
|
13
|
+
'react/jsx-dev-runtime',
|
|
14
|
+
'react-dom',
|
|
15
|
+
'react-dom/client',
|
|
16
|
+
'@synap/view-sdk',
|
|
17
|
+
'@synap/kit',
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
/** The kit module's specifier (a package reaches the shared layers only through it). */
|
|
21
|
+
export const FRAME_KIT_MODULE = '@synap/kit';
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Every name a bundle imports from the kit: `import{A as b,C}from"@synap/kit"`
|
|
25
|
+
* → `['A', 'C']`. A namespace or default import of the kit is returned as
|
|
26
|
+
* `*` / `default` so the build can refuse it (its names cannot be checked).
|
|
27
|
+
*/
|
|
28
|
+
export function kitImportsIn(code) {
|
|
29
|
+
const names = new Set();
|
|
30
|
+
for (const m of code.matchAll(/import\s*([^;'"]*?)\s*from\s*["']@synap\/kit["']/g)) {
|
|
31
|
+
const clause = m[1];
|
|
32
|
+
const braces = /\{([^}]*)\}/.exec(clause);
|
|
33
|
+
for (const part of braces ? braces[1].split(',') : []) {
|
|
34
|
+
const name = part.trim().split(/\s+as\s+/)[0];
|
|
35
|
+
if (name) names.add(name);
|
|
36
|
+
}
|
|
37
|
+
const head = clause.replace(/\{[^}]*\}/, '').replace(/,/g, ' ').trim();
|
|
38
|
+
if (/^\*\s*as\s+[\w$]+$/.test(head)) names.add('*');
|
|
39
|
+
else if (/^[\w$]+$/.test(head)) names.add('default');
|
|
40
|
+
}
|
|
41
|
+
return [...names].sort();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const NAMES = FRAME_HOST_MODULES.map((m) => m.replace(/[/.]/g, '\\$&')).join('|');
|
|
45
|
+
|
|
46
|
+
/** Matches a host module name exactly (an esbuild `onResolve` filter). */
|
|
47
|
+
export const FRAME_HOST_MODULE_RE = new RegExp(`^(${NAMES})$`);
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Every `require()` of a frame host module left in a bundle: a call to
|
|
51
|
+
* esbuild's `__require` shim, e.g. `jI("react")`. Each one throws in the frame
|
|
52
|
+
* ("Dynamic require of "react" is not supported"), so the module never links.
|
|
53
|
+
* `import("react")` and method calls (`x.f("react")`) are not requires.
|
|
54
|
+
*/
|
|
55
|
+
export function hostRequiresIn(code) {
|
|
56
|
+
const call = new RegExp(`(?<![\\w$.])(?!import\\()[A-Za-z_$][\\w$]*\\(\\s*["'](${NAMES})["']\\s*\\)`, 'g');
|
|
57
|
+
return [...code.matchAll(call)].map((m) => m[0]);
|
|
58
|
+
}
|
package/src/generate.mjs
CHANGED
|
@@ -23,20 +23,26 @@ import { readFileSync, readdirSync } from 'fs';
|
|
|
23
23
|
import { dirname, join } from 'path';
|
|
24
24
|
import { fileURLToPath } from 'url';
|
|
25
25
|
import yaml from 'js-yaml';
|
|
26
|
+
// `.ts` on purpose: this file runs under Node's type stripping (build.mjs) and
|
|
27
|
+
// vitest; lifecycle-presets.ts has only `import type`.
|
|
28
|
+
import { resolveLifecyclePresets } from './lifecycle-presets.ts';
|
|
26
29
|
|
|
27
30
|
export const SRC_DIR = dirname(fileURLToPath(import.meta.url));
|
|
28
31
|
export const TEMPLATES_FILE = join(SRC_DIR, 'templates.ts');
|
|
29
32
|
|
|
30
33
|
/**
|
|
31
|
-
* Read + parse every `src/*.yaml`, in the same (readdir) order build.mjs used
|
|
32
|
-
*
|
|
34
|
+
* Read + parse every `src/*.yaml`, in the same (readdir) order build.mjs used,
|
|
35
|
+
* and compile its lifecycle presets (`adopts:` → flat options, generated
|
|
36
|
+
* `enumValues`; see lifecycle-presets.ts). No validation here — validation
|
|
37
|
+
* stays in build.mjs, which is the writer; an overlay that cannot be resolved
|
|
38
|
+
* is left as authored and `validateTemplate` reports it.
|
|
33
39
|
*/
|
|
34
40
|
export function readYamlTemplates() {
|
|
35
41
|
return readdirSync(SRC_DIR)
|
|
36
42
|
.filter((f) => f.endsWith('.yaml'))
|
|
37
43
|
.map((file) => {
|
|
38
44
|
const parsed = yaml.load(readFileSync(join(SRC_DIR, file), 'utf-8'));
|
|
39
|
-
return { file, slug: parsed?.meta?.slug, data: parsed };
|
|
45
|
+
return { file, slug: parsed?.meta?.slug, data: resolveLifecyclePresets(parsed).template };
|
|
40
46
|
});
|
|
41
47
|
}
|
|
42
48
|
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* github-merge: a ship step as a governed tool verb (decision 4d6d7922: the pod
|
|
3
|
+
* holds the ship credential, an approval is a typed action pinned to a head,
|
|
4
|
+
* GitHub's own checks gate it).
|
|
5
|
+
*
|
|
6
|
+
* SEAM: the verb's REAL `code` body, run the way the pod's executor wraps it
|
|
7
|
+
* (`async (args, context) => { <code> }` with `callProvider` in scope), after
|
|
8
|
+
* the install-time values are put in its `{{repos}}` / `{{requiredChecks}}`
|
|
9
|
+
* the way the install templating does, against a recorded `callProvider`.
|
|
10
|
+
*
|
|
11
|
+
* NOT covered: the pod's own path (the approval, the vault, the sandbox) and
|
|
12
|
+
* GitHub itself (not exercised against a live repository: the proof merge in
|
|
13
|
+
* session 2d2a3473 does that).
|
|
14
|
+
*/
|
|
15
|
+
import { readFileSync } from "node:fs";
|
|
16
|
+
import { join } from "node:path";
|
|
17
|
+
import { describe, expect, it } from "vitest";
|
|
18
|
+
|
|
19
|
+
const capability = JSON.parse(
|
|
20
|
+
readFileSync(join(import.meta.dirname, "..", "capabilities", "github-merge.capability.json"), "utf8"),
|
|
21
|
+
) as { spec: Record<string, any> };
|
|
22
|
+
const verb = capability.spec.skills.find((s: { name: string }) => s.name === "github_merge_pr");
|
|
23
|
+
|
|
24
|
+
const AsyncFunction = Object.getPrototypeOf(async () => {}).constructor as new (
|
|
25
|
+
...args: string[]
|
|
26
|
+
) => (...a: unknown[]) => Promise<unknown>;
|
|
27
|
+
|
|
28
|
+
const SHA = "a524c103d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6";
|
|
29
|
+
const ARGS = { repo: "Synap-core/app-bases", pr: 12, expectedHeadSha: SHA, base: "main", method: "squash" };
|
|
30
|
+
const PR_PATH = "/repos/Synap-core/app-bases/pulls/12";
|
|
31
|
+
|
|
32
|
+
type Reply = { status: number; body?: unknown };
|
|
33
|
+
type Call = { tool: string; method: string; path: string; body?: unknown };
|
|
34
|
+
|
|
35
|
+
function github(over: Partial<Record<string, Reply>> = {}): Record<string, Reply> {
|
|
36
|
+
return {
|
|
37
|
+
[`GET ${PR_PATH}`]: {
|
|
38
|
+
status: 200,
|
|
39
|
+
body: { state: "open", merged: false, draft: false, head: { sha: SHA }, base: { ref: "main" }, html_url: "https://github.com/Synap-core/app-bases/pull/12" },
|
|
40
|
+
},
|
|
41
|
+
[`GET /repos/Synap-core/app-bases/commits/${SHA}/check-runs?per_page=100`]: { status: 200, body: { check_runs: [] } },
|
|
42
|
+
[`GET /repos/Synap-core/app-bases/commits/${SHA}/status`]: {
|
|
43
|
+
status: 200,
|
|
44
|
+
body: { statuses: [{ context: "synap/devbox", state: "success" }] },
|
|
45
|
+
},
|
|
46
|
+
[`PUT ${PR_PATH}/merge`]: { status: 200, body: { merged: true, sha: "f00dfeed00000000000000000000000000000000" } },
|
|
47
|
+
...over,
|
|
48
|
+
} as Record<string, Reply>;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async function run(
|
|
52
|
+
args: Record<string, unknown>,
|
|
53
|
+
replies: Record<string, Reply> = github(),
|
|
54
|
+
install: { repos?: string; requiredChecks?: string } = {},
|
|
55
|
+
) {
|
|
56
|
+
const calls: Call[] = [];
|
|
57
|
+
const callProvider = async (tool: string, method: string, path: string, body?: unknown) => {
|
|
58
|
+
calls.push({ tool, method, path, ...(body === undefined ? {} : { body }) });
|
|
59
|
+
const r = replies[`${method} ${path}`];
|
|
60
|
+
if (!r) throw new Error(`unexpected call ${method} ${path}`);
|
|
61
|
+
return r;
|
|
62
|
+
};
|
|
63
|
+
const code = String(verb.code)
|
|
64
|
+
.replaceAll("{{repos}}", install.repos ?? "Synap-core/app-bases, https://github.com/Synap-core/browser")
|
|
65
|
+
.replaceAll("{{requiredChecks}}", install.requiredChecks ?? "synap/devbox");
|
|
66
|
+
const body = new AsyncFunction("args", "context", "callProvider", "propose", code);
|
|
67
|
+
try {
|
|
68
|
+
return { result: await body(args, {}, callProvider, {}), calls, error: null as Error | null };
|
|
69
|
+
} catch (error) {
|
|
70
|
+
return { result: null, calls, error: error as Error };
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
const merged = (calls: Call[]) => calls.some((c) => c.method === "PUT");
|
|
74
|
+
|
|
75
|
+
describe("github-merge — the manifest", () => {
|
|
76
|
+
it("one write verb on its own tool, its own token, never pod-wide, separate from github-read", () => {
|
|
77
|
+
expect(capability.spec.key).toBe("github-merge");
|
|
78
|
+
expect(capability.spec.skills.map((s: { name: string }) => s.name)).toEqual(["github_merge_pr"]);
|
|
79
|
+
expect(verb.kind).toBe("code");
|
|
80
|
+
expect(verb.requires).toEqual(["github_merge_api"]);
|
|
81
|
+
expect(verb.metadata).toEqual({ verbType: "write" });
|
|
82
|
+
expect(verb.parameters).toEqual({ repo: "string", pr: "number", expectedHeadSha: "string", base: "string", method: "string?" });
|
|
83
|
+
expect(capability.spec.tools.map((t: { name: string; credentialRef: string }) => [t.name, t.credentialRef])).toEqual([
|
|
84
|
+
["github_merge_api", "githubMergeTokenSecret"],
|
|
85
|
+
]);
|
|
86
|
+
expect(capability.spec.vault.filter((v: { podWide?: boolean }) => v.podWide)).toEqual([]);
|
|
87
|
+
// github-read keeps its promise: it holds no write verb and no write tool.
|
|
88
|
+
const read = JSON.parse(readFileSync(join(import.meta.dirname, "..", "capabilities", "github-read.capability.json"), "utf8")).spec;
|
|
89
|
+
expect(read.skills.map((s: { name: string }) => s.name)).not.toContain("github_merge_pr");
|
|
90
|
+
// Every call goes to its own tool.
|
|
91
|
+
const tools = [...String(verb.code).matchAll(/callProvider\(\s*'([^']+)'/g)].map((m) => m[1]);
|
|
92
|
+
expect(tools.length).toBeGreaterThanOrEqual(2);
|
|
93
|
+
expect(new Set(tools)).toEqual(new Set(["github_merge_api"]));
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
describe("github_merge_pr — merges only the approved head, with its checks green", () => {
|
|
98
|
+
it("reads the PR and its checks, then merges pinned to the approved sha", async () => {
|
|
99
|
+
const { result, calls, error } = await run(ARGS);
|
|
100
|
+
expect(error).toBeNull();
|
|
101
|
+
expect(calls.map((c) => `${c.method} ${c.path}`)).toEqual([
|
|
102
|
+
`GET ${PR_PATH}`,
|
|
103
|
+
`GET /repos/Synap-core/app-bases/commits/${SHA}/check-runs?per_page=100`,
|
|
104
|
+
`GET /repos/Synap-core/app-bases/commits/${SHA}/status`,
|
|
105
|
+
`PUT ${PR_PATH}/merge`,
|
|
106
|
+
]);
|
|
107
|
+
expect(calls.at(-1)!.body).toEqual({ sha: SHA, merge_method: "squash" });
|
|
108
|
+
expect(result).toEqual({
|
|
109
|
+
merged: true,
|
|
110
|
+
mergeCommitSha: "f00dfeed00000000000000000000000000000000",
|
|
111
|
+
url: "https://github.com/Synap-core/app-bases/pull/12",
|
|
112
|
+
detail: "Merged Synap-core/app-bases#12 at a524c103 into main as f00dfeed.",
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it("a repository not in the install's list is refused before anything is read", async () => {
|
|
117
|
+
const { calls, error } = await run({ ...ARGS, repo: "Synap-core/backend" });
|
|
118
|
+
expect(error?.message).toMatch(/Synap-core\/backend is not in this pod's merge list/);
|
|
119
|
+
expect(calls).toEqual([]);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it("a head that moved since the approval is refused, never merged", async () => {
|
|
123
|
+
const replies = github({
|
|
124
|
+
[`GET ${PR_PATH}`]: { status: 200, body: { state: "open", head: { sha: "b".repeat(40) }, base: { ref: "main" } } },
|
|
125
|
+
});
|
|
126
|
+
const { calls, error } = await run(ARGS, replies);
|
|
127
|
+
expect(error?.message).toMatch(/moved since it was approved: approved a524c103, head is now bbbbbbbb/);
|
|
128
|
+
expect(merged(calls)).toBe(false);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it("a required check missing, pending or failed refuses; so does any failed check", async () => {
|
|
132
|
+
const statusPath = `GET /repos/Synap-core/app-bases/commits/${SHA}/status`;
|
|
133
|
+
const runsPath = `GET /repos/Synap-core/app-bases/commits/${SHA}/check-runs?per_page=100`;
|
|
134
|
+
const cases: Array<[Record<string, Reply>, RegExp]> = [
|
|
135
|
+
[{ [statusPath]: { status: 200, body: { statuses: [] } } }, /missing: synap\/devbox/],
|
|
136
|
+
[{ [statusPath]: { status: 200, body: { statuses: [{ context: "synap/devbox", state: "pending" }] } } }, /not green: synap\/devbox \(pending\)/],
|
|
137
|
+
[{ [statusPath]: { status: 200, body: { statuses: [{ context: "synap/devbox", state: "failure" }] } } }, /not green: synap\/devbox \(failed\)/],
|
|
138
|
+
[
|
|
139
|
+
{ [runsPath]: { status: 200, body: { check_runs: [{ id: 1, name: "lint", status: "completed", conclusion: "failure" }] } } },
|
|
140
|
+
/failed: lint/,
|
|
141
|
+
],
|
|
142
|
+
];
|
|
143
|
+
for (const [over, reason] of cases) {
|
|
144
|
+
const { calls, error } = await run(ARGS, github(over));
|
|
145
|
+
expect(error?.message).toMatch(reason);
|
|
146
|
+
expect(merged(calls)).toBe(false);
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it("a check that failed then passed on a re-run reads by its latest run", async () => {
|
|
151
|
+
const runsPath = `GET /repos/Synap-core/app-bases/commits/${SHA}/check-runs?per_page=100`;
|
|
152
|
+
const replies = github({
|
|
153
|
+
[runsPath]: {
|
|
154
|
+
status: 200,
|
|
155
|
+
body: {
|
|
156
|
+
check_runs: [
|
|
157
|
+
{ id: 9, name: "lint", status: "completed", conclusion: "success" },
|
|
158
|
+
{ id: 3, name: "lint", status: "completed", conclusion: "failure" },
|
|
159
|
+
],
|
|
160
|
+
},
|
|
161
|
+
},
|
|
162
|
+
});
|
|
163
|
+
const { error, calls } = await run(ARGS, replies);
|
|
164
|
+
expect(error).toBeNull();
|
|
165
|
+
expect(merged(calls)).toBe(true);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it("no required check configured refuses: nothing would vouch for the head", async () => {
|
|
169
|
+
const { calls, error } = await run(ARGS, github(), { requiredChecks: " , " });
|
|
170
|
+
expect(error?.message).toMatch(/no required checks are configured/);
|
|
171
|
+
expect(calls).toEqual([]);
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
it("a PR that is merged, closed, a draft or targets another base is refused", async () => {
|
|
175
|
+
const pr = (body: Record<string, unknown>) =>
|
|
176
|
+
github({ [`GET ${PR_PATH}`]: { status: 200, body: { state: "open", head: { sha: SHA }, base: { ref: "main" }, ...body } } });
|
|
177
|
+
for (const [body, reason] of [
|
|
178
|
+
[{ merged: true }, /already merged/],
|
|
179
|
+
[{ state: "closed" }, /is closed/],
|
|
180
|
+
[{ draft: true }, /is a draft/],
|
|
181
|
+
[{ base: { ref: "release" } }, /targets release, not main/],
|
|
182
|
+
] as Array<[Record<string, unknown>, RegExp]>) {
|
|
183
|
+
const { calls, error } = await run(ARGS, pr(body));
|
|
184
|
+
expect(error?.message).toMatch(reason);
|
|
185
|
+
expect(merged(calls)).toBe(false);
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it("arguments that cannot pin the act are refused before any call", async () => {
|
|
190
|
+
for (const bad of [
|
|
191
|
+
{ ...ARGS, expectedHeadSha: "a524c103" },
|
|
192
|
+
{ ...ARGS, pr: 0 },
|
|
193
|
+
{ ...ARGS, base: "" },
|
|
194
|
+
{ ...ARGS, method: "force" },
|
|
195
|
+
{ ...ARGS, repo: "app-bases" },
|
|
196
|
+
]) {
|
|
197
|
+
const { calls, error } = await run(bad);
|
|
198
|
+
expect(error, JSON.stringify(bad)).not.toBeNull();
|
|
199
|
+
expect(calls).toEqual([]);
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it("a failed read or a merge GitHub refuses throws: nothing reads as merged", async () => {
|
|
204
|
+
const unread = await run(ARGS, github({ [`GET ${PR_PATH}`]: { status: 404, body: {} } }));
|
|
205
|
+
expect(unread.error?.message).toMatch(/HTTP 404/);
|
|
206
|
+
expect(merged(unread.calls)).toBe(false);
|
|
207
|
+
const refused = await run(ARGS, github({ [`PUT ${PR_PATH}/merge`]: { status: 409, body: { message: "Head branch was modified" } } }));
|
|
208
|
+
expect(refused.error?.message).toMatch(/refused to merge .*HTTP 409: Head branch was modified/);
|
|
209
|
+
});
|
|
210
|
+
});
|
package/src/grants.yaml
CHANGED
|
@@ -67,6 +67,14 @@ profiles:
|
|
|
67
67
|
color: blue
|
|
68
68
|
description: A single grant application — one project, one round, one outcome. The pipeline card and analysis unit.
|
|
69
69
|
scope: WORKSPACE
|
|
70
|
+
# The ONE lifecycle (work model L1): every option's category.
|
|
71
|
+
lifecycle:
|
|
72
|
+
property: stage
|
|
73
|
+
options:
|
|
74
|
+
- { key: scope, category: open }
|
|
75
|
+
- { key: abstract, category: active }
|
|
76
|
+
- { key: build, category: active }
|
|
77
|
+
- { key: awaiting-results, category: active }
|
|
70
78
|
properties:
|
|
71
79
|
# Pipeline + workflow state
|
|
72
80
|
- slug: stage
|
|
@@ -693,6 +701,15 @@ profiles:
|
|
|
693
701
|
applicableKinds:
|
|
694
702
|
- person
|
|
695
703
|
- company
|
|
704
|
+
# The ONE lifecycle (work model L1): every option's category.
|
|
705
|
+
lifecycle:
|
|
706
|
+
property: clientStatus
|
|
707
|
+
options:
|
|
708
|
+
- { key: prospect, category: open }
|
|
709
|
+
- { key: active, category: active }
|
|
710
|
+
- { key: paused, category: paused }
|
|
711
|
+
- { key: former, category: done }
|
|
712
|
+
- { key: churned, category: lost }
|
|
696
713
|
properties:
|
|
697
714
|
- slug: clientStatus
|
|
698
715
|
label: Status
|
|
@@ -844,16 +861,12 @@ profiles:
|
|
|
844
861
|
|
|
845
862
|
views:
|
|
846
863
|
- name: Pipeline
|
|
847
|
-
|
|
864
|
+
# The ONE board (work model L3, D3): columns ARE the submission's declared
|
|
865
|
+
# lifecycle (`stage`), moves go through the one mover.
|
|
866
|
+
type: pipeline
|
|
848
867
|
scopeProfileSlug: grant_submission
|
|
849
868
|
config:
|
|
850
|
-
|
|
851
|
-
cardFields:
|
|
852
|
-
- summary
|
|
853
|
-
- requestedAmount
|
|
854
|
-
- awardedAmount
|
|
855
|
-
- outcome
|
|
856
|
-
- status
|
|
869
|
+
profileSlug: grant_submission
|
|
857
870
|
- name: Submissions
|
|
858
871
|
type: table
|
|
859
872
|
scopeProfileSlug: grant_submission
|
|
@@ -983,8 +996,8 @@ entityLinks:
|
|
|
983
996
|
# trancheModel, nextDeadline) are READ from the `grantProgram` entity at runtime,
|
|
984
997
|
# NOT hardcoded per chain — so one playbook serves Stellar SCF, Optimism RetroPGF,
|
|
985
998
|
# etc. Only a genuinely different-SHAPED program later earns its own override
|
|
986
|
-
# playbook (base + data-driven-override decision). Generalizes the
|
|
987
|
-
# advisor
|
|
999
|
+
# playbook (base + data-driven-override decision). Generalizes the retired
|
|
1000
|
+
# Stellar staged advisor capability into a template (it was removed, D5). Bound to `contract` (the
|
|
988
1001
|
# engagement, per decision — NOT company); targets a `grant-program` param — the
|
|
989
1002
|
# 3-layer spine (contract → grant-program → round → grant_submission) made runnable.
|
|
990
1003
|
playbooks:
|
|
@@ -1033,13 +1046,16 @@ playbooks:
|
|
|
1033
1046
|
# `awarded` stays `started` because delivery follows an award — a rejected
|
|
1034
1047
|
# pursuit's terminality lives on the submission's own `outcome` field, not
|
|
1035
1048
|
# here.
|
|
1049
|
+
# Every step is worked where the grant submissions live: grants composes
|
|
1050
|
+
# onto Operations, so the DOMAIN is `operations` (the workspace template
|
|
1051
|
+
# slug the workspace carries — an overlay's own slug is never a domain).
|
|
1036
1052
|
stages:
|
|
1037
|
-
- { key: identify, name: Identify, category: planned, 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." }
|
|
1038
|
-
- { key: draft, name: Draft, category: started, 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." }
|
|
1039
|
-
- { key: submit, name: Submit, category: started, description: "Finalize against the program deadline and submit the application.", goal: "Application submitted on time; submission stage = awaiting-results." }
|
|
1040
|
-
- { key: awaiting, name: Awaiting, category: paused, 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)." }
|
|
1041
|
-
- { key: awarded, name: Awarded, category: started, description: "Record the funder's decision and the awarded amount.", goal: "Submission outcome set (awarded / rejected / partial) with the awarded amount." }
|
|
1042
|
-
- { key: delivering, name: Delivering, category: started, description: "Work the program's tranche model — ship and record each tranche.", goal: "Tranche deliverables filled and trancheCompletion updated as milestones ship." }
|
|
1053
|
+
- { key: identify, name: Identify, category: planned, domain: operations, 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." }
|
|
1054
|
+
- { key: draft, name: Draft, category: started, domain: operations, 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." }
|
|
1055
|
+
- { key: submit, name: Submit, category: started, domain: operations, description: "Finalize against the program deadline and submit the application.", goal: "Application submitted on time; submission stage = awaiting-results." }
|
|
1056
|
+
- { key: awaiting, name: Awaiting, category: paused, domain: operations, 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)." }
|
|
1057
|
+
- { key: awarded, name: Awarded, category: started, domain: operations, description: "Record the funder's decision and the awarded amount.", goal: "Submission outcome set (awarded / rejected / partial) with the awarded amount." }
|
|
1058
|
+
- { key: delivering, name: Delivering, category: started, domain: operations, description: "Work the program's tranche model — ship and record each tranche.", goal: "Tranche deliverables filled and trancheCompletion updated as milestones ship." }
|
|
1043
1059
|
executor: is-agent
|
|
1044
1060
|
grants:
|
|
1045
1061
|
- entity.create
|
|
@@ -1285,10 +1301,23 @@ bento:
|
|
|
1285
1301
|
w: 4
|
|
1286
1302
|
h: 6
|
|
1287
1303
|
integrations:
|
|
1288
|
-
- templateKey: stellar-grant-client
|
|
1289
1304
|
- templateKey: agency-skills
|
|
1290
1305
|
- templateKey: nango-google
|
|
1291
1306
|
- templateKey: discord-bot
|
|
1307
|
+
|
|
1308
|
+
# ── taskIntents — WHAT THIS WORKSPACE NEEDS TO FUNCTION ───────────────────
|
|
1309
|
+
# Closed vocabulary: `@synap-core/types/capability-intents` (pod migrations
|
|
1310
|
+
# 0283/0284). DERIVED from the declared intents of this template's own
|
|
1311
|
+
# `integrations` capability packs — not hand-guessed. Checked at authoring
|
|
1312
|
+
# time by `validateTemplate` (`task-intent-unknown`) and by
|
|
1313
|
+
# `src/task-intents.test.ts`.
|
|
1314
|
+
taskIntents:
|
|
1315
|
+
- send_message
|
|
1316
|
+
- search_external
|
|
1317
|
+
- list_records
|
|
1318
|
+
- schedule_event
|
|
1319
|
+
- manage_file
|
|
1320
|
+
- fetch_record
|
|
1292
1321
|
onboarding:
|
|
1293
1322
|
goal: Capture the agency's live grant pipeline — the funding rounds and their deadlines, the projects being advised, and the open submissions in flight — so the workspace mirrors the real book of work, not a generic template.
|
|
1294
1323
|
framing: "Act as a seasoned blockchain-grants advisor who has shepherded dozens of applications through programs like SCF, Optimism RetroPGF, and Arbitrum. Speak to how a submission actually wins: a crisp problem, a defensible reason the chain matters, evidence of traction, and milestones a reviewer can grade. Translate the founder's words into rounds, projects, and submissions on the pipeline."
|