@synap-core/workspace-templates 0.30.0 → 0.31.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 +12 -4
- package/dist/{define-DqoItRiR.d.ts → define-DHDi8C33.d.ts} +19 -18
- package/dist/index.d.ts +2 -2
- package/dist/index.js +428 -239
- package/dist/index.js.map +1 -1
- package/dist/wire.d.ts +1 -1
- package/package.json +1 -1
- package/src/brand-library.yaml +0 -6
- package/src/builder-dev-fold.template.test.ts +2 -3
- package/src/builder-dev-playbooks.template.test.ts +7 -5
- package/src/builder.yaml +60 -42
- package/src/business-model.yaml +20 -1
- package/src/civic-france.yaml +9 -0
- package/src/content-creation.yaml +1 -1
- package/src/content-os.yaml +22 -18
- package/src/crm-absorbs-outreach.template.test.ts +1 -1
- package/src/crm-build-plan.template.test.ts +1 -1
- package/src/crm.yaml +5 -14
- package/src/decisions-wave.template.test.ts +7 -5
- package/src/define.primary-surface.test.ts +9 -0
- package/src/define.ts +4 -1
- package/src/finance.yaml +10 -6
- package/src/foundation.yaml +1 -2
- package/src/hr.yaml +3 -2
- package/src/intent.test.ts +43 -2
- package/src/internal-runbook.yaml +0 -1
- package/src/kanban-grouping.test.ts +72 -0
- package/src/legal.yaml +4 -4
- package/src/lifecycle-coverage.test.ts +7 -64
- package/src/lifecycle-declarations.test.ts +70 -74
- package/src/marketing.yaml +4 -3
- package/src/meetings-knowledge.yaml +1 -2
- package/src/operations.yaml +13 -9
- package/src/process-contract.validate.test.ts +40 -55
- package/src/project-management.yaml +2 -2
- package/src/publish-official-lib.test.ts +3 -2
- package/src/research-base.yaml +12 -5
- package/src/research-decision.yaml +18 -2
- package/src/ship-a-change.template.test.ts +3 -2
- package/src/social.yaml +4 -1
- package/src/start.test.ts +22 -0
- package/src/templates.ts +324 -183
- package/src/types.ts +28 -18
- package/src/validate.ts +143 -77
package/dist/index.js
CHANGED
|
@@ -61,6 +61,9 @@ var PACKAGE_DEPENDENCY_KINDS = [
|
|
|
61
61
|
/** @deprecated Legacy alias for `workflow` (CP 0049). Still accepted on the wire. */
|
|
62
62
|
"automation"
|
|
63
63
|
];
|
|
64
|
+
function startProfileOf(meta) {
|
|
65
|
+
return meta.start?.profile ?? meta.intent?.primary;
|
|
66
|
+
}
|
|
64
67
|
var START_VIEWS_MAX = 3;
|
|
65
68
|
var START_EXAMPLE_RANGE = [4, 6];
|
|
66
69
|
var START_STARTERS_RANGE = [3, 6];
|
|
@@ -3350,12 +3353,8 @@ var DATA = {
|
|
|
3350
3353
|
"description": "Turn a request for a logo, image, icon or template into a draft built from the approved brand, and approve it with a person before anything uses it.",
|
|
3351
3354
|
"goalTemplate": "Make this brand asset ({asset}); each stage moves its status. DRAFT: read the request and the approved brand only \u2014 approved colours, fonts, references and the active token set; never a draft \u2014 and load the creative-director skill to pick the recipe. Make the draft (or up to three variants, each its own asset derived from this one) and record the generator, prompt and parameters on it. APPROVE: a person approves one draft as final; with variants, the approval asks them to `choose` one, your recommendation first, and that choice is the approval. Until then nothing may use it. Deprecating an asset is a person's call.",
|
|
3352
3355
|
"subjectProfile": {
|
|
3353
|
-
"profileSlug": "brand-asset"
|
|
3354
|
-
"statusProperty": "asset-status"
|
|
3356
|
+
"profileSlug": "brand-asset"
|
|
3355
3357
|
},
|
|
3356
|
-
"humanOnlyStatuses": [
|
|
3357
|
-
"deprecated"
|
|
3358
|
-
],
|
|
3359
3358
|
"params": [
|
|
3360
3359
|
{
|
|
3361
3360
|
"name": "asset",
|
|
@@ -3420,12 +3419,8 @@ var DATA = {
|
|
|
3420
3419
|
"description": "Draft or revise the brand voice guide from what the brand already publishes, and approve it with a person.",
|
|
3421
3420
|
"goalTemplate": "Write this brand voice guide ({guide}); each stage moves its status. DRAFT: read the brand identity, the approved assets and the last posts and emails the brand published; write the voice in a few traits, each with a do and a don't taken from real examples, and the words to use and avoid. APPROVE: a person approves the guide; then it is what every draft is checked against. Retiring a guide is a person's call.",
|
|
3422
3421
|
"subjectProfile": {
|
|
3423
|
-
"profileSlug": "brand-voice-guide"
|
|
3424
|
-
"statusProperty": "voice-status"
|
|
3422
|
+
"profileSlug": "brand-voice-guide"
|
|
3425
3423
|
},
|
|
3426
|
-
"humanOnlyStatuses": [
|
|
3427
|
-
"deprecated"
|
|
3428
|
-
],
|
|
3429
3424
|
"params": [
|
|
3430
3425
|
{
|
|
3431
3426
|
"name": "guide",
|
|
@@ -3473,12 +3468,8 @@ var DATA = {
|
|
|
3473
3468
|
"description": "Distil approved references into a content guideline (base, format or the frame for one goal), and approve it with a person.",
|
|
3474
3469
|
"goalTemplate": "Write this content guideline ({guideline}); each stage moves its status. DRAFT: read the brand identity, the approved voice guide and the approved references; load the content-taste skill. Write what to take and what to avoid as rules a maker can follow, link the guideline to every reference it was distilled from, and fill the summary. For a frame guideline, set the goal and give the structure in order. APPROVE: a person approves the guideline; then content is made and checked against it. Retiring a guideline is a person's call.",
|
|
3475
3470
|
"subjectProfile": {
|
|
3476
|
-
"profileSlug": "content-guideline"
|
|
3477
|
-
"statusProperty": "guideline-status"
|
|
3471
|
+
"profileSlug": "content-guideline"
|
|
3478
3472
|
},
|
|
3479
|
-
"humanOnlyStatuses": [
|
|
3480
|
-
"deprecated"
|
|
3481
|
-
],
|
|
3482
3473
|
"params": [
|
|
3483
3474
|
{
|
|
3484
3475
|
"name": "guideline",
|
|
@@ -3609,6 +3600,7 @@ var DATA = {
|
|
|
3609
3600
|
},
|
|
3610
3601
|
"intent": {
|
|
3611
3602
|
"label": "Product",
|
|
3603
|
+
"primary": "devplane_feature",
|
|
3612
3604
|
"produces": [
|
|
3613
3605
|
{
|
|
3614
3606
|
"profile": "devplane_feature"
|
|
@@ -4725,6 +4717,23 @@ var DATA = {
|
|
|
4725
4717
|
"icon": "alert-triangle",
|
|
4726
4718
|
"color": "red",
|
|
4727
4719
|
"description": "A production incident with severity, status, and root cause tracking",
|
|
4720
|
+
"lifecycle": {
|
|
4721
|
+
"property": "incidentStatus",
|
|
4722
|
+
"options": [
|
|
4723
|
+
{
|
|
4724
|
+
"key": "open",
|
|
4725
|
+
"category": "open"
|
|
4726
|
+
},
|
|
4727
|
+
{
|
|
4728
|
+
"key": "investigating",
|
|
4729
|
+
"category": "active"
|
|
4730
|
+
},
|
|
4731
|
+
{
|
|
4732
|
+
"key": "resolved",
|
|
4733
|
+
"category": "done"
|
|
4734
|
+
}
|
|
4735
|
+
]
|
|
4736
|
+
},
|
|
4728
4737
|
"properties": [
|
|
4729
4738
|
{
|
|
4730
4739
|
"slug": "severity",
|
|
@@ -5658,6 +5667,27 @@ var DATA = {
|
|
|
5658
5667
|
"icon": "workflow",
|
|
5659
5668
|
"color": "blue",
|
|
5660
5669
|
"description": "A user flow or business process \u2014 a sequence of steps through system components",
|
|
5670
|
+
"lifecycle": {
|
|
5671
|
+
"property": "workflowStatus",
|
|
5672
|
+
"options": [
|
|
5673
|
+
{
|
|
5674
|
+
"key": "documented",
|
|
5675
|
+
"category": "open"
|
|
5676
|
+
},
|
|
5677
|
+
{
|
|
5678
|
+
"key": "in-progress",
|
|
5679
|
+
"category": "active"
|
|
5680
|
+
},
|
|
5681
|
+
{
|
|
5682
|
+
"key": "needs-review",
|
|
5683
|
+
"category": "active"
|
|
5684
|
+
},
|
|
5685
|
+
{
|
|
5686
|
+
"key": "complete",
|
|
5687
|
+
"category": "done"
|
|
5688
|
+
}
|
|
5689
|
+
]
|
|
5690
|
+
},
|
|
5661
5691
|
"properties": [
|
|
5662
5692
|
{
|
|
5663
5693
|
"slug": "workflowStatus",
|
|
@@ -5998,10 +6028,52 @@ var DATA = {
|
|
|
5998
6028
|
"slug": "task-board",
|
|
5999
6029
|
"type": "kanban",
|
|
6000
6030
|
"scopeProfileSlug": "task",
|
|
6001
|
-
"
|
|
6031
|
+
"config": {
|
|
6032
|
+
"groupByField": "task-status"
|
|
6033
|
+
},
|
|
6002
6034
|
"defaultView": true,
|
|
6003
6035
|
"description": "All delegated tasks by status: Backlog \u2192 Queued \u2192 Running \u2192 Review \u2192 Done."
|
|
6004
6036
|
},
|
|
6037
|
+
{
|
|
6038
|
+
"name": "Feature Board",
|
|
6039
|
+
"slug": "feature-board",
|
|
6040
|
+
"type": "kanban",
|
|
6041
|
+
"scopeProfileSlug": "devplane_feature",
|
|
6042
|
+
"description": "Features by stage, Idea to Shipped.",
|
|
6043
|
+
"config": {
|
|
6044
|
+
"groupByField": "featureStatus"
|
|
6045
|
+
}
|
|
6046
|
+
},
|
|
6047
|
+
{
|
|
6048
|
+
"name": "Story Board",
|
|
6049
|
+
"slug": "story-board",
|
|
6050
|
+
"type": "kanban",
|
|
6051
|
+
"scopeProfileSlug": "devplane_story",
|
|
6052
|
+
"description": "User stories by status, Draft to Done.",
|
|
6053
|
+
"config": {
|
|
6054
|
+
"groupByField": "storyStatus"
|
|
6055
|
+
}
|
|
6056
|
+
},
|
|
6057
|
+
{
|
|
6058
|
+
"name": "Issue Board",
|
|
6059
|
+
"slug": "issue-board",
|
|
6060
|
+
"type": "kanban",
|
|
6061
|
+
"scopeProfileSlug": "issue",
|
|
6062
|
+
"description": "Bugs and problems by status, Open to Fixed.",
|
|
6063
|
+
"config": {
|
|
6064
|
+
"groupByField": "issueStatus"
|
|
6065
|
+
}
|
|
6066
|
+
},
|
|
6067
|
+
{
|
|
6068
|
+
"name": "Design Board",
|
|
6069
|
+
"slug": "design-board",
|
|
6070
|
+
"type": "kanban",
|
|
6071
|
+
"scopeProfileSlug": "ui-design",
|
|
6072
|
+
"description": "Designs by status, from draft to implemented.",
|
|
6073
|
+
"config": {
|
|
6074
|
+
"groupByField": "design-status"
|
|
6075
|
+
}
|
|
6076
|
+
},
|
|
6005
6077
|
{
|
|
6006
6078
|
"name": "Deployments",
|
|
6007
6079
|
"type": "calendar",
|
|
@@ -6045,7 +6117,9 @@ var DATA = {
|
|
|
6045
6117
|
"slug": "service-health",
|
|
6046
6118
|
"type": "kanban",
|
|
6047
6119
|
"scopeProfileSlug": "devplane_service",
|
|
6048
|
-
"
|
|
6120
|
+
"config": {
|
|
6121
|
+
"groupByField": "serviceStatus"
|
|
6122
|
+
},
|
|
6049
6123
|
"description": "Every service by health \u2014 healthy, degraded, down, maintenance."
|
|
6050
6124
|
},
|
|
6051
6125
|
{
|
|
@@ -6053,7 +6127,9 @@ var DATA = {
|
|
|
6053
6127
|
"slug": "services-by-type",
|
|
6054
6128
|
"type": "kanban",
|
|
6055
6129
|
"scopeProfileSlug": "devplane_service",
|
|
6056
|
-
"
|
|
6130
|
+
"config": {
|
|
6131
|
+
"groupByField": "serviceType"
|
|
6132
|
+
},
|
|
6057
6133
|
"description": "Services grouped by what they are \u2014 API, worker, cron, database, cache, queue."
|
|
6058
6134
|
},
|
|
6059
6135
|
{
|
|
@@ -6193,13 +6269,8 @@ var DATA = {
|
|
|
6193
6269
|
"description": "Take a UI design request from three variants built only of real Storybook components, through a rubric critique and a person's review on the canvas, to a pull request verified by the gates, a before/after compare and a walk in the running app.",
|
|
6194
6270
|
"goalTemplate": "Take this UI design ({design}) for {surface} from draft to implemented. Load the designing-with-real-components skill first, and design-critique for the Critique stage. GENERATE: read the Storybook component manifest (/manifests/components.json, or the Storybook MCP), the design's goal and scope and the entities it references, and write THREE variants as Puck JSON \u2014 this design and two sibling ui-designs sharing its variant-group \u2014 using ONLY components the manifest lists, with props each one declares, design tokens and named motion presets only; each variant lays out every state the screen has (empty, loading, failed, full, long content); fill components-used from the JSON and set schema-version. CRITIQUE: judge each variant with the design-critique rubric and write the findings into its critique; a checked or pattern blocker sends the run back to Generate; opinions are never applied. REVIEW: a person compares the variants on the canvas and approves one; nothing is implemented from an unapproved design, and only a person sets approved. IMPLEMENT: read design-json and the Storybook MCP, build the screen from those same real components, open a pull request and record its URL in implementation-pr and the built screen's story in implemented-story-id. VERIFY: typecheck, story coverage, interaction and accessibility tests, visual regression and the token guards are green, the implemented story was captured and compared with the design and the current story, and the screen was walked in the running app. Move the design's status only through its lifecycle (update_entity with expectedStatus); never invent a status.",
|
|
6195
6271
|
"subjectProfile": {
|
|
6196
|
-
"profileSlug": "ui-design"
|
|
6197
|
-
"statusProperty": "design-status"
|
|
6272
|
+
"profileSlug": "ui-design"
|
|
6198
6273
|
},
|
|
6199
|
-
"humanOnlyStatuses": [
|
|
6200
|
-
"approved",
|
|
6201
|
-
"archived"
|
|
6202
|
-
],
|
|
6203
6274
|
"activators": [
|
|
6204
6275
|
{
|
|
6205
6276
|
"on": "manual",
|
|
@@ -6716,13 +6787,8 @@ var DATA = {
|
|
|
6716
6787
|
"description": "Take a shaped change from branch to pull request, green checks, merge, lab and a dogfood walk, until its criteria are verified. The facts (PR opened, CI finished, merged, deployed) move the step; the person approves only the lab deploy.",
|
|
6717
6788
|
"goalTemplate": `Ship this change ({feature}) through one session, one branch and its pull requests, then the merge, the lab and a dogfood walk, until its criteria are verified. The step follows the work; move the feature's status only through its lifecycle (update_entity with expectedStatus), never invent one. SHAPE: write the end state and 2-5 binary criteria on this session, set the feature's change type (feature, refactor or chore), and name the repos it touches. BUILD: branch <type>/<slug> from origin/main in your own worktree, commit in verified slices, and run the smallest gate that answers for each finished slice, with its negative control; quote the output. PR OPENED: open a draft pull request whose body carries the line "Pod session: <this session id>", and record it on this session as the outcome pr-<repo>-<number> (kind pull_request, ref url); one outcome per pull request, and for each a session criterion keyed the same, "<repo>#<number> is merged", checked by the capability github_check_pr_merged, so the pod grades the merge itself. CHECKS: read the checks on the pull request's head with github_pr_checks (the github-read capability, on the person's own token) or wait for the ci.finished event; a red check sends the work back to Build. MERGE: a person merges; never merge and never push to main. The merge marks the outcome met. DEPLOY: ask the person for the lab deploy (synap_request_deploy_approval); the deploy is their call. DOGFOOD: walk the change in the running lab as a person would and report WORKS, BROKEN or CANNOT-TELL with what was seen. VERIFY: grade every criterion with its evidence, and separate VERIFIED (gate or check output quoted) from NEEDS-DOGFOOD.`,
|
|
6718
6789
|
"subjectProfile": {
|
|
6719
|
-
"profileSlug": "devplane_feature"
|
|
6720
|
-
"statusProperty": "featureStatus"
|
|
6790
|
+
"profileSlug": "devplane_feature"
|
|
6721
6791
|
},
|
|
6722
|
-
"humanOnlyStatuses": [
|
|
6723
|
-
"backlog",
|
|
6724
|
-
"cancelled"
|
|
6725
|
-
],
|
|
6726
6792
|
"activators": [
|
|
6727
6793
|
{
|
|
6728
6794
|
"on": "enters_status",
|
|
@@ -6971,21 +7037,12 @@ var DATA = {
|
|
|
6971
7037
|
},
|
|
6972
7038
|
{
|
|
6973
7039
|
"name": "Build a change",
|
|
6974
|
-
"description": "Dev set (decision d05), key: build. Works one feature (devplane_feature) from shaped criteria to a verified change on main; its stages follow the feature's lifecycle (spec, in_progress, in_review, done)
|
|
7040
|
+
"description": "Dev set (decision d05), key: build. Works one feature (devplane_feature) from shaped criteria to a verified change on main; its stages follow the feature's lifecycle (spec, in_progress, in_review, done); a cancelled or failed session leaves the feature where it is. One feature per session, never two subjects (founder default, 2026-11-05). A quick change is this playbook with size small (no plan); a night shift run is this playbook with unattended true. No activators until binding is turned on; the shape would be {on: enters_status, status: spec, size: per_thing, mode: propose}, the trigger Ship a change uses today. Reuses Ship a change (2c5aa553) stage text and evidence keys; absorbs AI Dev Session and Quick change once they are re-pointed. Draft until the founder activates it.",
|
|
6975
7041
|
"goalTemplate": 'Build {feature} through one session and one PR per repo (or gated commits on main in a private repo), size {size}. SHAPE: write the end state and 2-5 binary criteria on this session (plan skill, skipped when size is small). BUILD: branch <type>/<slug> from origin/main in your own worktree, commit in verified slices, run the smallest gate that answers with its negative control, quote the output. LAND: follow the land stages (Change proposed, Checks green, Landed on main, Deployed to lab, Dogfooded, Criteria verified). Unattended: {unattended}. When unattended is true, ask no person, stop at a draft PR or a gated branch, never merge, push main or deploy, and post one "Look at first:" paragraph in the room.',
|
|
6976
7042
|
"scope": "session",
|
|
6977
7043
|
"subjectProfile": {
|
|
6978
|
-
"
|
|
6979
|
-
"failed": "spec",
|
|
6980
|
-
"cancelled": "spec"
|
|
6981
|
-
},
|
|
6982
|
-
"profileSlug": "devplane_feature",
|
|
6983
|
-
"statusProperty": "featureStatus"
|
|
7044
|
+
"profileSlug": "devplane_feature"
|
|
6984
7045
|
},
|
|
6985
|
-
"humanOnlyStatuses": [
|
|
6986
|
-
"backlog",
|
|
6987
|
-
"cancelled"
|
|
6988
|
-
],
|
|
6989
7046
|
"params": [
|
|
6990
7047
|
{
|
|
6991
7048
|
"name": "feature",
|
|
@@ -7224,21 +7281,12 @@ var DATA = {
|
|
|
7224
7281
|
},
|
|
7225
7282
|
{
|
|
7226
7283
|
"name": "Fix a bug",
|
|
7227
|
-
"description": "Dev set (decision d05), key: fix. Works an issue at its root: reproduce it with a failing test, fix the canonical path, a clean-context review, then the shared land stages. Its stages follow the issue's lifecycle (open, investigating, fixed); a cancelled or failed session
|
|
7284
|
+
"description": "Dev set (decision d05), key: fix. Works an issue at its root: reproduce it with a failing test, fix the canonical path, a clean-context review, then the shared land stages. Its stages follow the issue's lifecycle (open, investigating, fixed); a cancelled or failed session leaves the issue where it is, as Fix a finding does. Small fixes may be batched (founder default, 2026-11-05): several issues in one session, one as the subject and the rest as session targets; today a stage moves only the subject. No activators until binding is turned on; a batch would be {on: count, status: open, min: 3} or {on: schedule, cron, status: open} (both one session per moment). A night shift run is this playbook with unattended true. Reuses Bugfix (8c335e2e), Night shift: build an issue (c101dd75) and Ship a change (2c5aa553). Draft until the founder activates it.",
|
|
7228
7285
|
"goalTemplate": 'Fix the issue {issue} at its root and prove it with a test that fails before the fix and passes after. REPRODUCE (recall first, smallest reproduction, root cause file:line), FIX on the canonical path in your own worktree, a clean-context REVIEW, then LAND through the shared land stages (Change proposed, Checks green, Landed on main, Deployed to lab, Dogfooded, Criteria verified). Unattended: {unattended}. When unattended is true, ask no person, stop at a draft PR or a gated branch, never merge, push main or deploy; if no code fix is right, write the root cause as a pod document; post one "Look at first:" paragraph in the room.',
|
|
7229
7286
|
"scope": "session",
|
|
7230
7287
|
"subjectProfile": {
|
|
7231
|
-
"
|
|
7232
|
-
"failed": "open",
|
|
7233
|
-
"cancelled": "open"
|
|
7234
|
-
},
|
|
7235
|
-
"profileSlug": "issue",
|
|
7236
|
-
"statusProperty": "issueStatus"
|
|
7288
|
+
"profileSlug": "issue"
|
|
7237
7289
|
},
|
|
7238
|
-
"humanOnlyStatuses": [
|
|
7239
|
-
"open",
|
|
7240
|
-
"wont_fix"
|
|
7241
|
-
],
|
|
7242
7290
|
"params": [
|
|
7243
7291
|
{
|
|
7244
7292
|
"name": "issue",
|
|
@@ -8084,14 +8132,8 @@ var DATA = {
|
|
|
8084
8132
|
"goalTemplate": "Fix the finding and see the fix live",
|
|
8085
8133
|
"scope": "session",
|
|
8086
8134
|
"subjectProfile": {
|
|
8087
|
-
"profileSlug": "finding"
|
|
8088
|
-
"statusProperty": "status"
|
|
8135
|
+
"profileSlug": "finding"
|
|
8089
8136
|
},
|
|
8090
|
-
"humanOnlyStatuses": [
|
|
8091
|
-
"open",
|
|
8092
|
-
"wont_fix",
|
|
8093
|
-
"superseded"
|
|
8094
|
-
],
|
|
8095
8137
|
"params": [],
|
|
8096
8138
|
"executor": "is-agent",
|
|
8097
8139
|
"status": "active",
|
|
@@ -8368,6 +8410,27 @@ var DATA = {
|
|
|
8368
8410
|
"displayName": "Question",
|
|
8369
8411
|
"icon": "help-circle",
|
|
8370
8412
|
"profileKind": "kind",
|
|
8413
|
+
"lifecycle": {
|
|
8414
|
+
"property": "questionStatus",
|
|
8415
|
+
"options": [
|
|
8416
|
+
{
|
|
8417
|
+
"key": "open",
|
|
8418
|
+
"category": "open"
|
|
8419
|
+
},
|
|
8420
|
+
{
|
|
8421
|
+
"key": "exploring",
|
|
8422
|
+
"category": "active"
|
|
8423
|
+
},
|
|
8424
|
+
{
|
|
8425
|
+
"key": "answered",
|
|
8426
|
+
"category": "done"
|
|
8427
|
+
},
|
|
8428
|
+
{
|
|
8429
|
+
"key": "abandoned",
|
|
8430
|
+
"category": "lost"
|
|
8431
|
+
}
|
|
8432
|
+
]
|
|
8433
|
+
},
|
|
8371
8434
|
"properties": [
|
|
8372
8435
|
{
|
|
8373
8436
|
"slug": "questionStatus",
|
|
@@ -8388,6 +8451,27 @@ var DATA = {
|
|
|
8388
8451
|
"displayName": "Decision",
|
|
8389
8452
|
"icon": "gavel",
|
|
8390
8453
|
"profileKind": "kind",
|
|
8454
|
+
"lifecycle": {
|
|
8455
|
+
"property": "decisionStatus",
|
|
8456
|
+
"options": [
|
|
8457
|
+
{
|
|
8458
|
+
"key": "proposed",
|
|
8459
|
+
"category": "open"
|
|
8460
|
+
},
|
|
8461
|
+
{
|
|
8462
|
+
"key": "accepted",
|
|
8463
|
+
"category": "done"
|
|
8464
|
+
},
|
|
8465
|
+
{
|
|
8466
|
+
"key": "superseded",
|
|
8467
|
+
"category": "lost"
|
|
8468
|
+
},
|
|
8469
|
+
{
|
|
8470
|
+
"key": "rejected",
|
|
8471
|
+
"category": "lost"
|
|
8472
|
+
}
|
|
8473
|
+
]
|
|
8474
|
+
},
|
|
8391
8475
|
"properties": [
|
|
8392
8476
|
{
|
|
8393
8477
|
"slug": "decisionStatus",
|
|
@@ -8709,7 +8793,9 @@ var DATA = {
|
|
|
8709
8793
|
"slug": "business-model-open-questions",
|
|
8710
8794
|
"type": "kanban",
|
|
8711
8795
|
"scopeProfileSlug": "question",
|
|
8712
|
-
"
|
|
8796
|
+
"config": {
|
|
8797
|
+
"groupByField": "questionStatus"
|
|
8798
|
+
},
|
|
8713
8799
|
"description": "The nine component questions and everything they spawned, by status."
|
|
8714
8800
|
}
|
|
8715
8801
|
],
|
|
@@ -9093,6 +9179,27 @@ var DATA = {
|
|
|
9093
9179
|
"icon": "help-circle",
|
|
9094
9180
|
"color": "blue",
|
|
9095
9181
|
"description": "An open question driving a civic-data investigation.",
|
|
9182
|
+
"lifecycle": {
|
|
9183
|
+
"property": "questionStatus",
|
|
9184
|
+
"options": [
|
|
9185
|
+
{
|
|
9186
|
+
"key": "open",
|
|
9187
|
+
"category": "open"
|
|
9188
|
+
},
|
|
9189
|
+
{
|
|
9190
|
+
"key": "exploring",
|
|
9191
|
+
"category": "active"
|
|
9192
|
+
},
|
|
9193
|
+
{
|
|
9194
|
+
"key": "answered",
|
|
9195
|
+
"category": "done"
|
|
9196
|
+
},
|
|
9197
|
+
{
|
|
9198
|
+
"key": "abandoned",
|
|
9199
|
+
"category": "lost"
|
|
9200
|
+
}
|
|
9201
|
+
]
|
|
9202
|
+
},
|
|
9096
9203
|
"properties": [
|
|
9097
9204
|
{
|
|
9098
9205
|
"slug": "questionStatus",
|
|
@@ -9666,7 +9773,7 @@ var DATA = {
|
|
|
9666
9773
|
"scopeProfileSlug": "content-program",
|
|
9667
9774
|
"description": "Programs grouped by status \u2014 a running scoreboard for the content operation.",
|
|
9668
9775
|
"config": {
|
|
9669
|
-
"
|
|
9776
|
+
"groupByField": "status"
|
|
9670
9777
|
}
|
|
9671
9778
|
}
|
|
9672
9779
|
],
|
|
@@ -9787,7 +9894,6 @@ var DATA = {
|
|
|
9787
9894
|
"musician",
|
|
9788
9895
|
"photographer"
|
|
9789
9896
|
],
|
|
9790
|
-
"profile": "post",
|
|
9791
9897
|
"views": [
|
|
9792
9898
|
"content-posts",
|
|
9793
9899
|
"content-calendar"
|
|
@@ -9841,6 +9947,7 @@ var DATA = {
|
|
|
9841
9947
|
},
|
|
9842
9948
|
"intent": {
|
|
9843
9949
|
"label": "Content",
|
|
9950
|
+
"primary": "post",
|
|
9844
9951
|
"produces": [
|
|
9845
9952
|
{
|
|
9846
9953
|
"profile": "post"
|
|
@@ -10555,6 +10662,27 @@ var DATA = {
|
|
|
10555
10662
|
"color": "#0EA5E9",
|
|
10556
10663
|
"description": "An ordered run of posts that teaches one thing end to end \u2014 the curriculum axis of the content engine.",
|
|
10557
10664
|
"scope": "WORKSPACE",
|
|
10665
|
+
"lifecycle": {
|
|
10666
|
+
"property": "series-status",
|
|
10667
|
+
"options": [
|
|
10668
|
+
{
|
|
10669
|
+
"key": "Planned",
|
|
10670
|
+
"category": "open"
|
|
10671
|
+
},
|
|
10672
|
+
{
|
|
10673
|
+
"key": "Running",
|
|
10674
|
+
"category": "active"
|
|
10675
|
+
},
|
|
10676
|
+
{
|
|
10677
|
+
"key": "Complete",
|
|
10678
|
+
"category": "done"
|
|
10679
|
+
},
|
|
10680
|
+
{
|
|
10681
|
+
"key": "Abandoned",
|
|
10682
|
+
"category": "lost"
|
|
10683
|
+
}
|
|
10684
|
+
]
|
|
10685
|
+
},
|
|
10558
10686
|
"properties": [
|
|
10559
10687
|
{
|
|
10560
10688
|
"slug": "series-status",
|
|
@@ -11718,7 +11846,9 @@ var DATA = {
|
|
|
11718
11846
|
"slug": "content-posts",
|
|
11719
11847
|
"type": "kanban",
|
|
11720
11848
|
"scopeProfileSlug": "post",
|
|
11721
|
-
"
|
|
11849
|
+
"config": {
|
|
11850
|
+
"groupByField": "post-status"
|
|
11851
|
+
},
|
|
11722
11852
|
"description": "Content posts by status."
|
|
11723
11853
|
},
|
|
11724
11854
|
{
|
|
@@ -11762,7 +11892,9 @@ var DATA = {
|
|
|
11762
11892
|
"slug": "content-newsletters",
|
|
11763
11893
|
"type": "kanban",
|
|
11764
11894
|
"scopeProfileSlug": "newsletter",
|
|
11765
|
-
"
|
|
11895
|
+
"config": {
|
|
11896
|
+
"groupByField": "newsletter-status"
|
|
11897
|
+
},
|
|
11766
11898
|
"description": "Newsletter issues by status."
|
|
11767
11899
|
},
|
|
11768
11900
|
{
|
|
@@ -12058,13 +12190,8 @@ var DATA = {
|
|
|
12058
12190
|
"description": "Take a briefed post from brief to script to published in any format \u2014 text, visual or video (HyperFrames) \u2014 from the pod's brand kit, one stage per post status, with a person approving the script before anything is generated.",
|
|
12059
12191
|
"goalTemplate": "Produce content for {platform} from this idea ({post}) in the {format} format. Read the brand first from the pod's brand kit (GET /api/hub/brand/kit) \u2014 never re-derive it from a website capture \u2014 and load the creative-director skill (linked to this space) to pick the approved brand-template (layout and recipe), references and assets for this format and style; drafts never feed generation. Work the stages in order; each stage moves the post's status. SCRIPT before anything is made: write the script or outline for the format (carousel slide outline, deck outline, video script and storyboard, or the text draft) by filling the narrative frame for the post's goal \u2014 the approved content-guideline of type frame for that goal, else the default frame (sell: story, example, lesson, link; hot-take: claim, tension, reason, invitation; tutorial: promise, steps, result, next; product-demo: problem, real screen, outcome, try it). A person approves the script before the draft; nothing is generated from an unapproved script. At the draft, run the creative-director readiness check first (approved references, an approved base guideline, an approved brand-template for this format); if anything is missing, stop and route to the content-taste skill or brand onboarding instead of improvising. Then branch on the format. TEXT (article, thread, newsletter section, caption): draft it with a scroll-stopping hook and a clear CTA. VISUAL: build it pass by pass through the edit-loop skill, a person answering each pass \u2014 a carousel, LinkedIn PDF or image post as a layered composition saved on the post (carousel-format), a deck, PDF document or presentation in Open Design (open-design-edit). VIDEO (short or long video): first pick the route \u2014 FROM SCRATCH (script, storyboard, frames), FROM A VOICE (an existing voice-over or talk: transcribe it, cut on its beats, build the frames under it) or RE-EDIT (existing footage plus a voice: cut, caption, add music and motion); takes ({takes}) mean RE-EDIT, and the Draft stage says how takes, transcripts and passes are handled. Then run the HyperFrames workflow on your own agent with HYPERFRAMES_NO_TELEMETRY=1, from the kit's frame.md (format=frame-md), with GSAP vendored locally \u2014 never `hyperframes publish`, never a cloud render. For a visual or video format, record a content-composition made for this post (for a video: the brief and storyboard documents, the project zip, the MP4, key-frame stills and the brand-kit hash). Keep every format on the post's pillar and the brand voice. A human approves the script before the draft, each pass as it is built, and the draft on the preview at Review. At Schedule, a post whose account is a Postiz channel is queued through the capability that serves publish_post (postiz_schedule_post); that queueing is itself a proposal a person approves. Nothing publishes itself, and the post reads Published only once it went live: reported by Postiz, or confirmed by a person at Publish.",
|
|
12060
12192
|
"subjectProfile": {
|
|
12061
|
-
"profileSlug": "post"
|
|
12062
|
-
"statusProperty": "post-status"
|
|
12193
|
+
"profileSlug": "post"
|
|
12063
12194
|
},
|
|
12064
|
-
"humanOnlyStatuses": [
|
|
12065
|
-
"Idea",
|
|
12066
|
-
"Archived"
|
|
12067
|
-
],
|
|
12068
12195
|
"activators": [
|
|
12069
12196
|
{
|
|
12070
12197
|
"on": "enters_status",
|
|
@@ -12502,8 +12629,7 @@ var DATA = {
|
|
|
12502
12629
|
"description": "Turn a published post into new posts for other formats and platforms, each linked to the original.",
|
|
12503
12630
|
"goalTemplate": "Repurpose this published post ({post}). Read what it says and how it performed, then propose one to three new pieces in other formats or on other platforms where the same idea travels (a thread from an article, a short video from a thread, a newsletter section from a series). Create each as a new post at the Idea status, on the same pillar, with its own hook, and link it derived_from the original. Never republish the original as is.",
|
|
12504
12631
|
"subjectProfile": {
|
|
12505
|
-
"profileSlug": "post"
|
|
12506
|
-
"statusProperty": "post-status"
|
|
12632
|
+
"profileSlug": "post"
|
|
12507
12633
|
},
|
|
12508
12634
|
"activators": [
|
|
12509
12635
|
{
|
|
@@ -12539,12 +12665,8 @@ var DATA = {
|
|
|
12539
12665
|
"description": "Take a newsletter issue from idea to sent \u2014 angle, draft, edit, schedule \u2014 with a person approving before it goes out.",
|
|
12540
12666
|
"goalTemplate": "Produce this newsletter issue ({newsletter}). Read the brand voice from the pod's brand kit, then work the stages in order: settle the issue's angle and the posts it draws on, draft it, edit it down with a subject line and preview text, and schedule the send. A person approves the final issue before it is sent, nothing sends itself, and the issue reads Sent only once a person confirms it went out.",
|
|
12541
12667
|
"subjectProfile": {
|
|
12542
|
-
"profileSlug": "newsletter"
|
|
12543
|
-
"statusProperty": "newsletter-status"
|
|
12668
|
+
"profileSlug": "newsletter"
|
|
12544
12669
|
},
|
|
12545
|
-
"humanOnlyStatuses": [
|
|
12546
|
-
"Archived"
|
|
12547
|
-
],
|
|
12548
12670
|
"params": [
|
|
12549
12671
|
{
|
|
12550
12672
|
"name": "newsletter",
|
|
@@ -13280,7 +13402,6 @@ var DATA = {
|
|
|
13280
13402
|
"small-business",
|
|
13281
13403
|
"founder"
|
|
13282
13404
|
],
|
|
13283
|
-
"profile": "deal",
|
|
13284
13405
|
"views": [
|
|
13285
13406
|
"deal-pipeline",
|
|
13286
13407
|
"deals-table",
|
|
@@ -13339,6 +13460,7 @@ var DATA = {
|
|
|
13339
13460
|
},
|
|
13340
13461
|
"intent": {
|
|
13341
13462
|
"label": "Sales",
|
|
13463
|
+
"primary": "deal",
|
|
13342
13464
|
"produces": [
|
|
13343
13465
|
{
|
|
13344
13466
|
"profile": "lead"
|
|
@@ -14312,10 +14434,9 @@ var DATA = {
|
|
|
14312
14434
|
"slug": "outreach-touches",
|
|
14313
14435
|
"type": "kanban",
|
|
14314
14436
|
"scopeProfileSlug": "outreach-touch",
|
|
14315
|
-
"groupBy": "status",
|
|
14316
14437
|
"description": "Every outreach message, grouped by where it stands \u2014 drafted for a person to send, through replied.",
|
|
14317
14438
|
"config": {
|
|
14318
|
-
"
|
|
14439
|
+
"groupByField": "status",
|
|
14319
14440
|
"cardFields": [
|
|
14320
14441
|
"channel",
|
|
14321
14442
|
"touchType",
|
|
@@ -14415,17 +14536,8 @@ var DATA = {
|
|
|
14415
14536
|
"description": 'Research one lead, decide with a person whether it is worth pursuing, and set a pursued lead qualified with "book a meeting" as its next action.',
|
|
14416
14537
|
"goalTemplate": 'Qualify this lead ({lead}). RESEARCH: verify and enrich only facts you can support \u2014 company, role, size, recent signal \u2014 and record the source of each. SCORE: assess fit against the offers and the ideal client, set the ICP score, qualification notes and outreach channels, and write the step you recommend as the lead\'s next action: `nextAction` one imperative line (e.g. "Send the first message on LinkedIn") and `nextActionAt` its date. DECIDE: ask the person to `choose` pursue, nurture or drop, your recommendation first; that answer is the decision. NURTURE: set the lead to nurturing, with `nextAction` to check in again and `nextActionAt` the date they gave (three months out when they gave none). DROP: write their reason in `disqualificationReason` and move the lead to disqualified; that move is the person\'s (disqualified is human-only), so it comes back proposed for them to approve, which is success; never write it another way. PURSUE: only when they chose pursue, set the lead to qualified with `nextAction` "Book a meeting" (one line naming the channel, e.g. "Ask for a meeting on LinkedIn") and `nextActionAt` its date. Never create a deal: a deal is opened when a person records a booked meeting, and that same step converts the lead. Never mark the lead a client.',
|
|
14417
14538
|
"subjectProfile": {
|
|
14418
|
-
"profileSlug": "lead"
|
|
14419
|
-
|
|
14420
|
-
},
|
|
14421
|
-
"humanOnlyStatuses": [
|
|
14422
|
-
"new",
|
|
14423
|
-
"outreach",
|
|
14424
|
-
"engaged",
|
|
14425
|
-
"nurturing",
|
|
14426
|
-
"converted",
|
|
14427
|
-
"disqualified"
|
|
14428
|
-
],
|
|
14539
|
+
"profileSlug": "lead"
|
|
14540
|
+
},
|
|
14429
14541
|
"activators": [
|
|
14430
14542
|
{
|
|
14431
14543
|
"on": "manual",
|
|
@@ -14549,12 +14661,8 @@ var DATA = {
|
|
|
14549
14661
|
"description": "Take a deal from first conversation to won or lost \u2014 qualify it, write and send the proposal, negotiate, and hand a won deal to delivery, with a person deciding every commitment.",
|
|
14550
14662
|
"goalTemplate": `Work this deal ({deal}) through its stages; each stage moves the deal's commercial stage. QUALIFY: from the pod (the company, its people, every past message and meeting) and public sources, establish the four facts a proposal needs \u2014 the problem the buyer wants solved and what it costs them, a budget range, who signs, and when they need it \u2014 and link the people involved to the deal. Do not guess a fact: when one is missing, ask the person who owns the relationship. WRITE: draft the proposal document (scope, deliverables, timeline, price, terms) from the agency's offers and past won proposals, and set the deal's value and close date. SEND: a person approves the proposal before it goes to the client; nothing is sent without that approval. AWAIT: wait for the client's answer and record it on the deal. NEGOTIATE: its check reads that answer; log every objection and counter-offer and propose the next step. WON: once a person confirms the client said yes, reuse the engagement Operations already has for this company; only when there is none, open one at proposed with its client set to the company. Then attach the client role to the company with handoff status handed_off. The daily "Pick up handed-off clients" automation skips any client that already has an engagement, so no second one is opened. A lost deal is a person's call \u2014 record why it was lost.`,
|
|
14551
14663
|
"subjectProfile": {
|
|
14552
|
-
"profileSlug": "deal"
|
|
14553
|
-
"statusProperty": "commercialStage"
|
|
14664
|
+
"profileSlug": "deal"
|
|
14554
14665
|
},
|
|
14555
|
-
"humanOnlyStatuses": [
|
|
14556
|
-
"lost"
|
|
14557
|
-
],
|
|
14558
14666
|
"activators": [
|
|
14559
14667
|
{
|
|
14560
14668
|
"on": "created",
|
|
@@ -15262,8 +15370,7 @@ var DATA = {
|
|
|
15262
15370
|
"description": "Each weekday, read the channels of the leads in outreach; summarise a reply and propose the next step, or, when the follow-up is due with no reply, draft the next message in the angle of its outreach campaign as a proposed message for a person to approve; the agent never sends.",
|
|
15263
15371
|
"goalTemplate": `Follow up on the leads in outreach. For each, read its channels (synap_get_channel, mode person: for a company lead, the channels of the person who decides there) and its notes. A REPLY is an inbound message after our last outbound one in a channel. With a reply, draft nothing: summarise it for a person; when they show interest, propose the next step (prepare the free estimate, "Prepare a pre-sale estimate", or book a meeting) and propose moving the lead to engaged; when they decline, propose nurturing or disqualified with their reason (in disqualificationReason). Unless you propose dropping them, write the next step on the lead as nextAction, one imperative line, and nextActionAt, the date they named or today. Never open a deal and never answer the reply yourself; a person does. With no reply, and only when the lead's next action date is today or past and no proposed message to it is still waiting, draft the next message, to the lead (for a company, the person who decides there) and in the angle of the lead's outreach campaign: follow its value proposition and call to action, short and in the lead's language, offering the next useful step, never pressure. For a lead with no campaign, ask the person which one. Draft at most two follow-ups per lead: a lead already sent two follow-ups gets no draft, never a third follow-up; when its last date passes in silence, propose moving it to nurturing through the status door, with its expected status. DRAFT RULES: each draft is one proposed message in the channel of the person it is written to, filed through the pod's send door: run messaging.send with to = that person's entity id (for a company lead, its decider: when that person is not in the pod, create them through the entity door with only their own fields, such as name, role and LinkedIn URL, never the company's website, domain or email), text = the message, and provider linkedin or email. Read their channel first (synap_get_channel, mode person) for what was already said. The pod files it for a person to review in that channel; it goes out only once they approve it. Never call a tool's own send verb (Unipile, Gmail), never create an outreach touch, a note or a task holding the message, and never use synap_capture for it. Never send. A LinkedIn message is drafted whatever their reach: the pod holds a draft to someone who is not a connection yet, shows it with the next step and lets it go out once they connect, so never hold a draft back for want of a connection. Draft it, then, in this order: (1) a person with no linkedinUrl: find their profile with linkedin_find_profile (their full name and company); on a match, propose its linkedinUrl and linkedinProviderId on the person for a person to approve; on none or several, ask the person to choose among its candidates (the process "Find their LinkedIn" does this for one lead). (2) Read their linkedinConnection, which only the pod writes (never set it or linkedinInvitedAt): connected, nothing more; none or not set, ask to connect with linkedin_send_invite (it refuses someone already connected or already invited: then nothing more); pending, the draft waits for them; ignored, draft by email instead when they have an email address. While the draft waits for them to connect, set the lead's nextAction to "Wait for <name> to accept the LinkedIn request" and its nextActionAt 7 days later. An email message needs none of this. For each draft, set the lead's \`nextAction\` to send it (e.g. "Send the drafted message to Ana on LinkedIn") and \`nextActionAt\` to today. The person reviews each proposed message in its channel, edits it if needed and approves it; the agent never sends. CADENCE: when a message is sent (a person approved its proposed message, so it is an outbound message in the channel), set its lead's lastContactedAt to the sent day, count the messages sent to the lead (the outbound messages in its channels, read with synap_get_channel mode person: for a company lead, the channels of the person written to there), and set the lead's next action: after the first message, nextAction "Follow up with <name> if no reply" and nextActionAt the sent day + 3 days; after the first follow-up, nextAction "Follow up with <name> a last time if no reply" and nextActionAt the sent day + 7 days; after the second follow-up, nextAction "No reply after two follow-ups: nurture or drop <name>" and nextActionAt the sent day + 7 days, and when that date passes with no reply, "Follow up on leads in outreach" proposes nurturing. Never a third follow-up. Propose moving a lead at new or researching to outreach through the status door, with its expected status. A reply stops the cadence: a reply is an inbound message after our last outbound one in the channel, and "Follow up on leads in outreach" reads it and proposes the next step.`,
|
|
15264
15372
|
"subjectProfile": {
|
|
15265
|
-
"profileSlug": "lead"
|
|
15266
|
-
"statusProperty": "leadStage"
|
|
15373
|
+
"profileSlug": "lead"
|
|
15267
15374
|
},
|
|
15268
15375
|
"activators": [
|
|
15269
15376
|
{
|
|
@@ -15349,13 +15456,8 @@ var DATA = {
|
|
|
15349
15456
|
"description": "Run one outreach campaign \u2014 write the angle and its offer, pick the people, draft one personal first message each as a proposed message in their channel; a person approves the list, then approves each message, which the pod sends.",
|
|
15350
15457
|
"goalTemplate": `Run this outreach campaign ({campaign}) on {channel}; each stage moves its status. If its type is not outreach, stop and say so. PREPARE: write the target audience, the value proposition and the call to action. Pick the people to reach from the pod (and public sources when the pod has too few), resolving each one before creating anything, link each as a member of the campaign with the lead role (lead source cold-outreach when it has none), and for each draft one first message to that person in this campaign's angle, specific to them \u2014 their company, role and any past message \u2014 never a generic template. DRAFT RULES: each draft is one proposed message in the channel of the person it is written to, filed through the pod's send door: run messaging.send with to = that person's entity id (for a company lead, its decider: when that person is not in the pod, create them through the entity door with only their own fields, such as name, role and LinkedIn URL, never the company's website, domain or email), text = the message, and provider linkedin or email. Read their channel first (synap_get_channel, mode person) for what was already said. The pod files it for a person to review in that channel; it goes out only once they approve it. Never call a tool's own send verb (Unipile, Gmail), never create an outreach touch, a note or a task holding the message, and never use synap_capture for it. Never send. A LinkedIn message is drafted whatever their reach: the pod holds a draft to someone who is not a connection yet, shows it with the next step and lets it go out once they connect, so never hold a draft back for want of a connection. Draft it, then, in this order: (1) a person with no linkedinUrl: find their profile with linkedin_find_profile (their full name and company); on a match, propose its linkedinUrl and linkedinProviderId on the person for a person to approve; on none or several, ask the person to choose among its candidates (the process "Find their LinkedIn" does this for one lead). (2) Read their linkedinConnection, which only the pod writes (never set it or linkedinInvitedAt): connected, nothing more; none or not set, ask to connect with linkedin_send_invite (it refuses someone already connected or already invited: then nothing more); pending, the draft waits for them; ignored, draft by email instead when they have an email address. While the draft waits for them to connect, set the lead's nextAction to "Wait for <name> to accept the LinkedIn request" and its nextActionAt 7 days later. An email message needs none of this. START: a person approves the audience, the offer and the list, then reviews each proposed message in its channel and approves it; nothing goes out before they approve it, and the agent never sends. Once a message is sent, follow the cadence, so "Follow up on leads in outreach" drafts the next one. CADENCE: when a message is sent (a person approved its proposed message, so it is an outbound message in the channel), set its lead's lastContactedAt to the sent day, count the messages sent to the lead (the outbound messages in its channels, read with synap_get_channel mode person: for a company lead, the channels of the person written to there), and set the lead's next action: after the first message, nextAction "Follow up with <name> if no reply" and nextActionAt the sent day + 3 days; after the first follow-up, nextAction "Follow up with <name> a last time if no reply" and nextActionAt the sent day + 7 days; after the second follow-up, nextAction "No reply after two follow-ups: nurture or drop <name>" and nextActionAt the sent day + 7 days, and when that date passes with no reply, "Follow up on leads in outreach" proposes nurturing. Never a third follow-up. Propose moving a lead at new or researching to outreach through the status door, with its expected status. A reply stops the cadence: a reply is an inbound message after our last outbound one in the channel, and "Follow up on leads in outreach" reads it and proposes the next step. FINISH: when every person written to has replied or their follow-ups ended in silence, record in the campaign notes how many messages were sent (the outbound messages in their channels), how many got a reply and how many led to a booked meeting. Pausing or cancelling is a person's call.`,
|
|
15351
15458
|
"subjectProfile": {
|
|
15352
|
-
"profileSlug": "campaign"
|
|
15353
|
-
"statusProperty": "campaign-status"
|
|
15459
|
+
"profileSlug": "campaign"
|
|
15354
15460
|
},
|
|
15355
|
-
"humanOnlyStatuses": [
|
|
15356
|
-
"paused",
|
|
15357
|
-
"cancelled"
|
|
15358
|
-
],
|
|
15359
15461
|
"activators": [
|
|
15360
15462
|
{
|
|
15361
15463
|
"on": "created",
|
|
@@ -15457,15 +15559,8 @@ var DATA = {
|
|
|
15457
15559
|
"description": "Once a person has marked an outreach touch sent (messages recorded before the channels), watch for the reply; summarise it and propose the next step \u2014 the free estimate or a meeting \u2014 and never open a deal.",
|
|
15458
15560
|
"goalTemplate": 'Watch this sent outreach message ({touch}) for a reply; each stage moves its status. WAIT: watch its conversation. Set it bounced when it did not reach the person, or no response when its lead\'s next action date passes with no reply; that date is the cadence\'s (the sent day + 3 days after the first message, the sent day + 7 days after each follow-up), so when the lead has none, count from this message\'s sent date. The next message is drafted by "Follow up on leads in outreach", not here. When this message is the second follow-up (the third message sent to the lead) and its date passes with no reply, mark it no response and propose moving the lead to nurturing through the status door, with its expected status; there is never a third follow-up. REPLIED: its check reads the reply. Summarise it for a person. When they show interest, propose the next step \u2014 prepare the free estimate ("Prepare a pre-sale estimate") or book a meeting \u2014 and propose moving the lead to engaged; when they decline, propose nurturing or disqualified with their reason (in `disqualificationReason`). Unless you propose dropping them, write the next step you propose on the lead as `nextAction`, one imperative line (e.g. "Book a meeting with Ana"; "Check in again" for nurturing), and `nextActionAt`, the date they named or today; never leave it only in the summary. BOUNCED: set the lead\'s `nextAction` to find another way to reach them and `nextActionAt` to today. Never open a deal: a deal is opened only when a person records a booked meeting. Never answer the reply yourself; a person does. Opened is recorded by a person.',
|
|
15459
15561
|
"subjectProfile": {
|
|
15460
|
-
"profileSlug": "outreach-touch"
|
|
15461
|
-
|
|
15462
|
-
},
|
|
15463
|
-
"humanOnlyStatuses": [
|
|
15464
|
-
"drafted",
|
|
15465
|
-
"sent",
|
|
15466
|
-
"opened",
|
|
15467
|
-
"replaced"
|
|
15468
|
-
],
|
|
15562
|
+
"profileSlug": "outreach-touch"
|
|
15563
|
+
},
|
|
15469
15564
|
"activators": [
|
|
15470
15565
|
{
|
|
15471
15566
|
"on": "enters_status",
|
|
@@ -16462,6 +16557,7 @@ var DATA = {
|
|
|
16462
16557
|
"isPublic": true,
|
|
16463
16558
|
"intent": {
|
|
16464
16559
|
"label": "Finance",
|
|
16560
|
+
"primary": "invoice",
|
|
16465
16561
|
"produces": [
|
|
16466
16562
|
{
|
|
16467
16563
|
"profile": "invoice"
|
|
@@ -17057,15 +17153,24 @@ var DATA = {
|
|
|
17057
17153
|
}
|
|
17058
17154
|
}
|
|
17059
17155
|
},
|
|
17156
|
+
{
|
|
17157
|
+
"name": "Invoice Board",
|
|
17158
|
+
"slug": "invoice-board",
|
|
17159
|
+
"type": "kanban",
|
|
17160
|
+
"scopeProfileSlug": "invoice",
|
|
17161
|
+
"description": "Invoices by status, draft to paid \u2014 the flow board of Bill a client.",
|
|
17162
|
+
"config": {
|
|
17163
|
+
"groupByField": "status"
|
|
17164
|
+
}
|
|
17165
|
+
},
|
|
17060
17166
|
{
|
|
17061
17167
|
"name": "Expenses",
|
|
17062
17168
|
"slug": "expenses",
|
|
17063
17169
|
"type": "kanban",
|
|
17064
17170
|
"scopeProfileSlug": "expense",
|
|
17065
|
-
"groupBy": "status",
|
|
17066
17171
|
"description": "Accounts payable and spend, grouped by approval/payment status.",
|
|
17067
17172
|
"config": {
|
|
17068
|
-
"
|
|
17173
|
+
"groupByField": "status",
|
|
17069
17174
|
"cardFields": [
|
|
17070
17175
|
"category",
|
|
17071
17176
|
"amount",
|
|
@@ -17372,12 +17477,8 @@ var DATA = {
|
|
|
17372
17477
|
"description": "Take an invoice from draft to paid \u2014 prepare it, send it with a person's approval, chase it when late, and confirm the payment.",
|
|
17373
17478
|
"goalTemplate": "Bill this invoice ({invoice}); each stage moves its status. PREPARE: set the invoice number, customer, amount, currency, issue date and due date from the engagement or deal it bills, and check the amount against what was agreed. SEND: a person approves the invoice before it goes to the client; record the issue date. DUE: wait for the payment; look for it in the bank or payment provider (match the amount and the reference), and move on once it is found or the due date has passed. PAID: its check reads the payment; set the paid date and link the account it was deposited to. When the due date passed unpaid, the check sends the run to CHASE instead: the invoice becomes overdue and you draft a polite reminder for a person to send; once the payment is found, move the run back to Paid. Voiding an invoice is a person's call.",
|
|
17374
17479
|
"subjectProfile": {
|
|
17375
|
-
"profileSlug": "invoice"
|
|
17376
|
-
"statusProperty": "status"
|
|
17480
|
+
"profileSlug": "invoice"
|
|
17377
17481
|
},
|
|
17378
|
-
"humanOnlyStatuses": [
|
|
17379
|
-
"void"
|
|
17380
|
-
],
|
|
17381
17482
|
"activators": [
|
|
17382
17483
|
{
|
|
17383
17484
|
"on": "created",
|
|
@@ -17535,13 +17636,8 @@ var DATA = {
|
|
|
17535
17636
|
"description": "Review an expense against its budget, get a person's approval, and record the payment.",
|
|
17536
17637
|
"goalTemplate": "Handle this expense ({expense}); each stage moves its status. REVIEW: check the vendor, amount, date, category and receipt, link the budget it counts against and say how much of that budget is left after it. APPROVE: a person approves or rejects the spend. PAY: a person confirms it was paid; link the account it was paid from. Reimbursement and rejection are a person's call.",
|
|
17537
17638
|
"subjectProfile": {
|
|
17538
|
-
"profileSlug": "expense"
|
|
17539
|
-
"statusProperty": "status"
|
|
17639
|
+
"profileSlug": "expense"
|
|
17540
17640
|
},
|
|
17541
|
-
"humanOnlyStatuses": [
|
|
17542
|
-
"reimbursed",
|
|
17543
|
-
"rejected"
|
|
17544
|
-
],
|
|
17545
17641
|
"activators": [
|
|
17546
17642
|
{
|
|
17547
17643
|
"on": "created",
|
|
@@ -18552,9 +18648,8 @@ var DATA = {
|
|
|
18552
18648
|
"slug": "pain-points",
|
|
18553
18649
|
"type": "kanban",
|
|
18554
18650
|
"scopeProfileSlug": "pain-point",
|
|
18555
|
-
"groupBy": "severity",
|
|
18556
18651
|
"config": {
|
|
18557
|
-
"
|
|
18652
|
+
"groupByField": "severity",
|
|
18558
18653
|
"cardFields": [
|
|
18559
18654
|
"description",
|
|
18560
18655
|
"audience",
|
|
@@ -20606,6 +20701,7 @@ Report the submission's current state as STRICT JSON, using ONLY these vocabular
|
|
|
20606
20701
|
"isPublic": true,
|
|
20607
20702
|
"intent": {
|
|
20608
20703
|
"label": "Team",
|
|
20704
|
+
"primary": "candidate",
|
|
20609
20705
|
"produces": [
|
|
20610
20706
|
{
|
|
20611
20707
|
"profile": "candidate"
|
|
@@ -21173,10 +21269,9 @@ Report the submission's current state as STRICT JSON, using ONLY these vocabular
|
|
|
21173
21269
|
"slug": "reviews",
|
|
21174
21270
|
"type": "kanban",
|
|
21175
21271
|
"scopeProfileSlug": "review",
|
|
21176
|
-
"groupBy": "status",
|
|
21177
21272
|
"description": "Performance reviews across the cycle, by status.",
|
|
21178
21273
|
"config": {
|
|
21179
|
-
"
|
|
21274
|
+
"groupByField": "status",
|
|
21180
21275
|
"cardFields": [
|
|
21181
21276
|
"cycle",
|
|
21182
21277
|
"rating",
|
|
@@ -21985,7 +22080,6 @@ Report the submission's current state as STRICT JSON, using ONLY these vocabular
|
|
|
21985
22080
|
"slug": "workflows",
|
|
21986
22081
|
"type": "kanban",
|
|
21987
22082
|
"scopeProfileSlug": "workflow",
|
|
21988
|
-
"groupBy": "workflow-status",
|
|
21989
22083
|
"description": "Recurring processes on a board by status.",
|
|
21990
22084
|
"config": {
|
|
21991
22085
|
"groupByField": "workflow-status",
|
|
@@ -22370,6 +22464,7 @@ Report the submission's current state as STRICT JSON, using ONLY these vocabular
|
|
|
22370
22464
|
"isPublic": true,
|
|
22371
22465
|
"intent": {
|
|
22372
22466
|
"label": "Legal",
|
|
22467
|
+
"primary": "legal-contract",
|
|
22373
22468
|
"produces": [
|
|
22374
22469
|
{
|
|
22375
22470
|
"profile": "legal-contract"
|
|
@@ -22977,10 +23072,9 @@ Report the submission's current state as STRICT JSON, using ONLY these vocabular
|
|
|
22977
23072
|
"slug": "renewals",
|
|
22978
23073
|
"type": "kanban",
|
|
22979
23074
|
"scopeProfileSlug": "legal-contract",
|
|
22980
|
-
"groupBy": "status",
|
|
22981
23075
|
"description": "Contracts by lifecycle stage \u2014 catch renewals and expirations before they bite.",
|
|
22982
23076
|
"config": {
|
|
22983
|
-
"
|
|
23077
|
+
"groupByField": "status",
|
|
22984
23078
|
"cardFields": [
|
|
22985
23079
|
"counterparty",
|
|
22986
23080
|
"renewalType",
|
|
@@ -22995,10 +23089,9 @@ Report the submission's current state as STRICT JSON, using ONLY these vocabular
|
|
|
22995
23089
|
"slug": "compliance",
|
|
22996
23090
|
"type": "kanban",
|
|
22997
23091
|
"scopeProfileSlug": "compliance-item",
|
|
22998
|
-
"groupBy": "status",
|
|
22999
23092
|
"description": "Obligations by status \u2014 nothing goes overdue.",
|
|
23000
23093
|
"config": {
|
|
23001
|
-
"
|
|
23094
|
+
"groupByField": "status",
|
|
23002
23095
|
"cardFields": [
|
|
23003
23096
|
"obligationType",
|
|
23004
23097
|
"jurisdiction",
|
|
@@ -25773,6 +25866,7 @@ Report the submission's current state as STRICT JSON, using ONLY these vocabular
|
|
|
25773
25866
|
"isPublic": true,
|
|
25774
25867
|
"intent": {
|
|
25775
25868
|
"label": "Marketing",
|
|
25869
|
+
"primary": "campaign",
|
|
25776
25870
|
"produces": [
|
|
25777
25871
|
{
|
|
25778
25872
|
"profile": "campaign"
|
|
@@ -26121,7 +26215,9 @@ Report the submission's current state as STRICT JSON, using ONLY these vocabular
|
|
|
26121
26215
|
"slug": "campaigns",
|
|
26122
26216
|
"type": "kanban",
|
|
26123
26217
|
"scopeProfileSlug": "campaign",
|
|
26124
|
-
"
|
|
26218
|
+
"config": {
|
|
26219
|
+
"groupByField": "campaign-status"
|
|
26220
|
+
},
|
|
26125
26221
|
"description": "Campaign kanban board by status."
|
|
26126
26222
|
},
|
|
26127
26223
|
{
|
|
@@ -26262,13 +26358,8 @@ Report the submission's current state as STRICT JSON, using ONLY these vocabular
|
|
|
26262
26358
|
"description": "Take a campaign from brief to results \u2014 goal, audience, plan, content, launch with a person's approval, and a report on the leads it brought.",
|
|
26263
26359
|
"goalTemplate": "Run this campaign ({campaign}); each stage moves its status. BRIEF: write the campaign's goal as a number, its target audience, channel, budget, start and end dates, from the brand kit and the agency's offers. PLAN: list the pieces to make and the outreach to send, each with a date. MAKE: create each piece as a post at the Idea status in Content OS and each outreach sequence as an outreach campaign, so their own processes produce them. LAUNCH: a person approves the launch; nothing is published, sent or paid for before that. REPORT: after the end date, record the lead count and conversion rate, link every new lead the campaign brought, and write what worked and what to change. Pausing or cancelling is a person's call.",
|
|
26264
26360
|
"subjectProfile": {
|
|
26265
|
-
"profileSlug": "campaign"
|
|
26266
|
-
"statusProperty": "campaign-status"
|
|
26361
|
+
"profileSlug": "campaign"
|
|
26267
26362
|
},
|
|
26268
|
-
"humanOnlyStatuses": [
|
|
26269
|
-
"paused",
|
|
26270
|
-
"cancelled"
|
|
26271
|
-
],
|
|
26272
26363
|
"activators": [
|
|
26273
26364
|
{
|
|
26274
26365
|
"on": "created",
|
|
@@ -26631,10 +26722,9 @@ Report the submission's current state as STRICT JSON, using ONLY these vocabular
|
|
|
26631
26722
|
"slug": "meeting-action-items",
|
|
26632
26723
|
"type": "kanban",
|
|
26633
26724
|
"scopeProfileSlug": "task",
|
|
26634
|
-
"groupBy": "status",
|
|
26635
26725
|
"description": "Tasks generated from meetings, by status.",
|
|
26636
26726
|
"config": {
|
|
26637
|
-
"
|
|
26727
|
+
"groupByField": "status"
|
|
26638
26728
|
}
|
|
26639
26729
|
}
|
|
26640
26730
|
],
|
|
@@ -27583,7 +27673,6 @@ Pick the SINGLE best provider for this need, or none if nothing fits well. Respo
|
|
|
27583
27673
|
"suggestFor": [
|
|
27584
27674
|
"consultant"
|
|
27585
27675
|
],
|
|
27586
|
-
"profile": "engagement",
|
|
27587
27676
|
"views": [
|
|
27588
27677
|
"engagements"
|
|
27589
27678
|
],
|
|
@@ -27629,6 +27718,7 @@ Pick the SINGLE best provider for this need, or none if nothing fits well. Respo
|
|
|
27629
27718
|
},
|
|
27630
27719
|
"intent": {
|
|
27631
27720
|
"label": "Delivery",
|
|
27721
|
+
"primary": "engagement",
|
|
27632
27722
|
"produces": [
|
|
27633
27723
|
{
|
|
27634
27724
|
"profile": "engagement"
|
|
@@ -28044,7 +28134,9 @@ Pick the SINGLE best provider for this need, or none if nothing fits well. Respo
|
|
|
28044
28134
|
"slug": "engagements",
|
|
28045
28135
|
"type": "kanban",
|
|
28046
28136
|
"scopeProfileSlug": "engagement",
|
|
28047
|
-
"
|
|
28137
|
+
"config": {
|
|
28138
|
+
"groupByField": "engagement-status"
|
|
28139
|
+
},
|
|
28048
28140
|
"description": "Client engagements on a board by status."
|
|
28049
28141
|
},
|
|
28050
28142
|
{
|
|
@@ -28052,7 +28144,9 @@ Pick the SINGLE best provider for this need, or none if nothing fits well. Respo
|
|
|
28052
28144
|
"slug": "deliverables",
|
|
28053
28145
|
"type": "kanban",
|
|
28054
28146
|
"scopeProfileSlug": "deliverable",
|
|
28055
|
-
"
|
|
28147
|
+
"config": {
|
|
28148
|
+
"groupByField": "deliverable-status"
|
|
28149
|
+
},
|
|
28056
28150
|
"description": "Deliverables on a board by status."
|
|
28057
28151
|
},
|
|
28058
28152
|
{
|
|
@@ -28062,6 +28156,16 @@ Pick the SINGLE best provider for this need, or none if nothing fits well. Respo
|
|
|
28062
28156
|
"scopeProfileSlug": "contract",
|
|
28063
28157
|
"description": "Contracts with their status, value, and term."
|
|
28064
28158
|
},
|
|
28159
|
+
{
|
|
28160
|
+
"name": "Contract Board",
|
|
28161
|
+
"slug": "contract-board",
|
|
28162
|
+
"type": "kanban",
|
|
28163
|
+
"scopeProfileSlug": "contract",
|
|
28164
|
+
"description": "Contracts by status \u2014 the flow board of Get a contract signed.",
|
|
28165
|
+
"config": {
|
|
28166
|
+
"groupByField": "contract-status"
|
|
28167
|
+
}
|
|
28168
|
+
},
|
|
28065
28169
|
{
|
|
28066
28170
|
"name": "Client Briefings",
|
|
28067
28171
|
"slug": "client-briefings",
|
|
@@ -28695,13 +28799,8 @@ Recent conversation (oldest first, JSON): {{steps.read-comms.output.messages}}`
|
|
|
28695
28799
|
"description": "Run one client engagement from kick-off to close \u2014 scope, contract, deliverables, final review and the closing invoice \u2014 with a person approving the start and the close.",
|
|
28696
28800
|
"goalTemplate": "Run this client engagement ({engagement}); each stage moves its status. KICK-OFF: read the won deal, the proposal and every message with the client; write the scope, owner, start and end dates and value on the engagement; create one deliverable per promised output (each with a due date and an owner); reuse the draft contract already linked to the engagement (the daily client pick-up opens one), and create a draft contract only when none is linked. START: a person confirms the contract is signed and the work may start. DELIVER: track every deliverable to accepted (or cancelled by a person), post a short weekly status in this session's room, and flag any deliverable past its due date. CLOSE: once every deliverable is accepted or cancelled, write the closing review (what was delivered, what slipped, what the client said), create the closing invoice in Finance at the draft status for the engagement value still owed (when the engagement bills per deliverable, subtract every milestone invoice already created for it), and propose the renewal or next engagement. A person approves the close. Pausing or cancelling an engagement is a person's call.",
|
|
28697
28801
|
"subjectProfile": {
|
|
28698
|
-
"profileSlug": "engagement"
|
|
28699
|
-
"statusProperty": "engagement-status"
|
|
28802
|
+
"profileSlug": "engagement"
|
|
28700
28803
|
},
|
|
28701
|
-
"humanOnlyStatuses": [
|
|
28702
|
-
"on-hold",
|
|
28703
|
-
"cancelled"
|
|
28704
|
-
],
|
|
28705
28804
|
"activators": [
|
|
28706
28805
|
{
|
|
28707
28806
|
"on": "created",
|
|
@@ -28835,13 +28934,8 @@ Recent conversation (oldest first, JSON): {{steps.read-comms.output.messages}}`
|
|
|
28835
28934
|
"description": "Take one deliverable from in progress to accepted by the client \u2014 plan, produce, review internally, send, get the client's sign-off, and bill it when the engagement bills per deliverable.",
|
|
28836
28935
|
"goalTemplate": "Deliver this deliverable ({deliverable}); each stage moves its status. PLAN: read the engagement's scope and the client's brief, and write what done looks like for this deliverable. PRODUCE: make it, or break it into tasks for the people who will. REVIEW: check it against what done looks like before anyone outside sees it. SEND: a person approves before it goes to the client; record the date it was delivered. ACCEPTED: a person confirms the client accepted it; if the client asked for changes, go back to producing. Then read the engagement's billing: only when it bills per deliverable, create this deliverable's invoice in Finance at the draft status for its share of the engagement value, linked to the deliverable. When it bills at close, create no invoice; the engagement's close bills it.",
|
|
28837
28936
|
"subjectProfile": {
|
|
28838
|
-
"profileSlug": "deliverable"
|
|
28839
|
-
"statusProperty": "deliverable-status"
|
|
28937
|
+
"profileSlug": "deliverable"
|
|
28840
28938
|
},
|
|
28841
|
-
"humanOnlyStatuses": [
|
|
28842
|
-
"not-started",
|
|
28843
|
-
"cancelled"
|
|
28844
|
-
],
|
|
28845
28939
|
"activators": [
|
|
28846
28940
|
{
|
|
28847
28941
|
"on": "enters_status",
|
|
@@ -28970,13 +29064,8 @@ Recent conversation (oldest first, JSON): {{steps.read-comms.output.messages}}`
|
|
|
28970
29064
|
"description": "Take a client contract from draft to signed and active, with a person approving what is sent and confirming the signature.",
|
|
28971
29065
|
"goalTemplate": "Get this contract ({contract}) signed; each stage moves its status. DRAFT: fill the contract from the engagement's scope, value, dates and the agency's standard terms, and list every term that differs from the standard. SEND: a person approves the contract before it goes to the client. SIGNED: ask the person to `provide` the signed file; their approval confirms both sides signed. ACTIVE: set the start and end dates and link the signed document. Expiry and termination are a person's call.",
|
|
28972
29066
|
"subjectProfile": {
|
|
28973
|
-
"profileSlug": "contract"
|
|
28974
|
-
"statusProperty": "contract-status"
|
|
29067
|
+
"profileSlug": "contract"
|
|
28975
29068
|
},
|
|
28976
|
-
"humanOnlyStatuses": [
|
|
28977
|
-
"expired",
|
|
28978
|
-
"terminated"
|
|
28979
|
-
],
|
|
28980
29069
|
"activators": [
|
|
28981
29070
|
{
|
|
28982
29071
|
"on": "created",
|
|
@@ -29089,6 +29178,7 @@ Recent conversation (oldest first, JSON): {{steps.read-comms.output.messages}}`
|
|
|
29089
29178
|
"isPublic": true,
|
|
29090
29179
|
"intent": {
|
|
29091
29180
|
"label": "Planning",
|
|
29181
|
+
"primary": "sprint",
|
|
29092
29182
|
"produces": [
|
|
29093
29183
|
{
|
|
29094
29184
|
"profile": "sprint"
|
|
@@ -29377,7 +29467,6 @@ Recent conversation (oldest first, JSON): {{steps.read-comms.output.messages}}`
|
|
|
29377
29467
|
"slug": "sprint-board",
|
|
29378
29468
|
"type": "kanban",
|
|
29379
29469
|
"scopeProfileSlug": "task",
|
|
29380
|
-
"groupBy": "task-status",
|
|
29381
29470
|
"description": "Active sprint tasks in Backlog / Todo / In Progress / Review / Done lanes",
|
|
29382
29471
|
"config": {
|
|
29383
29472
|
"groupByField": "task-status",
|
|
@@ -29493,7 +29582,6 @@ Recent conversation (oldest first, JSON): {{steps.read-comms.output.messages}}`
|
|
|
29493
29582
|
"slug": "retrospective-board",
|
|
29494
29583
|
"type": "kanban",
|
|
29495
29584
|
"scopeProfileSlug": "sprint",
|
|
29496
|
-
"groupBy": "sprint-status",
|
|
29497
29585
|
"description": "Sprint retrospectives \u2014 completed sprints with retrospective notes attached as documents",
|
|
29498
29586
|
"config": {
|
|
29499
29587
|
"groupByField": "sprint-status"
|
|
@@ -30355,6 +30443,7 @@ Recent conversation (oldest first, JSON): {{steps.read-comms.output.messages}}`
|
|
|
30355
30443
|
"isPublic": true,
|
|
30356
30444
|
"intent": {
|
|
30357
30445
|
"label": "Research",
|
|
30446
|
+
"primary": "research-finding",
|
|
30358
30447
|
"produces": [
|
|
30359
30448
|
{
|
|
30360
30449
|
"profile": "hypothesis"
|
|
@@ -30385,6 +30474,27 @@ Recent conversation (oldest first, JSON): {{steps.read-comms.output.messages}}`
|
|
|
30385
30474
|
"icon": "help-circle",
|
|
30386
30475
|
"color": "blue",
|
|
30387
30476
|
"description": "An open question driving an investigation \u2014 the thing research answers.",
|
|
30477
|
+
"lifecycle": {
|
|
30478
|
+
"property": "questionStatus",
|
|
30479
|
+
"options": [
|
|
30480
|
+
{
|
|
30481
|
+
"key": "open",
|
|
30482
|
+
"category": "open"
|
|
30483
|
+
},
|
|
30484
|
+
{
|
|
30485
|
+
"key": "exploring",
|
|
30486
|
+
"category": "active"
|
|
30487
|
+
},
|
|
30488
|
+
{
|
|
30489
|
+
"key": "answered",
|
|
30490
|
+
"category": "done"
|
|
30491
|
+
},
|
|
30492
|
+
{
|
|
30493
|
+
"key": "abandoned",
|
|
30494
|
+
"category": "lost"
|
|
30495
|
+
}
|
|
30496
|
+
]
|
|
30497
|
+
},
|
|
30388
30498
|
"properties": [
|
|
30389
30499
|
{
|
|
30390
30500
|
"slug": "questionStatus",
|
|
@@ -30770,10 +30880,9 @@ Recent conversation (oldest first, JSON): {{steps.read-comms.output.messages}}`
|
|
|
30770
30880
|
"slug": "research-hypotheses",
|
|
30771
30881
|
"type": "kanban",
|
|
30772
30882
|
"scopeProfileSlug": "hypothesis",
|
|
30773
|
-
"groupBy": "stance",
|
|
30774
30883
|
"description": "Candidate theories, grouped by where the evidence has left them.",
|
|
30775
30884
|
"config": {
|
|
30776
|
-
"
|
|
30885
|
+
"groupByField": "stance"
|
|
30777
30886
|
}
|
|
30778
30887
|
},
|
|
30779
30888
|
{
|
|
@@ -30897,12 +31006,8 @@ Recent conversation (oldest first, JSON): {{steps.read-comms.output.messages}}`
|
|
|
30897
31006
|
"description": "Investigate a question end-to-end \u2014 gather and rank sources (keeping the rejected ones), weigh hypotheses, emit findings with calibrated confidence, and map them onto the knowledge graph.",
|
|
30898
31007
|
"goalTemplate": "Investigate the question {question} and record the WHOLE investigation, not just the answer; each stage moves the question's status. FRAME: restate the question so it can be answered, and say what evidence would answer it. GATHER: use the source-triage skill to gather sources widely via web search, classify each by type and credibility, and create a `source` entity for every one \u2014 including the ones you rejected, marked rejected with a reason (never silently drop a source). WEIGH: form the candidate answers as `hypothesis` entities that answer the question; link every source that bears on a hypothesis with `supports` or `contradicts`; use the evidence-synthesis skill to emit `research-finding` entities with honestly calibrated confidence, each `supported_by` its sources and `derived_from` this research; use the concept-cartography skill to place the findings onto the knowledge map. ANSWER: create the `research` entity that `investigates` the question and `explored` the sources, write its conclusion and confidence, and set the question to answered. Abandoning a question is a person's call. Every write is proposal-gated and reviewable.",
|
|
30899
31008
|
"subjectProfile": {
|
|
30900
|
-
"profileSlug": "question"
|
|
30901
|
-
"statusProperty": "questionStatus"
|
|
31009
|
+
"profileSlug": "question"
|
|
30902
31010
|
},
|
|
30903
|
-
"humanOnlyStatuses": [
|
|
30904
|
-
"abandoned"
|
|
30905
|
-
],
|
|
30906
31011
|
"activators": [
|
|
30907
31012
|
{
|
|
30908
31013
|
"on": "created",
|
|
@@ -31016,8 +31121,7 @@ Recent conversation (oldest first, JSON): {{steps.read-comms.output.messages}}`
|
|
|
31016
31121
|
"description": "When a finding is validated, propose the posts that would share it, each linked to the finding.",
|
|
31017
31122
|
"goalTemplate": "Turn this validated finding ({finding}) into content. Read the finding, its sources and the research it came from. Propose one to three posts that share it with the audience \u2014 each created at the Idea status, on the pillar it fits, with its hook and the reason it is worth making \u2014 and link each one derived_from the finding. Quote only what the sources support.",
|
|
31018
31123
|
"subjectProfile": {
|
|
31019
|
-
"profileSlug": "research-finding"
|
|
31020
|
-
"statusProperty": "status"
|
|
31124
|
+
"profileSlug": "research-finding"
|
|
31021
31125
|
},
|
|
31022
31126
|
"activators": [
|
|
31023
31127
|
{
|
|
@@ -31225,6 +31329,27 @@ counter-evidence: research that only kept what agreed with it isn't research.
|
|
|
31225
31329
|
"icon": "help-circle",
|
|
31226
31330
|
"color": "blue",
|
|
31227
31331
|
"description": "The open question being decided.",
|
|
31332
|
+
"lifecycle": {
|
|
31333
|
+
"property": "questionStatus",
|
|
31334
|
+
"options": [
|
|
31335
|
+
{
|
|
31336
|
+
"key": "open",
|
|
31337
|
+
"category": "open"
|
|
31338
|
+
},
|
|
31339
|
+
{
|
|
31340
|
+
"key": "exploring",
|
|
31341
|
+
"category": "active"
|
|
31342
|
+
},
|
|
31343
|
+
{
|
|
31344
|
+
"key": "answered",
|
|
31345
|
+
"category": "done"
|
|
31346
|
+
},
|
|
31347
|
+
{
|
|
31348
|
+
"key": "abandoned",
|
|
31349
|
+
"category": "lost"
|
|
31350
|
+
}
|
|
31351
|
+
]
|
|
31352
|
+
},
|
|
31228
31353
|
"properties": [
|
|
31229
31354
|
{
|
|
31230
31355
|
"slug": "questionStatus",
|
|
@@ -31299,6 +31424,27 @@ counter-evidence: research that only kept what agreed with it isn't research.
|
|
|
31299
31424
|
"icon": "scale",
|
|
31300
31425
|
"color": "amber",
|
|
31301
31426
|
"description": "A resolved decision \u2014 the recommendation, why it won, and the alternatives that lost (and why).",
|
|
31427
|
+
"lifecycle": {
|
|
31428
|
+
"property": "decisionStatus",
|
|
31429
|
+
"options": [
|
|
31430
|
+
{
|
|
31431
|
+
"key": "proposed",
|
|
31432
|
+
"category": "open"
|
|
31433
|
+
},
|
|
31434
|
+
{
|
|
31435
|
+
"key": "accepted",
|
|
31436
|
+
"category": "done"
|
|
31437
|
+
},
|
|
31438
|
+
{
|
|
31439
|
+
"key": "superseded",
|
|
31440
|
+
"category": "lost"
|
|
31441
|
+
},
|
|
31442
|
+
{
|
|
31443
|
+
"key": "rejected",
|
|
31444
|
+
"category": "lost"
|
|
31445
|
+
}
|
|
31446
|
+
]
|
|
31447
|
+
},
|
|
31302
31448
|
"properties": [
|
|
31303
31449
|
{
|
|
31304
31450
|
"slug": "summary",
|
|
@@ -31403,13 +31549,8 @@ counter-evidence: research that only kept what agreed with it isn't research.
|
|
|
31403
31549
|
"description": "Put a proposed decision in front of a person with its options and evidence, and record what they chose.",
|
|
31404
31550
|
"goalTemplate": "Bring this decision ({decision}) to a person. Summarise the question it resolves, the options that were considered, the evidence for and against each, and the recommendation, in one screen. A person accepts or rejects it. When a later decision replaces it, the person marks it superseded.",
|
|
31405
31551
|
"subjectProfile": {
|
|
31406
|
-
"profileSlug": "decision"
|
|
31407
|
-
"statusProperty": "decisionStatus"
|
|
31552
|
+
"profileSlug": "decision"
|
|
31408
31553
|
},
|
|
31409
|
-
"humanOnlyStatuses": [
|
|
31410
|
-
"superseded",
|
|
31411
|
-
"rejected"
|
|
31412
|
-
],
|
|
31413
31554
|
"activators": [
|
|
31414
31555
|
{
|
|
31415
31556
|
"on": "created",
|
|
@@ -31497,6 +31638,7 @@ counter-evidence: research that only kept what agreed with it isn't research.
|
|
|
31497
31638
|
"isPublic": true,
|
|
31498
31639
|
"intent": {
|
|
31499
31640
|
"label": "Distribution",
|
|
31641
|
+
"primary": "post",
|
|
31500
31642
|
"produces": [
|
|
31501
31643
|
{
|
|
31502
31644
|
"profile": "post"
|
|
@@ -31881,7 +32023,9 @@ counter-evidence: research that only kept what agreed with it isn't research.
|
|
|
31881
32023
|
"slug": "content-calendar",
|
|
31882
32024
|
"type": "kanban",
|
|
31883
32025
|
"scopeProfileSlug": "post",
|
|
31884
|
-
"
|
|
32026
|
+
"config": {
|
|
32027
|
+
"groupByField": "post-status"
|
|
32028
|
+
},
|
|
31885
32029
|
"description": "Posts on a board by status \u2014 idea to published."
|
|
31886
32030
|
},
|
|
31887
32031
|
{
|
|
@@ -35206,48 +35350,46 @@ function checkProcessContract(tpl, resolveDep, corpus, add) {
|
|
|
35206
35350
|
for (const pb of tpl.playbooks || []) {
|
|
35207
35351
|
const at = `playbooks."${pb.name}"`;
|
|
35208
35352
|
const kind = pb.subjectProfile?.profileSlug;
|
|
35209
|
-
const
|
|
35210
|
-
|
|
35211
|
-
|
|
35212
|
-
|
|
35353
|
+
const raw = pb;
|
|
35354
|
+
const subjectRaw = pb.subjectProfile ?? {};
|
|
35355
|
+
for (const [field, present] of [
|
|
35356
|
+
["subjectProfile.statusProperty", subjectRaw.statusProperty !== void 0],
|
|
35357
|
+
["subjectProfile.onEnd", subjectRaw.onEnd !== void 0],
|
|
35358
|
+
["onEnd", raw.onEnd !== void 0],
|
|
35359
|
+
["humanOnlyStatuses", raw.humanOnlyStatuses !== void 0],
|
|
35360
|
+
["subjectProfile.humanOnlyStatuses", subjectRaw.humanOnlyStatuses !== void 0]
|
|
35361
|
+
])
|
|
35362
|
+
if (present)
|
|
35213
35363
|
add(
|
|
35214
|
-
"playbook-
|
|
35215
|
-
`${at}
|
|
35216
|
-
`playbook "${pb.name}" declares
|
|
35364
|
+
"playbook-field-retired",
|
|
35365
|
+
`${at}.${field}`,
|
|
35366
|
+
`playbook "${pb.name}" declares ${field} \u2014 retired: the process follows the kind's declared lifecycle (a person-only status is \`humanOnly\` on that lifecycle, a status no stage owns is derived, and a session end moves nothing)`
|
|
35217
35367
|
);
|
|
35218
|
-
|
|
35219
|
-
|
|
35220
|
-
|
|
35221
|
-
|
|
35222
|
-
|
|
35223
|
-
|
|
35224
|
-
|
|
35225
|
-
|
|
35226
|
-
|
|
35227
|
-
|
|
35228
|
-
} else if (prop.inputType !== "select" || !Array.isArray(prop.enumValues) || prop.enumValues.length === 0) {
|
|
35229
|
-
add(
|
|
35230
|
-
"playbook-status-property-not-select",
|
|
35231
|
-
`${at}.subjectProfile.statusProperty`,
|
|
35232
|
-
`playbook "${pb.name}" statusProperty "${kind}.${statusProperty}" must be a select property with enumValues (found inputType "${String(prop.inputType)}")`
|
|
35233
|
-
);
|
|
35234
|
-
} else {
|
|
35235
|
-
statuses = new Set(prop.enumValues);
|
|
35236
|
-
}
|
|
35368
|
+
let statuses = null;
|
|
35369
|
+
let lifecycleProperty;
|
|
35370
|
+
let judgeable = hasCorpus(resolveDep);
|
|
35371
|
+
if (kind) {
|
|
35372
|
+
const decls = profileDeclarations(kind, tpl, resolveDep, corpus);
|
|
35373
|
+
judgeable ||= decls.length > 0;
|
|
35374
|
+
const declared = decls.find((p) => p.lifecycle !== void 0)?.lifecycle;
|
|
35375
|
+
if (declared) {
|
|
35376
|
+
lifecycleProperty = declared.property;
|
|
35377
|
+
statuses = new Set(declared.options.map((o) => o.key));
|
|
35237
35378
|
}
|
|
35238
35379
|
}
|
|
35239
35380
|
const checkStatus = (rule, path, value) => {
|
|
35240
|
-
if (
|
|
35381
|
+
if (lifecycleProperty === void 0) {
|
|
35382
|
+
if (!judgeable) return;
|
|
35241
35383
|
add(
|
|
35242
|
-
`${rule}-no-
|
|
35384
|
+
`${rule}-no-lifecycle`,
|
|
35243
35385
|
path,
|
|
35244
|
-
`${path} = "${String(value)}" needs
|
|
35386
|
+
`${path} = "${String(value)}" needs "${String(kind)}" to declare a lifecycle \u2014 a status is a value of the kind's lifecycle property`
|
|
35245
35387
|
);
|
|
35246
35388
|
} else if (statuses && !statuses.has(value)) {
|
|
35247
35389
|
add(
|
|
35248
35390
|
rule,
|
|
35249
35391
|
path,
|
|
35250
|
-
`${path} = "${String(value)}" is not a value of ${kind}.${
|
|
35392
|
+
`${path} = "${String(value)}" is not a value of ${kind}.${lifecycleProperty} (${[...statuses].join(", ")})`
|
|
35251
35393
|
);
|
|
35252
35394
|
}
|
|
35253
35395
|
};
|
|
@@ -35292,9 +35434,6 @@ function checkProcessContract(tpl, resolveDep, corpus, add) {
|
|
|
35292
35434
|
add("activator-status-unexpected", `${ap}.status`, `${ap}.status is not read when on = "${a.on}"`);
|
|
35293
35435
|
}
|
|
35294
35436
|
});
|
|
35295
|
-
(pb.humanOnlyStatuses ?? []).forEach(
|
|
35296
|
-
(s, i) => checkStatus("human-only-status-unknown", `${at}.humanOnlyStatuses[${i}]`, s)
|
|
35297
|
-
);
|
|
35298
35437
|
const outputs = [
|
|
35299
35438
|
...(pb.expectedOutputs ?? []).map((o, i) => ({
|
|
35300
35439
|
at: `${at}.expectedOutputs[${i}]`,
|
|
@@ -35477,18 +35616,6 @@ function checkLifecycleDeclarations(tpl, resolveDep, corpus, add) {
|
|
|
35477
35616
|
`"${profile.slug}" declares "${other.property}" here ${profile.lifecycle ? "with a different lifecycle than" : "without the lifecycle"} another template declares \u2014 the pod keeps whichever installs first, so every declaration must be identical`
|
|
35478
35617
|
);
|
|
35479
35618
|
}
|
|
35480
|
-
for (const pb of tpl.playbooks ?? []) {
|
|
35481
|
-
const kind = pb.subjectProfile?.profileSlug;
|
|
35482
|
-
const statusProperty = pb.subjectProfile?.statusProperty;
|
|
35483
|
-
if (!kind || statusProperty === void 0) continue;
|
|
35484
|
-
const lc = profileDeclarations(kind, tpl, resolveDep, corpus).map((p) => p.lifecycle).find((l) => l !== void 0);
|
|
35485
|
-
if (lc && lc.property !== statusProperty)
|
|
35486
|
-
add(
|
|
35487
|
-
"lifecycle-status-property",
|
|
35488
|
-
`playbooks."${pb.name}".subjectProfile.statusProperty`,
|
|
35489
|
-
`playbook "${pb.name}" moves "${kind}.${statusProperty}" but "${kind}"'s lifecycle is "${lc.property}" \u2014 a kind has ONE lifecycle`
|
|
35490
|
-
);
|
|
35491
|
-
}
|
|
35492
35619
|
}
|
|
35493
35620
|
function checkPresentationDeclarations(tpl, resolveDep, corpus, add) {
|
|
35494
35621
|
for (const profile of tpl.profiles ?? []) {
|
|
@@ -35545,6 +35672,36 @@ function checkPresentationDeclarations(tpl, resolveDep, corpus, add) {
|
|
|
35545
35672
|
);
|
|
35546
35673
|
}
|
|
35547
35674
|
}
|
|
35675
|
+
function checkKanbanGrouping(tpl, resolveDep, corpus, add) {
|
|
35676
|
+
for (const v of tpl.views ?? []) {
|
|
35677
|
+
if (String(v.type || "").toLowerCase() !== "kanban") continue;
|
|
35678
|
+
const at = `views."${v.name}"`;
|
|
35679
|
+
const dead = v.groupBy !== void 0 ? "groupBy" : v.config?.groupBy !== void 0 ? "config.groupBy" : null;
|
|
35680
|
+
if (dead)
|
|
35681
|
+
add(
|
|
35682
|
+
"view-kanban-group-dead",
|
|
35683
|
+
`${at}.${dead}`,
|
|
35684
|
+
`kanban "${v.name}" declares ${dead} \u2014 the board never reads it (it groups by status); write config.groupByField instead`
|
|
35685
|
+
);
|
|
35686
|
+
const field = v.config?.groupByField;
|
|
35687
|
+
if (typeof field !== "string" || !field.trim()) {
|
|
35688
|
+
add("view-kanban-group-missing", `${at}.config.groupByField`, `kanban "${v.name}" names no config.groupByField \u2014 its columns would be the values of "status"`);
|
|
35689
|
+
continue;
|
|
35690
|
+
}
|
|
35691
|
+
const kind = v.scopeProfileSlug;
|
|
35692
|
+
if (!kind) continue;
|
|
35693
|
+
const slug = field.includes(".") ? field.split(".").pop() : field;
|
|
35694
|
+
const decls = profileDeclarations(kind, tpl, resolveDep, corpus);
|
|
35695
|
+
if (decls.length === 0) continue;
|
|
35696
|
+
const prop = decls.flatMap((p) => p.properties ?? []).find((p) => p.slug === slug);
|
|
35697
|
+
if (!prop || prop.inputType !== "select" || !Array.isArray(prop.enumValues) || prop.enumValues.length === 0)
|
|
35698
|
+
add(
|
|
35699
|
+
"view-kanban-group-unknown",
|
|
35700
|
+
`${at}.config.groupByField`,
|
|
35701
|
+
`kanban "${v.name}" groups by "${slug}", which is not a select property of "${kind}" with enumValues`
|
|
35702
|
+
);
|
|
35703
|
+
}
|
|
35704
|
+
}
|
|
35548
35705
|
function checkIntent(tpl, resolveDep, add) {
|
|
35549
35706
|
const intent = tpl.meta?.intent;
|
|
35550
35707
|
if (intent === void 0) return;
|
|
@@ -35558,16 +35715,37 @@ function checkIntent(tpl, resolveDep, add) {
|
|
|
35558
35715
|
return;
|
|
35559
35716
|
}
|
|
35560
35717
|
if (!intent || typeof intent !== "object" || Array.isArray(intent)) {
|
|
35561
|
-
add("intent-shape", "meta.intent", "meta.intent must be { label, produces?: [{ profile }] }");
|
|
35718
|
+
add("intent-shape", "meta.intent", "meta.intent must be { label, primary?, produces?: [{ profile }] }");
|
|
35562
35719
|
return;
|
|
35563
35720
|
}
|
|
35564
|
-
const { label, produces } = intent;
|
|
35721
|
+
const { label, produces, primary } = intent;
|
|
35565
35722
|
if (typeof label !== "string" || !label.trim() || label.trim().length > INTENT_LABEL_MAX)
|
|
35566
35723
|
add(
|
|
35567
35724
|
"intent-label",
|
|
35568
35725
|
"meta.intent.label",
|
|
35569
35726
|
`meta.intent.label must be a non-empty string of at most ${INTENT_LABEL_MAX} characters (got ${JSON.stringify(label)})`
|
|
35570
35727
|
);
|
|
35728
|
+
const producedSlugs = Array.isArray(produces) ? produces.map((e) => e?.profile).filter((s) => typeof s === "string") : [];
|
|
35729
|
+
if (primary !== void 0 && (typeof primary !== "string" || !primary.trim()))
|
|
35730
|
+
add("intent-shape", "meta.intent.primary", `meta.intent.primary must be a profile slug (got ${JSON.stringify(primary)})`);
|
|
35731
|
+
else if (primary === void 0 && producedSlugs.length > 0)
|
|
35732
|
+
add(
|
|
35733
|
+
"intent-primary-missing",
|
|
35734
|
+
"meta.intent.primary",
|
|
35735
|
+
`"${tpl.meta.slug}" produces ${producedSlugs.join(", ")} but names no meta.intent.primary \u2014 name the ONE output its main flow produces`
|
|
35736
|
+
);
|
|
35737
|
+
else if (primary !== void 0 && producedSlugs.length === 0)
|
|
35738
|
+
add(
|
|
35739
|
+
"intent-primary-library",
|
|
35740
|
+
"meta.intent.primary",
|
|
35741
|
+
`meta.intent.primary "${primary}" on a library \u2014 a space with no produces has no flow; list "${primary}" in produces or drop primary`
|
|
35742
|
+
);
|
|
35743
|
+
else if (primary !== void 0 && !producedSlugs.includes(primary))
|
|
35744
|
+
add(
|
|
35745
|
+
"intent-primary-not-produced",
|
|
35746
|
+
"meta.intent.primary",
|
|
35747
|
+
`meta.intent.primary "${primary}" is not in meta.intent.produces (${producedSlugs.join(", ")}) \u2014 the primary is one of what the space produces`
|
|
35748
|
+
);
|
|
35571
35749
|
if (produces === void 0) return;
|
|
35572
35750
|
if (!Array.isArray(produces)) {
|
|
35573
35751
|
add("intent-shape", "meta.intent.produces", "meta.intent.produces must be a list of { profile }");
|
|
@@ -35616,12 +35794,20 @@ function checkStart(tpl, resolveDep, corpus, add) {
|
|
|
35616
35794
|
const strList = (v) => Array.isArray(v) && v.every(isStr);
|
|
35617
35795
|
const st = raw;
|
|
35618
35796
|
const fs = st?.firstSession;
|
|
35619
|
-
const shapeOk = !!st && typeof st === "object" && !Array.isArray(st) && Number.isInteger(st.order) && st.order > 0 && isStr(st.label) && isStr(st.promise) && strList(st.suggestFor) && isStr(st.profile) && strList(st.views) && strList(st.starters) && Array.isArray(st.example) && st.example.every((e) => e && isStr(e.title) && (e.values === void 0 || typeof e.values === "object" && e.values !== null && !Array.isArray(e.values))) && !!fs && isStr(fs.title) && isStr(fs.goal);
|
|
35797
|
+
const shapeOk = !!st && typeof st === "object" && !Array.isArray(st) && Number.isInteger(st.order) && st.order > 0 && isStr(st.label) && isStr(st.promise) && strList(st.suggestFor) && (st.profile === void 0 || isStr(st.profile)) && strList(st.views) && strList(st.starters) && Array.isArray(st.example) && st.example.every((e) => e && isStr(e.title) && (e.values === void 0 || typeof e.values === "object" && e.values !== null && !Array.isArray(e.values))) && !!fs && isStr(fs.title) && isStr(fs.goal);
|
|
35620
35798
|
if (!shapeOk) {
|
|
35621
|
-
add("start-shape", "meta.start", "meta.start must be { order (int \u2265 1), label, promise, suggestFor[], profile
|
|
35799
|
+
add("start-shape", "meta.start", "meta.start must be { order (int \u2265 1), label, promise, suggestFor[], profile?, views[], example[{ title, values? }], firstSession{ title, goal }, starters[] }");
|
|
35622
35800
|
return;
|
|
35623
35801
|
}
|
|
35624
35802
|
const start = st;
|
|
35803
|
+
const primary = tpl.meta.intent?.primary;
|
|
35804
|
+
if (start.profile !== void 0 && start.profile === primary)
|
|
35805
|
+
add("start-profile-redundant", "meta.start.profile", `meta.start.profile repeats meta.intent.primary "${primary}" \u2014 omit it (the start defaults to the primary output)`);
|
|
35806
|
+
const profile = startProfileOf(tpl.meta);
|
|
35807
|
+
if (!profile) {
|
|
35808
|
+
add("start-profile-missing", "meta.profile", `meta.start names no kind and "${tpl.meta.slug}" declares no meta.intent.primary to default to`);
|
|
35809
|
+
return;
|
|
35810
|
+
}
|
|
35625
35811
|
if (start.label.trim().length > INTENT_LABEL_MAX)
|
|
35626
35812
|
add("start-label", "meta.start.label", `meta.start.label must be at most ${INTENT_LABEL_MAX} characters (got ${JSON.stringify(start.label)})`);
|
|
35627
35813
|
const [exMin, exMax] = START_EXAMPLE_RANGE;
|
|
@@ -35640,24 +35826,24 @@ function checkStart(tpl, resolveDep, corpus, add) {
|
|
|
35640
35826
|
return;
|
|
35641
35827
|
}
|
|
35642
35828
|
const scope = [view.scopeProfileSlug, ...view.scopeProfileSlugs ?? []].filter(Boolean);
|
|
35643
|
-
if (!scope.includes(
|
|
35644
|
-
add("start-views-scope", at, `view "${slug}" is not scoped to "${
|
|
35829
|
+
if (!scope.includes(profile))
|
|
35830
|
+
add("start-views-scope", at, `view "${slug}" is not scoped to "${profile}" (scope: ${scope.join(", ") || "none"})`);
|
|
35645
35831
|
});
|
|
35646
35832
|
const closure = [...collectDependencyClosure(tpl, resolveDep)];
|
|
35647
35833
|
const sources = [tpl, ...closure.map(resolveDep).filter((t) => !!t)];
|
|
35648
|
-
const decls = sources.flatMap((t) => (t.profiles ?? []).filter((p) => p.slug ===
|
|
35834
|
+
const decls = sources.flatMap((t) => (t.profiles ?? []).filter((p) => p.slug === profile));
|
|
35649
35835
|
if (decls.length === 0) {
|
|
35650
35836
|
if (!closure.some((d) => !resolveDep(d)))
|
|
35651
|
-
add("start-profile-unknown", "meta.
|
|
35837
|
+
add("start-profile-unknown", "meta.profile", `"${profile}" is neither declared by "${tpl.meta.slug}" nor by a template it depends on`);
|
|
35652
35838
|
} else {
|
|
35653
35839
|
const props = new Map(decls.flatMap((p) => p.properties ?? []).map((p) => [p.slug, p]));
|
|
35654
35840
|
start.example.forEach((row, i) => {
|
|
35655
35841
|
for (const [key, value] of Object.entries(row.values ?? {})) {
|
|
35656
35842
|
const at = `meta.start.example[${i}].values.${key}`;
|
|
35657
35843
|
const prop = props.get(key);
|
|
35658
|
-
if (!prop) add("start-example-property", at, `"${key}" is not a property of "${
|
|
35844
|
+
if (!prop) add("start-example-property", at, `"${key}" is not a property of "${profile}"`);
|
|
35659
35845
|
else if (prop.enumValues?.length && !prop.enumValues.includes(value))
|
|
35660
|
-
add("start-example-enum", at, `${JSON.stringify(value)} is not one of ${
|
|
35846
|
+
add("start-example-enum", at, `${JSON.stringify(value)} is not one of ${profile}.${key}: ${prop.enumValues.join(", ")}`);
|
|
35661
35847
|
}
|
|
35662
35848
|
});
|
|
35663
35849
|
}
|
|
@@ -36281,6 +36467,7 @@ function validateTemplate(authored, authoredOpts = {}) {
|
|
|
36281
36467
|
checkSharedRoleReferences(tpl, resolveDepFrom(opts), add);
|
|
36282
36468
|
checkStageDomains(tpl, resolveDepFrom(opts), add);
|
|
36283
36469
|
checkProcessContract(tpl, resolveDepFrom(opts), opts.corpus, add);
|
|
36470
|
+
checkKanbanGrouping(tpl, resolveDepFrom(opts), opts.corpus, add);
|
|
36284
36471
|
checkLifecycleDeclarations(tpl, resolveDepFrom(opts), opts.corpus, add);
|
|
36285
36472
|
checkPresentationDeclarations(tpl, resolveDepFrom(opts), opts.corpus, add);
|
|
36286
36473
|
checkIntent(tpl, resolveDepFrom(opts), add);
|
|
@@ -36651,7 +36838,9 @@ function toPackageDefinition(slugOrYaml) {
|
|
|
36651
36838
|
// `taskIntents` note on the PackageDefinition type above.
|
|
36652
36839
|
taskIntents: tpl.taskIntents,
|
|
36653
36840
|
...tpl.meta.intent ? { intent: tpl.meta.intent } : {},
|
|
36654
|
-
|
|
36841
|
+
// The start's kind defaults to the primary output (one source); the wire
|
|
36842
|
+
// always carries it, so the pod, the CP and the landing read `start.profile`.
|
|
36843
|
+
...tpl.meta.start ? { start: { ...tpl.meta.start, profile: startProfileOf(tpl.meta) } } : {},
|
|
36655
36844
|
sourceRoles: ws.sourceRoles,
|
|
36656
36845
|
defaultSources: ws.defaultSources,
|
|
36657
36846
|
profiles: tpl.profiles,
|