@synap-core/workspace-templates 0.30.0 → 0.32.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 +13 -5
- package/agents/hermes.agent.yaml +3 -0
- 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/wire.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { g as PackageDefinitionOutput, X as WorkspaceDefinitionInput } from './define-
|
|
1
|
+
import { g as PackageDefinitionOutput, X as WorkspaceDefinitionInput } from './define-DHDi8C33.js';
|
|
2
2
|
import '@synap-core/types/presentation';
|
|
3
3
|
import '@synap-core/types/space-brief';
|
|
4
4
|
import '@synap-core/types/capability-intents';
|
package/package.json
CHANGED
package/src/brand-library.yaml
CHANGED
|
@@ -1171,8 +1171,6 @@ playbooks:
|
|
|
1171
1171
|
Deprecating an asset is a person's call.
|
|
1172
1172
|
subjectProfile:
|
|
1173
1173
|
profileSlug: brand-asset
|
|
1174
|
-
statusProperty: asset-status
|
|
1175
|
-
humanOnlyStatuses: [deprecated]
|
|
1176
1174
|
params:
|
|
1177
1175
|
- { name: asset, type: entity, label: Brand asset, required: true }
|
|
1178
1176
|
executor: is-agent
|
|
@@ -1212,8 +1210,6 @@ playbooks:
|
|
|
1212
1210
|
is checked against. Retiring a guide is a person's call.
|
|
1213
1211
|
subjectProfile:
|
|
1214
1212
|
profileSlug: brand-voice-guide
|
|
1215
|
-
statusProperty: voice-status
|
|
1216
|
-
humanOnlyStatuses: [deprecated]
|
|
1217
1213
|
params:
|
|
1218
1214
|
- { name: guide, type: entity, label: Brand voice guide, required: true }
|
|
1219
1215
|
executor: is-agent
|
|
@@ -1249,8 +1245,6 @@ playbooks:
|
|
|
1249
1245
|
guideline is a person's call.
|
|
1250
1246
|
subjectProfile:
|
|
1251
1247
|
profileSlug: content-guideline
|
|
1252
|
-
statusProperty: guideline-status
|
|
1253
|
-
humanOnlyStatuses: [deprecated]
|
|
1254
1248
|
params:
|
|
1255
1249
|
- { name: guideline, type: entity, label: Content guideline, required: true }
|
|
1256
1250
|
executor: is-agent
|
|
@@ -45,9 +45,8 @@ const INTRINSIC_COLUMNS = new Set(["title"]);
|
|
|
45
45
|
type View = {
|
|
46
46
|
name: string;
|
|
47
47
|
scopeProfileSlug?: string;
|
|
48
|
-
groupBy?: string;
|
|
49
48
|
sortBy?: string;
|
|
50
|
-
config?: { columns?: unknown[] };
|
|
49
|
+
config?: { columns?: unknown[]; groupByField?: string };
|
|
51
50
|
};
|
|
52
51
|
|
|
53
52
|
const def = toWorkspaceDefinition("builder-workspace");
|
|
@@ -70,7 +69,7 @@ describe("builder-workspace — dev-dashboard fold (R7)", () => {
|
|
|
70
69
|
expect(props.size, `${name} scopes "${scope}" which declares no properties`).toBeGreaterThan(0);
|
|
71
70
|
|
|
72
71
|
const named = [
|
|
73
|
-
view!.
|
|
72
|
+
view!.config?.groupByField,
|
|
74
73
|
view!.sortBy,
|
|
75
74
|
...((view!.config?.columns as string[] | undefined) ?? []),
|
|
76
75
|
].filter((s): s is string => typeof s === "string");
|
|
@@ -35,16 +35,18 @@ describe("builder dev playbooks", () => {
|
|
|
35
35
|
expect(pb?.subjectProfile?.activators).toBeUndefined();
|
|
36
36
|
});
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
38
|
+
// One model decision 4 (7 Nov 2026): the process follows the kind's declared
|
|
39
|
+
// lifecycle — no statusProperty, and no end move (decision of 6 Nov).
|
|
40
|
+
it.each(DEV_PLAYBOOKS)("%s follows its kind's lifecycle and declares no end move", (name) => {
|
|
41
|
+
const pb = find(name) as Record<string, any> | undefined;
|
|
42
|
+
expect(pb?.subjectProfile?.statusProperty).toBeUndefined();
|
|
43
|
+
expect(pb?.subjectProfile?.onEnd).toBeUndefined();
|
|
44
|
+
expect(pb?.humanOnlyStatuses).toBeUndefined();
|
|
42
45
|
});
|
|
43
46
|
|
|
44
47
|
it("Fix a bug works the core `issue` kind through its declared lifecycle", () => {
|
|
45
48
|
const pb = find("Fix a bug");
|
|
46
49
|
expect(pb?.subjectProfile?.profileSlug).toBe("issue");
|
|
47
|
-
expect(pb?.subjectProfile?.statusProperty).toBe("issueStatus");
|
|
48
50
|
const issue = (getWorkspaceTemplate("builder-workspace")?.profiles ?? []).find((p) => p.slug === "issue") as
|
|
49
51
|
| { lifecycle?: { property: string; options: { key: string; category: string }[] } }
|
|
50
52
|
| undefined;
|
package/src/builder.yaml
CHANGED
|
@@ -46,6 +46,8 @@ meta:
|
|
|
46
46
|
# What this space PRODUCES (homes): the purpose word, then its kinds/roles.
|
|
47
47
|
intent:
|
|
48
48
|
label: Product
|
|
49
|
+
# The ONE primary output (One model decision 3): the feature is what the dev loop ships (Build a change works one).
|
|
50
|
+
primary: devplane_feature
|
|
49
51
|
produces:
|
|
50
52
|
- profile: devplane_feature
|
|
51
53
|
- profile: devplane_story
|
|
@@ -720,6 +722,12 @@ profiles:
|
|
|
720
722
|
icon: alert-triangle
|
|
721
723
|
color: red
|
|
722
724
|
description: A production incident with severity, status, and root cause tracking
|
|
725
|
+
lifecycle:
|
|
726
|
+
property: incidentStatus
|
|
727
|
+
options:
|
|
728
|
+
- { key: open, category: open }
|
|
729
|
+
- { key: investigating, category: active }
|
|
730
|
+
- { key: resolved, category: done }
|
|
723
731
|
properties:
|
|
724
732
|
- slug: severity
|
|
725
733
|
label: Severity
|
|
@@ -1033,10 +1041,10 @@ profiles:
|
|
|
1033
1041
|
# `issue` is a CORE kind: the pod seeds it on every pod (ensure-system-profiles.ts,
|
|
1034
1042
|
# "an open bug or problem" → issue) with its ONE lifecycle `issueStatus`
|
|
1035
1043
|
# (ensure-system-lifecycles.ts). Declared here to match the seed exactly, the way
|
|
1036
|
-
# research-base declares `research`, so the Fix a bug playbook
|
|
1037
|
-
#
|
|
1044
|
+
# research-base declares `research`, so the Fix a bug playbook follows its
|
|
1045
|
+
# lifecycle. Pod-local extras (severity, night-shift-run) are not core: not here.
|
|
1038
1046
|
# ONE deviation from the seed: `wont_fix` is humanOnly (a person decides an issue
|
|
1039
|
-
# is not worth fixing)
|
|
1047
|
+
# is not worth fixing).
|
|
1040
1048
|
- slug: issue
|
|
1041
1049
|
displayName: Issue
|
|
1042
1050
|
icon: bug
|
|
@@ -1297,6 +1305,13 @@ profiles:
|
|
|
1297
1305
|
icon: workflow
|
|
1298
1306
|
color: blue
|
|
1299
1307
|
description: A user flow or business process — a sequence of steps through system components
|
|
1308
|
+
lifecycle:
|
|
1309
|
+
property: workflowStatus
|
|
1310
|
+
options:
|
|
1311
|
+
- { key: documented, category: open }
|
|
1312
|
+
- { key: in-progress, category: active }
|
|
1313
|
+
- { key: needs-review, category: active }
|
|
1314
|
+
- { key: complete, category: done }
|
|
1300
1315
|
properties:
|
|
1301
1316
|
- slug: workflowStatus
|
|
1302
1317
|
label: Status
|
|
@@ -1517,9 +1532,40 @@ views:
|
|
|
1517
1532
|
slug: task-board
|
|
1518
1533
|
type: kanban
|
|
1519
1534
|
scopeProfileSlug: task
|
|
1520
|
-
|
|
1535
|
+
config:
|
|
1536
|
+
groupByField: task-status
|
|
1521
1537
|
defaultView: true
|
|
1522
1538
|
description: "All delegated tasks by status: Backlog → Queued → Running → Review → Done."
|
|
1539
|
+
# The flow boards (One model §8 item 3): each produced kind on a board grouped
|
|
1540
|
+
# by its lifecycle — columns are its states, a drag is the governed status move.
|
|
1541
|
+
- name: Feature Board
|
|
1542
|
+
slug: feature-board
|
|
1543
|
+
type: kanban
|
|
1544
|
+
scopeProfileSlug: devplane_feature
|
|
1545
|
+
description: Features by stage, Idea to Shipped.
|
|
1546
|
+
config:
|
|
1547
|
+
groupByField: featureStatus
|
|
1548
|
+
- name: Story Board
|
|
1549
|
+
slug: story-board
|
|
1550
|
+
type: kanban
|
|
1551
|
+
scopeProfileSlug: devplane_story
|
|
1552
|
+
description: User stories by status, Draft to Done.
|
|
1553
|
+
config:
|
|
1554
|
+
groupByField: storyStatus
|
|
1555
|
+
- name: Issue Board
|
|
1556
|
+
slug: issue-board
|
|
1557
|
+
type: kanban
|
|
1558
|
+
scopeProfileSlug: issue
|
|
1559
|
+
description: Bugs and problems by status, Open to Fixed.
|
|
1560
|
+
config:
|
|
1561
|
+
groupByField: issueStatus
|
|
1562
|
+
- name: Design Board
|
|
1563
|
+
slug: design-board
|
|
1564
|
+
type: kanban
|
|
1565
|
+
scopeProfileSlug: ui-design
|
|
1566
|
+
description: Designs by status, from draft to implemented.
|
|
1567
|
+
config:
|
|
1568
|
+
groupByField: design-status
|
|
1523
1569
|
- name: Deployments
|
|
1524
1570
|
type: calendar
|
|
1525
1571
|
scopeProfileSlug: devplane_deployment
|
|
@@ -1547,13 +1593,15 @@ views:
|
|
|
1547
1593
|
slug: service-health
|
|
1548
1594
|
type: kanban
|
|
1549
1595
|
scopeProfileSlug: devplane_service
|
|
1550
|
-
|
|
1596
|
+
config:
|
|
1597
|
+
groupByField: serviceStatus
|
|
1551
1598
|
description: Every service by health — healthy, degraded, down, maintenance.
|
|
1552
1599
|
- name: Services by Type
|
|
1553
1600
|
slug: services-by-type
|
|
1554
1601
|
type: kanban
|
|
1555
1602
|
scopeProfileSlug: devplane_service
|
|
1556
|
-
|
|
1603
|
+
config:
|
|
1604
|
+
groupByField: serviceType
|
|
1557
1605
|
description: Services grouped by what they are — API, worker, cron, database, cache, queue.
|
|
1558
1606
|
- name: Deploy Timeline
|
|
1559
1607
|
slug: deploy-timeline
|
|
@@ -1688,10 +1736,8 @@ playbooks:
|
|
|
1688
1736
|
(update_entity with expectedStatus); never invent a status.
|
|
1689
1737
|
subjectProfile:
|
|
1690
1738
|
profileSlug: ui-design
|
|
1691
|
-
statusProperty: design-status
|
|
1692
1739
|
# No stage owns these: `approved` and `archived` are a person's call
|
|
1693
1740
|
# (humanOnly on the lifecycle).
|
|
1694
|
-
humanOnlyStatuses: [approved, archived]
|
|
1695
1741
|
activators:
|
|
1696
1742
|
- { on: manual, mode: propose }
|
|
1697
1743
|
params:
|
|
@@ -1999,8 +2045,7 @@ playbooks:
|
|
|
1999
2045
|
# THE DEV LOOP AS STORED CONFIG (plan v5 T2, 2026-10-27). One change = one
|
|
2000
2046
|
# session on its feature: shaped → building → PR opened → checks green →
|
|
2001
2047
|
# merged → deployed to lab → dogfooded → criteria verified. It FOLLOWS the
|
|
2002
|
-
# feature's declared lifecycle (`featureStatus`,
|
|
2003
|
-
# second status): Shape writes spec, Build in_progress, PR opened in_review,
|
|
2048
|
+
# feature's declared lifecycle (`featureStatus`, no second status): Shape writes spec, Build in_progress, PR opened in_review,
|
|
2004
2049
|
# Verify done (an agent's move into done proposes). The stages after PR
|
|
2005
2050
|
# opened carry no status — the feature stays In review until it ships.
|
|
2006
2051
|
#
|
|
@@ -2045,10 +2090,8 @@ playbooks:
|
|
|
2045
2090
|
NEEDS-DOGFOOD.
|
|
2046
2091
|
subjectProfile:
|
|
2047
2092
|
profileSlug: devplane_feature
|
|
2048
|
-
statusProperty: featureStatus
|
|
2049
2093
|
# No stage owns these: Idea is before the change is shaped, Dropped is a
|
|
2050
2094
|
# person's call (humanOnly on the lifecycle).
|
|
2051
|
-
humanOnlyStatuses: [backlog, cancelled]
|
|
2052
2095
|
activators:
|
|
2053
2096
|
- { on: enters_status, status: spec, mode: propose, maxPendingApprovals: 5 }
|
|
2054
2097
|
- { on: manual, mode: propose }
|
|
@@ -2192,24 +2235,18 @@ playbooks:
|
|
|
2192
2235
|
# THE SIX DEV PLAYBOOKS (decision d05, founder instruction 2026-11-05): Build a
|
|
2193
2236
|
# change, Fix a bug, Investigate, Review a change, Maintain, Fix main red.
|
|
2194
2237
|
# Mirrored field by field from the six draft playbook proposals filed on the Builder
|
|
2195
|
-
# pod (stored shape
|
|
2238
|
+
# pod (stored shape). No end moves (decision of 6 Nov 2026): a cancelled or failed
|
|
2239
|
+
# session leaves its subject where it is.
|
|
2196
2240
|
# ALL `status: draft` and NO activators: a pod keeps its own draft/active
|
|
2197
2241
|
# choice on later template updates, and a person turns each on.
|
|
2198
2242
|
|
|
2199
2243
|
# --- Build a change ---
|
|
2200
2244
|
- name: Build a change
|
|
2201
|
-
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)
|
|
2245
|
+
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.'
|
|
2202
2246
|
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.'
|
|
2203
2247
|
scope: session
|
|
2204
2248
|
subjectProfile:
|
|
2205
|
-
onEnd:
|
|
2206
|
-
failed: spec
|
|
2207
|
-
cancelled: spec
|
|
2208
2249
|
profileSlug: devplane_feature
|
|
2209
|
-
statusProperty: featureStatus
|
|
2210
|
-
humanOnlyStatuses:
|
|
2211
|
-
- backlog
|
|
2212
|
-
- cancelled
|
|
2213
2250
|
params:
|
|
2214
2251
|
- name: feature
|
|
2215
2252
|
type: entity
|
|
@@ -2364,21 +2401,11 @@ playbooks:
|
|
|
2364
2401
|
|
|
2365
2402
|
# --- Fix a bug ---
|
|
2366
2403
|
- name: Fix a bug
|
|
2367
|
-
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
|
|
2404
|
+
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.'
|
|
2368
2405
|
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.'
|
|
2369
2406
|
scope: session
|
|
2370
2407
|
subjectProfile:
|
|
2371
|
-
onEnd:
|
|
2372
|
-
failed: open
|
|
2373
|
-
cancelled: open
|
|
2374
2408
|
profileSlug: issue
|
|
2375
|
-
statusProperty: issueStatus
|
|
2376
|
-
# Added to the pod draft (it has none; the catalog's lifecycle-coverage check
|
|
2377
|
-
# needs every option owned): `open` is where an issue starts and a failed or
|
|
2378
|
-
# cancelled session returns it (onEnd), `wont_fix` is a person's call.
|
|
2379
|
-
humanOnlyStatuses:
|
|
2380
|
-
- open
|
|
2381
|
-
- wont_fix
|
|
2382
2409
|
params:
|
|
2383
2410
|
- name: issue
|
|
2384
2411
|
type: entity
|
|
@@ -2926,7 +2953,7 @@ playbooks:
|
|
|
2926
2953
|
|
|
2927
2954
|
# Founder's pod playbook 18abd1ea, as it runs since 6 Nov 2026: no onEnd
|
|
2928
2955
|
# (decision f06dc507: a cancelled or failed session leaves its finding where it
|
|
2929
|
-
# is)
|
|
2956
|
+
# is); it follows the kind's lifecycle (backend#853).
|
|
2930
2957
|
# The two pod automations that move it (PR merged → merged, deployed →
|
|
2931
2958
|
# verified) are not shipped: their session_update step scopes on
|
|
2932
2959
|
# `whenPlaybookId`, a pod uuid, and there is no catalog-stable reference yet.
|
|
@@ -2936,15 +2963,6 @@ playbooks:
|
|
|
2936
2963
|
scope: session
|
|
2937
2964
|
subjectProfile:
|
|
2938
2965
|
profileSlug: finding
|
|
2939
|
-
statusProperty: status
|
|
2940
|
-
# The options no stage owns (catalog lifecycle coverage): `open` is where a
|
|
2941
|
-
# finding starts, `wont_fix` and `superseded` are a call made outside the fix.
|
|
2942
|
-
# With no onEnd nothing moves a finding into them, and a finding in them has
|
|
2943
|
-
# no stage to bind a session to anyway.
|
|
2944
|
-
humanOnlyStatuses:
|
|
2945
|
-
- open
|
|
2946
|
-
- wont_fix
|
|
2947
|
-
- superseded
|
|
2948
2966
|
params: []
|
|
2949
2967
|
executor: is-agent
|
|
2950
2968
|
status: active
|
package/src/business-model.yaml
CHANGED
|
@@ -76,6 +76,15 @@ profiles:
|
|
|
76
76
|
displayName: Question
|
|
77
77
|
icon: help-circle
|
|
78
78
|
profileKind: kind
|
|
79
|
+
# The pod seeds this lifecycle on every pod (ensure-system-lifecycles.ts);
|
|
80
|
+
# declared here to match the seed exactly, so a playbook can follow it.
|
|
81
|
+
lifecycle:
|
|
82
|
+
property: questionStatus
|
|
83
|
+
options:
|
|
84
|
+
- { key: open, category: open }
|
|
85
|
+
- { key: exploring, category: active }
|
|
86
|
+
- { key: answered, category: done }
|
|
87
|
+
- { key: abandoned, category: lost }
|
|
79
88
|
properties:
|
|
80
89
|
- slug: questionStatus
|
|
81
90
|
label: Status
|
|
@@ -91,6 +100,15 @@ profiles:
|
|
|
91
100
|
displayName: Decision
|
|
92
101
|
icon: gavel
|
|
93
102
|
profileKind: kind
|
|
103
|
+
# The pod seeds this lifecycle on every pod (ensure-system-lifecycles.ts);
|
|
104
|
+
# declared here to match the seed exactly, so a playbook can follow it.
|
|
105
|
+
lifecycle:
|
|
106
|
+
property: decisionStatus
|
|
107
|
+
options:
|
|
108
|
+
- { key: proposed, category: open }
|
|
109
|
+
- { key: accepted, category: done }
|
|
110
|
+
- { key: superseded, category: lost }
|
|
111
|
+
- { key: rejected, category: lost }
|
|
94
112
|
properties:
|
|
95
113
|
- slug: decisionStatus
|
|
96
114
|
label: Status
|
|
@@ -312,7 +330,8 @@ views:
|
|
|
312
330
|
slug: business-model-open-questions
|
|
313
331
|
type: kanban
|
|
314
332
|
scopeProfileSlug: question
|
|
315
|
-
|
|
333
|
+
config:
|
|
334
|
+
groupByField: questionStatus
|
|
316
335
|
description: The nine component questions and everything they spawned, by status.
|
|
317
336
|
|
|
318
337
|
# ── Seeds: the interrogation, not fake data ────────────────────────────────
|
package/src/civic-france.yaml
CHANGED
|
@@ -39,6 +39,15 @@ profiles:
|
|
|
39
39
|
icon: help-circle
|
|
40
40
|
color: blue
|
|
41
41
|
description: An open question driving a civic-data investigation.
|
|
42
|
+
# The pod seeds this lifecycle on every pod (ensure-system-lifecycles.ts);
|
|
43
|
+
# declared here to match the seed exactly, so a playbook can follow it.
|
|
44
|
+
lifecycle:
|
|
45
|
+
property: questionStatus
|
|
46
|
+
options:
|
|
47
|
+
- { key: open, category: open }
|
|
48
|
+
- { key: exploring, category: active }
|
|
49
|
+
- { key: answered, category: done }
|
|
50
|
+
- { key: abandoned, category: lost }
|
|
42
51
|
properties:
|
|
43
52
|
- slug: questionStatus
|
|
44
53
|
label: Status
|
package/src/content-os.yaml
CHANGED
|
@@ -28,7 +28,6 @@ meta:
|
|
|
28
28
|
label: Content
|
|
29
29
|
promise: Every post from idea to published, on one calendar.
|
|
30
30
|
suggestFor: [writer, creator, marketer, musician, photographer]
|
|
31
|
-
profile: post
|
|
32
31
|
views: [content-posts, content-calendar]
|
|
33
32
|
example:
|
|
34
33
|
- { title: "Why we left spreadsheets", values: { post-status: Idea, post-format: Article } }
|
|
@@ -46,6 +45,8 @@ meta:
|
|
|
46
45
|
# What this space PRODUCES (homes): the purpose word, then its kinds/roles.
|
|
47
46
|
intent:
|
|
48
47
|
label: Content
|
|
48
|
+
# The ONE primary output (One model decision 3): a published post is what the editorial flow produces (Produce Content).
|
|
49
|
+
primary: post
|
|
49
50
|
produces:
|
|
50
51
|
- profile: post
|
|
51
52
|
- profile: newsletter
|
|
@@ -713,6 +714,13 @@ profiles:
|
|
|
713
714
|
color: "#0EA5E9"
|
|
714
715
|
description: "An ordered run of posts that teaches one thing end to end — the curriculum axis of the content engine."
|
|
715
716
|
scope: WORKSPACE
|
|
717
|
+
lifecycle:
|
|
718
|
+
property: series-status
|
|
719
|
+
options:
|
|
720
|
+
- { key: Planned, category: open }
|
|
721
|
+
- { key: Running, category: active }
|
|
722
|
+
- { key: Complete, category: done }
|
|
723
|
+
- { key: Abandoned, category: lost }
|
|
716
724
|
properties:
|
|
717
725
|
- slug: series-status
|
|
718
726
|
label: Status
|
|
@@ -1551,7 +1559,8 @@ views:
|
|
|
1551
1559
|
slug: content-posts
|
|
1552
1560
|
type: kanban
|
|
1553
1561
|
scopeProfileSlug: post
|
|
1554
|
-
|
|
1562
|
+
config:
|
|
1563
|
+
groupByField: post-status
|
|
1555
1564
|
description: Content posts by status.
|
|
1556
1565
|
# The editorial calendar — the "it lands on a calendar" promise. Posts render
|
|
1557
1566
|
# on a month grid keyed on their Publish Date. The calendar renderer resolves
|
|
@@ -1600,7 +1609,8 @@ views:
|
|
|
1600
1609
|
slug: content-newsletters
|
|
1601
1610
|
type: kanban
|
|
1602
1611
|
scopeProfileSlug: newsletter
|
|
1603
|
-
|
|
1612
|
+
config:
|
|
1613
|
+
groupByField: newsletter-status
|
|
1604
1614
|
description: Newsletter issues by status.
|
|
1605
1615
|
# ⚠️ NO `Journey Board` (matrix) view here — DELIBERATE, do not "restore" it.
|
|
1606
1616
|
# A coverage grid is the right idea and this is the wrong substrate today:
|
|
@@ -1806,15 +1816,15 @@ entityLinks:
|
|
|
1806
1816
|
# `subjectProfile` binds a playbook to a kind so the Capture → Session matcher
|
|
1807
1817
|
# (playbooks.subject_profile->>'profileSlug') can offer it when one is captured.
|
|
1808
1818
|
#
|
|
1809
|
-
# THE PROCESS CONTRACT (lifecycle-bound playbooks).
|
|
1810
|
-
#
|
|
1811
|
-
#
|
|
1812
|
-
#
|
|
1813
|
-
#
|
|
1819
|
+
# THE PROCESS CONTRACT (lifecycle-bound playbooks). A playbook FOLLOWS the kind's
|
|
1820
|
+
# declared lifecycle (`post-status`); each stage's `subjectStatus` is the value
|
|
1821
|
+
# that stage IS — advancing into the stage writes it to the subject (governed),
|
|
1822
|
+
# and the subject entering it advances an open run to the stage. A value no stage
|
|
1823
|
+
# owns is derived by the pod; a person-only value is `humanOnly` on the lifecycle.
|
|
1814
1824
|
# `activators` are DECLARATIONS the pod compiles into governed propose-mode rules
|
|
1815
1825
|
# at install — never hand-written `automations:`. `relationToSubject` is the edge
|
|
1816
1826
|
# written output → subject when a slot is satisfied. Validated by validate.ts
|
|
1817
|
-
# (`checkProcessContract`)
|
|
1827
|
+
# (`checkProcessContract`).
|
|
1818
1828
|
playbooks:
|
|
1819
1829
|
- name: Produce Content
|
|
1820
1830
|
# ONE playbook for every format (plan W3.3): text / visual / video are
|
|
@@ -1868,10 +1878,8 @@ playbooks:
|
|
|
1868
1878
|
by a person at Publish.
|
|
1869
1879
|
subjectProfile:
|
|
1870
1880
|
profileSlug: post
|
|
1871
|
-
statusProperty: post-status
|
|
1872
1881
|
# Idea is where a capture lands and Archived is a person's call — no stage
|
|
1873
1882
|
# owns either. The activator below starts the run once the post is Briefed.
|
|
1874
|
-
humanOnlyStatuses: [Idea, Archived]
|
|
1875
1883
|
activators:
|
|
1876
1884
|
- { on: enters_status, status: Briefed, mode: propose }
|
|
1877
1885
|
params:
|
|
@@ -2158,8 +2166,7 @@ playbooks:
|
|
|
2158
2166
|
|
|
2159
2167
|
# One take, several videos (D2, D3, 29 Oct). The take is a content-asset of
|
|
2160
2168
|
# Kind recording; it has no lifecycle (its state is derived from what was
|
|
2161
|
-
# made from it), so
|
|
2162
|
-
# move no status. The person decides at Keep, through one question per
|
|
2169
|
+
# made from it), so its stages move no status. The person decides at Keep, through one question per
|
|
2163
2170
|
# proposed piece (a slot ask: the door Content Studio and Relay answer), not
|
|
2164
2171
|
# a stage gate: keeping a piece commits nothing outside the pod, and the raw
|
|
2165
2172
|
# take is never touched. The per-piece slots are declared by the agent as
|
|
@@ -2321,8 +2328,8 @@ playbooks:
|
|
|
2321
2328
|
# never one run per post (a subject-bound playbook's `schedule:` field fans
|
|
2322
2329
|
# out per entity; an activator's `schedule` and a per_moment `manual` do
|
|
2323
2330
|
# not: api `run-playbook.ts` runWorkerBatch). The `subjectProfile` names the
|
|
2324
|
-
# kind those workers read; there are no stages
|
|
2325
|
-
#
|
|
2331
|
+
# kind those workers read; there are no stages, so it never moves a post's
|
|
2332
|
+
# status.
|
|
2326
2333
|
# Run by hand ("Re-plan" on Content Studio's calendar, or the pod's button)
|
|
2327
2334
|
# and once a week (founder, report 22fec5ee §6, 3 Nov 2026). The weekly one
|
|
2328
2335
|
# compiles to a governed rule the owner can pause on the pod; both propose
|
|
@@ -2455,7 +2462,6 @@ playbooks:
|
|
|
2455
2462
|
and link it derived_from the original. Never republish the original as is.
|
|
2456
2463
|
subjectProfile:
|
|
2457
2464
|
profileSlug: post
|
|
2458
|
-
statusProperty: post-status
|
|
2459
2465
|
activators:
|
|
2460
2466
|
# A copy or repost sent for another account (post-copy-of) is not repurposed: its original is.
|
|
2461
2467
|
- { on: enters_status, status: Published, mode: propose, unless: { property: post-copy-of, present: true } }
|
|
@@ -2480,8 +2486,6 @@ playbooks:
|
|
|
2480
2486
|
a person confirms it went out.
|
|
2481
2487
|
subjectProfile:
|
|
2482
2488
|
profileSlug: newsletter
|
|
2483
|
-
statusProperty: newsletter-status
|
|
2484
|
-
humanOnlyStatuses: [Archived]
|
|
2485
2489
|
params:
|
|
2486
2490
|
- { name: newsletter, type: entity, label: Newsletter issue, required: true }
|
|
2487
2491
|
executor: is-agent
|
|
@@ -114,7 +114,7 @@ describe("CRM absorbs Outreach & Comms", () => {
|
|
|
114
114
|
expect((reply.stages ?? []).map((s) => s.subjectStatus).filter(Boolean)).toEqual(["replied", "bounced", "no_response"]);
|
|
115
115
|
|
|
116
116
|
const outreach = playbookOf("Personalized Outreach");
|
|
117
|
-
expect(outreach.subjectProfile).
|
|
117
|
+
expect(outreach.subjectProfile).toEqual({ profileSlug: "campaign" });
|
|
118
118
|
expect(outputsOf(outreach)).toEqual([
|
|
119
119
|
{ kind: "messaging", label: "Proposed first messages", relationToSubject: "none" },
|
|
120
120
|
]);
|
|
@@ -88,7 +88,7 @@ describe("CRM build plan — catalog changes", () => {
|
|
|
88
88
|
|
|
89
89
|
it("A6: one follow-up process drafts proposed messages on weekdays; no outreach process opens a deal", () => {
|
|
90
90
|
const pb = playbookOf(crm, "Follow up on leads in outreach");
|
|
91
|
-
expect(pb.subjectProfile).
|
|
91
|
+
expect(pb.subjectProfile).toEqual({ profileSlug: "lead" });
|
|
92
92
|
expect(pb.activators).toContainEqual({ on: "schedule", cron: "0 7 * * 1-5", status: "outreach", mode: "propose" });
|
|
93
93
|
expect(outputsOf(pb).map((o) => o.kind)).toEqual(["messaging"]);
|
|
94
94
|
expect(pb.stages?.at(-1)).toMatchObject({ key: "send", gate: { kind: "human" } });
|
package/src/crm.yaml
CHANGED
|
@@ -51,7 +51,6 @@ meta:
|
|
|
51
51
|
label: Sales
|
|
52
52
|
promise: Leads and deals, each with a dated next step.
|
|
53
53
|
suggestFor: [small-business, founder]
|
|
54
|
-
profile: deal
|
|
55
54
|
views: [deal-pipeline, deals-table, deals-list]
|
|
56
55
|
example:
|
|
57
56
|
- { title: "Acme renewal", values: { commercialStage: negotiating, value: 12000, nextStep: "Send revised quote Thu" } }
|
|
@@ -69,6 +68,8 @@ meta:
|
|
|
69
68
|
# What this space PRODUCES (homes): the purpose word, then its kinds/roles.
|
|
70
69
|
intent:
|
|
71
70
|
label: Sales
|
|
71
|
+
# The ONE primary output (One model decision 3): the won deal; the lead-becomes-client role flow is not modeled yet (S8, decision 10).
|
|
72
|
+
primary: deal
|
|
72
73
|
produces:
|
|
73
74
|
- profile: lead
|
|
74
75
|
- profile: client
|
|
@@ -829,10 +830,9 @@ views:
|
|
|
829
830
|
slug: outreach-touches
|
|
830
831
|
type: kanban
|
|
831
832
|
scopeProfileSlug: outreach-touch
|
|
832
|
-
groupBy: status
|
|
833
833
|
description: Every outreach message, grouped by where it stands — drafted for a person to send, through replied.
|
|
834
834
|
config:
|
|
835
|
-
|
|
835
|
+
groupByField: status
|
|
836
836
|
cardFields: [channel, touchType, sentDate]
|
|
837
837
|
|
|
838
838
|
onboarding:
|
|
@@ -971,8 +971,6 @@ playbooks:
|
|
|
971
971
|
step converts the lead. Never mark the lead a client.
|
|
972
972
|
subjectProfile:
|
|
973
973
|
profileSlug: lead
|
|
974
|
-
statusProperty: leadStage
|
|
975
|
-
humanOnlyStatuses: [new, outreach, engaged, nurturing, converted, disqualified]
|
|
976
974
|
# Manual only (founder decision 2026-10-27): the `created` worker started
|
|
977
975
|
# one run per lead and burned the budget; a list is scored in ONE run by
|
|
978
976
|
# "Score a list" instead, and a person qualifies the leads worth it.
|
|
@@ -1096,8 +1094,6 @@ playbooks:
|
|
|
1096
1094
|
person's call — record why it was lost.
|
|
1097
1095
|
subjectProfile:
|
|
1098
1096
|
profileSlug: deal
|
|
1099
|
-
statusProperty: commercialStage
|
|
1100
|
-
humanOnlyStatuses: [lost]
|
|
1101
1097
|
activators:
|
|
1102
1098
|
- { on: created, mode: propose }
|
|
1103
1099
|
params:
|
|
@@ -1696,8 +1692,8 @@ playbooks:
|
|
|
1696
1692
|
# next step, and a deal is never opened. "Read the reply" keeps only the
|
|
1697
1693
|
# touches marked sent before the channels. A lifecycle READER of leadStage:
|
|
1698
1694
|
# no stage moves the lead; nurturing after the second follow-up is proposed
|
|
1699
|
-
# through the door once the last date passes in silence.
|
|
1700
|
-
#
|
|
1695
|
+
# through the door once the last date passes in silence. Its schedule worker
|
|
1696
|
+
# reads `outreach`, a value of the lead role's lifecycle (foundation).
|
|
1701
1697
|
# Next action (CRM review 2026-10-27): a drafted message becomes the lead's
|
|
1702
1698
|
# `nextAction` ("Send the drafted message to <name>", `nextActionAt` today),
|
|
1703
1699
|
# and a message marked sent sets `nextActionAt` by the CADENCE (founder
|
|
@@ -1783,7 +1779,6 @@ playbooks:
|
|
|
1783
1779
|
reads it and proposes the next step.
|
|
1784
1780
|
subjectProfile:
|
|
1785
1781
|
profileSlug: lead
|
|
1786
|
-
statusProperty: leadStage
|
|
1787
1782
|
activators:
|
|
1788
1783
|
# Weekdays 07:00 UTC, one batch over the leads in outreach.
|
|
1789
1784
|
- { on: schedule, cron: "0 7 * * 1-5", status: outreach, mode: propose }
|
|
@@ -1906,8 +1901,6 @@ playbooks:
|
|
|
1906
1901
|
meeting. Pausing or cancelling is a person's call.
|
|
1907
1902
|
subjectProfile:
|
|
1908
1903
|
profileSlug: campaign
|
|
1909
|
-
statusProperty: campaign-status
|
|
1910
|
-
humanOnlyStatuses: [paused, cancelled]
|
|
1911
1904
|
activators:
|
|
1912
1905
|
- { on: created, mode: propose }
|
|
1913
1906
|
params:
|
|
@@ -2004,11 +1997,9 @@ playbooks:
|
|
|
2004
1997
|
person does. Opened is recorded by a person.
|
|
2005
1998
|
subjectProfile:
|
|
2006
1999
|
profileSlug: outreach-touch
|
|
2007
|
-
statusProperty: status
|
|
2008
2000
|
# No stage of this process owns these: drafted and sent come before it
|
|
2009
2001
|
# starts, opened is a person's record, replaced closes a draft moved into
|
|
2010
2002
|
# the person's channel.
|
|
2011
|
-
humanOnlyStatuses: [drafted, sent, opened, replaced]
|
|
2012
2003
|
activators:
|
|
2013
2004
|
- { on: enters_status, status: sent, mode: run }
|
|
2014
2005
|
params:
|
|
@@ -54,15 +54,18 @@ describe("decision 1 — Business Developer folds into CRM", () => {
|
|
|
54
54
|
it("'Qualify a lead' is a process on the lead ROLE's leadStage, started by a person", () => {
|
|
55
55
|
const crm = tpl("crm");
|
|
56
56
|
const pb = playbookOf(crm, "Qualify a lead");
|
|
57
|
-
expect(pb?.subjectProfile).
|
|
57
|
+
expect(pb?.subjectProfile).toEqual({ profileSlug: "lead" });
|
|
58
58
|
// Manual only since 2026-10-27 (founder): a `created` worker ran once per
|
|
59
59
|
// lead and burned the budget; "Score a list" scores a whole list in one run.
|
|
60
60
|
expect(pb?.activators).toEqual([{ on: "manual", mode: "propose" }]);
|
|
61
61
|
// The subject is a ROLE, declared pod-wide by foundation.
|
|
62
62
|
expect(profileOf(tpl("foundation"), "lead")).toMatchObject({ profileKind: "role", scope: "shared" });
|
|
63
|
-
//
|
|
64
|
-
|
|
65
|
-
|
|
63
|
+
// It owns researching and qualified; the other leadStage values belong to a
|
|
64
|
+
// person or another flow — derived by the pod, never listed (One model
|
|
65
|
+
// decision 4, 7 Nov 2026; conflict C2 on `outreach` closed).
|
|
66
|
+
const owned = (pb?.stages ?? []).map((s) => s.subjectStatus).filter(Boolean);
|
|
67
|
+
expect(owned).toEqual(["researching", "qualified"]);
|
|
68
|
+
for (const s of owned) expect(propOf(tpl("foundation"), "lead", "leadStage")?.enumValues).toContain(s);
|
|
66
69
|
// Only the last stage, reached when the person chose pursue, moves the lead
|
|
67
70
|
// to qualified. It opens no deal (CRM build plan A4, 2026-10-22): a deal is
|
|
68
71
|
// opened when a person records a booked meeting, which converts the lead.
|
|
@@ -130,7 +133,6 @@ describe("decision 4 — 'Deliver a deliverable' starts when the deliverable ent
|
|
|
130
133
|
expect(pb.stages?.[0]?.subjectStatus).toBeUndefined();
|
|
131
134
|
expect(pb.stages?.map((s) => s.subjectStatus)).not.toContain("not-started");
|
|
132
135
|
// `cancelled` (0.20.0, plan aac47c4a W5) is a person's call too: no stage writes it.
|
|
133
|
-
expect(pb.humanOnlyStatuses).toEqual(["not-started", "cancelled"]);
|
|
134
136
|
expect(pb.stages?.map((s) => s.subjectStatus)).not.toContain("cancelled");
|
|
135
137
|
expect(errorsOf(tpl("operations"))).toEqual([]);
|
|
136
138
|
});
|
|
@@ -90,6 +90,7 @@ describe('workspace primary-surface conversion', () => {
|
|
|
90
90
|
slug: 'pipeline',
|
|
91
91
|
type: 'kanban',
|
|
92
92
|
scopeProfileSlug: 'thing',
|
|
93
|
+
config: { groupByField: 'status' },
|
|
93
94
|
},
|
|
94
95
|
],
|
|
95
96
|
layout: {
|
|
@@ -100,6 +101,14 @@ describe('workspace primary-surface conversion', () => {
|
|
|
100
101
|
},
|
|
101
102
|
},
|
|
102
103
|
});
|
|
104
|
+
// The kanban groups by a real select of its kind (view-kanban-group-*).
|
|
105
|
+
source.profiles = [
|
|
106
|
+
{
|
|
107
|
+
slug: 'thing',
|
|
108
|
+
displayName: 'Thing',
|
|
109
|
+
properties: [{ slug: 'status', label: 'Status', valueType: 'string', inputType: 'select', enumValues: ['open', 'done'] }],
|
|
110
|
+
},
|
|
111
|
+
] as typeof source.profiles;
|
|
103
112
|
|
|
104
113
|
expect(toWorkspaceDefinition(source).definition.layoutConfig).toMatchObject(
|
|
105
114
|
{
|