@synap-core/cli 1.8.0 → 1.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/agent-chat.d.ts +47 -0
- package/dist/commands/agent-chat.js +250 -0
- package/dist/commands/agent-chat.js.map +1 -0
- package/dist/commands/agent-run.js +3 -4
- package/dist/commands/agent-run.js.map +1 -1
- package/dist/commands/agents.js +85 -46
- package/dist/commands/agents.js.map +1 -1
- package/dist/commands/automation.js +2 -1
- package/dist/commands/automation.js.map +1 -1
- package/dist/commands/bridge-setup.d.ts +66 -0
- package/dist/commands/bridge-setup.js +1112 -0
- package/dist/commands/bridge-setup.js.map +1 -0
- package/dist/commands/cap-connections.d.ts +28 -0
- package/dist/commands/cap-connections.js +224 -0
- package/dist/commands/cap-connections.js.map +1 -0
- package/dist/commands/capability.d.ts +89 -0
- package/dist/commands/capability.js +1523 -0
- package/dist/commands/capability.js.map +1 -0
- package/dist/commands/centrality.d.ts +20 -0
- package/dist/commands/centrality.js +94 -0
- package/dist/commands/centrality.js.map +1 -0
- package/dist/commands/connect.d.ts +2 -0
- package/dist/commands/connect.js +16 -35
- package/dist/commands/connect.js.map +1 -1
- package/dist/commands/context.js +4 -6
- package/dist/commands/context.js.map +1 -1
- package/dist/commands/data-extra.d.ts +1 -0
- package/dist/commands/data-extra.js +34 -10
- package/dist/commands/data-extra.js.map +1 -1
- package/dist/commands/data.d.ts +4 -4
- package/dist/commands/data.js +241 -184
- package/dist/commands/data.js.map +1 -1
- package/dist/commands/diagnose.d.ts +20 -0
- package/dist/commands/diagnose.js +143 -0
- package/dist/commands/diagnose.js.map +1 -0
- package/dist/commands/digest.d.ts +17 -0
- package/dist/commands/digest.js +121 -0
- package/dist/commands/digest.js.map +1 -0
- package/dist/commands/discover.d.ts +2 -0
- package/dist/commands/discover.js +25 -8
- package/dist/commands/discover.js.map +1 -1
- package/dist/commands/events.js +2 -1
- package/dist/commands/events.js.map +1 -1
- package/dist/commands/explain.d.ts +1 -1
- package/dist/commands/explain.js +18 -15
- package/dist/commands/explain.js.map +1 -1
- package/dist/commands/facet.d.ts +29 -0
- package/dist/commands/facet.js +113 -0
- package/dist/commands/facet.js.map +1 -0
- package/dist/commands/graph.js +2 -1
- package/dist/commands/graph.js.map +1 -1
- package/dist/commands/import.d.ts +38 -0
- package/dist/commands/import.js +440 -0
- package/dist/commands/import.js.map +1 -0
- package/dist/commands/init.js +35 -16
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/keys.js +3 -4
- package/dist/commands/keys.js.map +1 -1
- package/dist/commands/knowledge.d.ts +2 -0
- package/dist/commands/knowledge.js +61 -9
- package/dist/commands/knowledge.js.map +1 -1
- package/dist/commands/launch.d.ts +50 -0
- package/dist/commands/launch.js +790 -0
- package/dist/commands/launch.js.map +1 -0
- package/dist/commands/lens.d.ts +45 -0
- package/dist/commands/lens.js +386 -0
- package/dist/commands/lens.js.map +1 -0
- package/dist/commands/market.d.ts +209 -0
- package/dist/commands/market.js +1368 -0
- package/dist/commands/market.js.map +1 -0
- package/dist/commands/mcp.d.ts +42 -0
- package/dist/commands/mcp.js +184 -0
- package/dist/commands/mcp.js.map +1 -0
- package/dist/commands/observe.js +14 -11
- package/dist/commands/observe.js.map +1 -1
- package/dist/commands/open.d.ts +21 -4
- package/dist/commands/open.js +45 -4
- package/dist/commands/open.js.map +1 -1
- package/dist/commands/pod.d.ts +100 -0
- package/dist/commands/pod.js +359 -0
- package/dist/commands/pod.js.map +1 -0
- package/dist/commands/pods.d.ts +1 -0
- package/dist/commands/pods.js +77 -15
- package/dist/commands/pods.js.map +1 -1
- package/dist/commands/proposals-actions.js +40 -3
- package/dist/commands/proposals-actions.js.map +1 -1
- package/dist/commands/purge.d.ts +23 -0
- package/dist/commands/purge.js +148 -0
- package/dist/commands/purge.js.map +1 -0
- package/dist/commands/raycast.d.ts +15 -0
- package/dist/commands/raycast.js +153 -0
- package/dist/commands/raycast.js.map +1 -0
- package/dist/commands/sessions.js +45 -21
- package/dist/commands/sessions.js.map +1 -1
- package/dist/commands/skill-manage.d.ts +21 -0
- package/dist/commands/skill-manage.js +195 -0
- package/dist/commands/skill-manage.js.map +1 -0
- package/dist/commands/skills-commands.js +2 -1
- package/dist/commands/skills-commands.js.map +1 -1
- package/dist/commands/skills.js +4 -3
- package/dist/commands/skills.js.map +1 -1
- package/dist/commands/status.d.ts +3 -1
- package/dist/commands/status.js +166 -14
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/statusline.d.ts +24 -0
- package/dist/commands/statusline.js +450 -0
- package/dist/commands/statusline.js.map +1 -0
- package/dist/commands/subscribe.js +2 -2
- package/dist/commands/subscribe.js.map +1 -1
- package/dist/commands/tools.d.ts +30 -0
- package/dist/commands/tools.js +315 -0
- package/dist/commands/tools.js.map +1 -0
- package/dist/commands/versions.d.ts +27 -0
- package/dist/commands/versions.js +277 -0
- package/dist/commands/versions.js.map +1 -0
- package/dist/commands/view.js +3 -2
- package/dist/commands/view.js.map +1 -1
- package/dist/commands/whoami.d.ts +21 -0
- package/dist/commands/whoami.js +129 -0
- package/dist/commands/whoami.js.map +1 -0
- package/dist/index.js +776 -60
- package/dist/index.js.map +1 -1
- package/dist/lib/approval-poll.d.ts +42 -0
- package/dist/lib/approval-poll.js +52 -0
- package/dist/lib/approval-poll.js.map +1 -0
- package/dist/lib/auth.d.ts +29 -6
- package/dist/lib/auth.js +105 -94
- package/dist/lib/auth.js.map +1 -1
- package/dist/lib/bundle-version.d.ts +11 -0
- package/dist/lib/bundle-version.js +33 -0
- package/dist/lib/bundle-version.js.map +1 -0
- package/dist/lib/capture-lane.d.ts +30 -12
- package/dist/lib/capture-lane.js +71 -14
- package/dist/lib/capture-lane.js.map +1 -1
- package/dist/lib/cp-packages.d.ts +121 -0
- package/dist/lib/cp-packages.js +218 -0
- package/dist/lib/cp-packages.js.map +1 -0
- package/dist/lib/describe-lens.d.ts +46 -0
- package/dist/lib/describe-lens.js +33 -0
- package/dist/lib/describe-lens.js.map +1 -0
- package/dist/lib/hub-client.d.ts +78 -1
- package/dist/lib/hub-client.js +474 -22
- package/dist/lib/hub-client.js.map +1 -1
- package/dist/lib/installed.d.ts +51 -0
- package/dist/lib/installed.js +70 -0
- package/dist/lib/installed.js.map +1 -0
- package/dist/lib/next-steps.d.ts +61 -0
- package/dist/lib/next-steps.js +146 -0
- package/dist/lib/next-steps.js.map +1 -0
- package/dist/lib/pod.d.ts +91 -19
- package/dist/lib/pod.js +222 -56
- package/dist/lib/pod.js.map +1 -1
- package/dist/lib/project-ref.d.ts +95 -0
- package/dist/lib/project-ref.js +144 -0
- package/dist/lib/project-ref.js.map +1 -0
- package/dist/lib/project.d.ts +35 -0
- package/dist/lib/project.js +39 -0
- package/dist/lib/project.js.map +1 -0
- package/dist/lib/session-lens.d.ts +33 -0
- package/dist/lib/session-lens.js +76 -0
- package/dist/lib/session-lens.js.map +1 -0
- package/dist/lib/skill-import.d.ts +40 -0
- package/dist/lib/skill-import.js +203 -0
- package/dist/lib/skill-import.js.map +1 -0
- package/dist/lib/skills-installer.d.ts +17 -6
- package/dist/lib/skills-installer.js +133 -19
- package/dist/lib/skills-installer.js.map +1 -1
- package/dist/lib/suite.d.ts +28 -0
- package/dist/lib/suite.js +29 -0
- package/dist/lib/suite.js.map +1 -0
- package/dist/lib/superwhisper-import.d.ts +40 -0
- package/dist/lib/superwhisper-import.js +389 -0
- package/dist/lib/superwhisper-import.js.map +1 -0
- package/dist/lib/targets.d.ts +49 -1
- package/dist/lib/targets.js +420 -288
- package/dist/lib/targets.js.map +1 -1
- package/dist/lib/unwrapList.d.ts +1 -0
- package/dist/lib/unwrapList.js +20 -0
- package/dist/lib/unwrapList.js.map +1 -0
- package/dist/utils/logger.d.ts +2 -0
- package/dist/utils/logger.js +10 -2
- package/dist/utils/logger.js.map +1 -1
- package/package.json +9 -4
- package/skills/agent-os/SKILL.md +212 -0
- package/skills/manifest.json +5 -0
- package/skills/onboard/SKILL.md +186 -0
- package/skills/synap/SKILL.md +1182 -37
- package/skills/synap/_order.txt +37 -0
- package/skills/synap/_skill.yaml +24 -0
- package/skills/synap/authentication.md +10 -0
- package/skills/synap/automations.md +264 -0
- package/skills/synap/capabilities.md +107 -0
- package/skills/synap/capture.md +10 -0
- package/skills/synap/cli-operations.md +107 -0
- package/skills/synap/common-mistakes.md +12 -0
- package/skills/synap/creative-loop.md +43 -0
- package/skills/synap/crm.md +117 -0
- package/skills/synap/diagnostics.md +35 -0
- package/skills/synap/digesting-a-channel.md +38 -0
- package/skills/synap/document-embeds.md +123 -0
- package/skills/synap/escalation-ladder.md +71 -0
- package/skills/synap/focus-sessions.md +47 -0
- package/skills/synap/governance-rules.md +64 -0
- package/skills/synap/governance.md +6 -6
- package/skills/synap/graph-gardening.md +21 -0
- package/skills/synap/inline-patterns.md +38 -0
- package/skills/synap/lenses.md +38 -0
- package/skills/synap/linking-principle.md +39 -0
- package/skills/synap/linking.md +1 -0
- package/skills/synap/mental-model.md +45 -0
- package/skills/synap/more.md +7 -0
- package/skills/synap/multi-entity-capture.md +10 -0
- package/skills/synap/next-action.md +37 -0
- package/skills/synap/north-star.md +17 -0
- package/skills/synap/operating-mode-rules.md +45 -0
- package/skills/synap/operating-mode.md +9 -0
- package/skills/synap/quick-reference.md +67 -0
- package/skills/synap/reading.md +43 -0
- package/skills/synap/recap.md +21 -0
- package/skills/synap/reflexes.md +46 -0
- package/skills/synap/response-style.md +21 -0
- package/skills/synap/scope.md +13 -0
- package/skills/synap/showing.md +17 -0
- package/skills/synap/user-model.md +25 -0
- package/skills/synap/viewframe-cells.md +290 -0
- package/skills/synap/work-flow.md +113 -0
- package/skills/synap/worked-examples.md +53 -0
- package/skills/synap/workspace-design.md +45 -0
- package/skills/synap/workspace-edges.md +49 -0
- package/skills/synap/writes.md +156 -0
- package/skills/synap-schema/SKILL.md +97 -6
- package/skills/synap-schema/_order.txt +13 -0
- package/skills/synap-schema/_skill.yaml +22 -0
- package/skills/synap-schema/before-you-touch.md +14 -0
- package/skills/synap-schema/common-mistakes.md +9 -0
- package/skills/synap-schema/creating-profiles.md +17 -0
- package/skills/synap-schema/discover-profiles.md +24 -0
- package/skills/synap-schema/entity-scope.md +8 -0
- package/skills/synap-schema/extend-vs-create.md +31 -0
- package/skills/synap-schema/more.md +6 -0
- package/skills/synap-schema/overlay-properties.md +22 -0
- package/skills/synap-schema/profiles.md +3 -0
- package/skills/synap-schema/property-defs.md +29 -0
- package/skills/synap-schema/read-before-write.md +50 -0
- package/skills/synap-schema/relations.md +18 -0
- package/skills/synap-schema/worked-example.md +30 -0
- package/skills/synap-ui/SKILL.md +26 -2
- package/skills/synap-ui/_order.txt +13 -0
- package/skills/synap-ui/_skill.yaml +23 -0
- package/skills/synap-ui/ai-companion.md +27 -0
- package/skills/synap-ui/arranging-bento.md +8 -0
- package/skills/synap-ui/before-you-build.md +20 -0
- package/skills/synap-ui/bento-layouts.md +35 -0
- package/skills/synap-ui/common-mistakes.md +10 -0
- package/skills/synap-ui/creating-views.md +26 -0
- package/skills/synap-ui/four-builds.md +24 -0
- package/skills/synap-ui/more.md +7 -0
- package/skills/synap-ui/proposing-vs-extending.md +11 -0
- package/skills/synap-ui/view-list.md +24 -0
- package/skills/synap-ui/views.md +3 -0
- package/skills/synap-ui/worked-example.md +18 -0
- package/skills/synap-ui/workspaces.md +47 -0
- package/templates/automations/bookmark-enrichment.automation.json +44 -0
- package/templates/automations/mail-feed.automation.json +86 -0
- package/templates/automations/url-bookmark-capture.automation.json +70 -0
- package/templates/skills/README.md +21 -0
- package/templates/skills/authoring-automations/SKILL.md +95 -0
- package/templates/skills/authoring-automations/reference/node-matrix.md +49 -0
- package/templates/skills/authoring-capabilities/SKILL.md +93 -0
- package/templates/skills/authoring-capabilities/reference/template-anatomy.md +70 -0
- package/templates/skills/authoring-playbooks/SKILL.md +102 -0
- package/templates/skills/authoring-playbooks/reference/stages-contract.md +35 -0
- package/templates/skills/authoring-playbooks/reference/worked-example.md +64 -0
- package/templates/skills/bookmark-enrichment/SKILL.md +40 -0
- package/templates/skills/coordinating-agents/SKILL.md +32 -0
- package/templates/skills/coordinating-branches/SKILL.md +27 -0
- package/templates/skills/mapping-the-server/SKILL.md +54 -0
- package/templates/skills/reading-client-progress/SKILL.md +65 -0
- package/templates/skills/routing-to-channels/SKILL.md +54 -0
- package/templates/skills/stellar-scf-grants-advisor/ASSOCIATE-CHEATSHEET.md +133 -0
- package/templates/skills/stellar-scf-grants-advisor/ASSOCIATE-ONBOARDING-GUIDE.md +761 -0
- package/templates/skills/stellar-scf-grants-advisor/CLIENT-FAQ.md +181 -0
- package/templates/skills/stellar-scf-grants-advisor/PARTNER-HANDOVER.md +130 -0
- package/templates/skills/stellar-scf-grants-advisor/SKILL.md +102 -0
- package/templates/skills/stellar-scf-grants-advisor/examples/README.md +43 -0
- package/templates/skills/stellar-scf-grants-advisor/examples/chat-summary-templates.md +158 -0
- package/templates/skills/stellar-scf-grants-advisor/examples/corpus-comparable-rows.md +136 -0
- package/templates/skills/stellar-scf-grants-advisor/examples/q10-team-rewrites.md +109 -0
- package/templates/skills/stellar-scf-grants-advisor/examples/q2-description-rewrites.md +88 -0
- package/templates/skills/stellar-scf-grants-advisor/examples/q6-integration-rewrites.md +107 -0
- package/templates/skills/stellar-scf-grants-advisor/examples/tranche-structures.md +176 -0
- package/templates/skills/stellar-scf-grants-advisor/reference/01-methodology.md +96 -0
- package/templates/skills/stellar-scf-grants-advisor/reference/02-scoring-framework.md +199 -0
- package/templates/skills/stellar-scf-grants-advisor/reference/03-rejection-patterns.md +241 -0
- package/templates/skills/stellar-scf-grants-advisor/reference/04-winner-patterns.md +120 -0
- package/templates/skills/stellar-scf-grants-advisor/reference/05-comeback-patterns.md +92 -0
- package/templates/skills/stellar-scf-grants-advisor/reference/06-budget-benchmarks.md +125 -0
- package/templates/skills/stellar-scf-grants-advisor/reference/07-integration-list.md +147 -0
- package/templates/skills/stellar-scf-grants-advisor/reference/08-abstract-fields.md +258 -0
- package/templates/skills/stellar-scf-grants-advisor/reference/09-build-fields.md +259 -0
- package/templates/skills/stellar-scf-grants-advisor/reference/10-corpus-winners.md +135 -0
- package/templates/skills/stellar-scf-grants-advisor/reference/11-corpus-rejects.md +267 -0
- package/templates/skills/stellar-scf-grants-advisor/reference/12-client-communication.md +131 -0
- package/templates/skills/stellar-scf-grants-advisor/reference/13-excel-output-spec.md +199 -0
- package/templates/skills/stellar-scf-grants-advisor/reference/14-worked-examples.md +160 -0
- package/templates/skills/stellar-scf-grants-advisor/templates/abstract-review-builder.py +253 -0
- package/templates/skills/stellar-scf-grants-advisor/templates/build-review-builder.py +213 -0
- package/dist/commands/connectors.d.ts +0 -31
- package/dist/commands/connectors.js +0 -276
- package/dist/commands/connectors.js.map +0 -1
- package/dist/commands/finish.d.ts +0 -25
- package/dist/commands/finish.js +0 -274
- package/dist/commands/finish.js.map +0 -1
- package/dist/commands/openclaw.d.ts +0 -40
- package/dist/commands/openclaw.js +0 -1148
- package/dist/commands/openclaw.js.map +0 -1
- package/dist/commands/security-audit.d.ts +0 -12
- package/dist/commands/security-audit.js +0 -100
- package/dist/commands/security-audit.js.map +0 -1
- package/dist/lib/hardening.d.ts +0 -18
- package/dist/lib/hardening.js +0 -214
- package/dist/lib/hardening.js.map +0 -1
- package/node_modules/@synap/hub-rest-client/dist/index.cjs +0 -881
- package/node_modules/@synap/hub-rest-client/dist/index.d.cts +0 -907
- package/node_modules/@synap/hub-rest-client/dist/index.d.ts +0 -907
- package/node_modules/@synap/hub-rest-client/dist/index.js +0 -851
- package/node_modules/@synap/hub-rest-client/package.json +0 -45
- package/node_modules/@synap/hub-rest-client/src/client.ts +0 -1143
- package/node_modules/@synap/hub-rest-client/src/errors.ts +0 -30
- package/node_modules/@synap/hub-rest-client/src/index.ts +0 -111
- package/node_modules/@synap/hub-rest-client/src/setup.ts +0 -77
- package/node_modules/@synap/hub-rest-client/src/types.ts +0 -639
package/skills/synap/SKILL.md
CHANGED
|
@@ -35,6 +35,131 @@ You are connected to a **Synap Data Pod** at `{SYNAP_POD_URL}`. All requests use
|
|
|
35
35
|
|
|
36
36
|
Your job is to turn unstructured input into a **connected** knowledge graph. Isolated entities are anti-value. Every entity you create should link to at least one other entity.
|
|
37
37
|
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Reflexes — the two habits, every session
|
|
41
|
+
|
|
42
|
+
> Canonical source — MCP instructions and other surfaces derive from this file.
|
|
43
|
+
|
|
44
|
+
You are connected to the user's Synap pod — their sovereign personal data brain
|
|
45
|
+
(the source of truth about their life, work, projects, people, and preferences).
|
|
46
|
+
Two reflexes hold on every session, on every door (MCP, IS, CLI, HTTP):
|
|
47
|
+
|
|
48
|
+
- **Recall — BEFORE any non-trivial task.** Before answering anything about the
|
|
49
|
+
user's own world, and before creating anything, call `synap_ask` (CLI:
|
|
50
|
+
`synap ask`, REST: `POST /api/hub/knowledge/ask`) to recall what the pod
|
|
51
|
+
already knows. Prefer it over your own assumptions or training data — asking
|
|
52
|
+
first also avoids duplicate creates.
|
|
53
|
+
- **Capture — AFTER you learn something durable.** A fact, a decision, a new
|
|
54
|
+
person/company/task, a stated preference — call `synap_capture` (CLI:
|
|
55
|
+
`synap capture`) to write it back. Don't wait to be asked; this is how the
|
|
56
|
+
second brain grows.
|
|
57
|
+
|
|
58
|
+
Run `synap_orient` (CLI: `synap orient`) once per session to see the available
|
|
59
|
+
workspaces, projects, and entity types before acting.
|
|
60
|
+
|
|
61
|
+
**Writes are governed: a `"proposed"` response is normal, never an error.** It
|
|
62
|
+
means the write is queued for the user's review — like a PR, not a failure. Keep
|
|
63
|
+
working; see `writes.md` for the full governance contract and `inline-patterns.md`
|
|
64
|
+
for how to surface a proposal's review link in a Companion reply.
|
|
65
|
+
|
|
66
|
+
**No private scratchpad.** Everything you learn goes into the shared graph, not a hidden note. Capture a proven tool-fact into `knowledge` immediately; PROMOTE it into a curated skill only once it's proven reusable — a skill is a versioned artifact (one capability, when-to-use + do/don't), never an append-anything log.
|
|
67
|
+
|
|
68
|
+
## Escalation ladder (keep in a corner of your head)
|
|
69
|
+
|
|
70
|
+
You can always escalate — never dead-end on "I can't." Full detail: `escalation-ladder.md`.
|
|
71
|
+
|
|
72
|
+
- **L0 Reflexes** — recall before, capture after, proposed ≠ error
|
|
73
|
+
- **L1 OPERATE on data** — capture, create_entity, link, attach KNOWN facets, sessions
|
|
74
|
+
- **L2 DISCOVER before invent** — list_profiles, list_capabilities, market.search (capability|template|automation|cell)
|
|
75
|
+
- **L3 MUTATE meta-model (proposal-gated)** — only if L2 empty for the need:
|
|
76
|
+
define_role, create_property_def/profile, create_view, create_workspace, market.install.
|
|
77
|
+
**Template FIRST for new domains:** market.search(kind:template) before freehand create_workspace
|
|
78
|
+
- **L4 CRYSTALLIZE after proof** — promote_session_to_playbook, promote_cell_to_renderer, create_playbook.
|
|
79
|
+
Never crystallize a one-off that hasn't succeeded once
|
|
80
|
+
|
|
81
|
+
**Gates:**
|
|
82
|
+
|
|
83
|
+
- Blocked / can't express need → L2 then L3 propose (never dead-end error; never silent invent)
|
|
84
|
+
- Success / repeatable pattern → one structural suggestion (question first if speculative)
|
|
85
|
+
- Capture placement routes to EXISTING lenses only — never invent a workspace from capture
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
# Escalation ladder — discover → invent under proposal → crystallize after proof
|
|
90
|
+
|
|
91
|
+
The always-on brief lives in `reflexes.md`. This file is the full HOW when you need more than the corner-of-your-head reminder.
|
|
92
|
+
|
|
93
|
+
## Why it exists
|
|
94
|
+
|
|
95
|
+
Agents fail in two ways: (1) dead-end ("I can't do that") when the substrate could express the need after discovery or a proposed meta change, and (2) silent invent (minting workspaces/profiles/views without checking what already exists). The ladder is the habit that prevents both. Soft teaching only — no hard tool filtering by tier.
|
|
96
|
+
|
|
97
|
+
## Levels
|
|
98
|
+
|
|
99
|
+
### L0 — Reflexes (always)
|
|
100
|
+
|
|
101
|
+
Recall before non-trivial work (`synap_ask` / search). Capture durable learning after. Treat `"proposed"` as success-in-review, not an error. Orient once per session.
|
|
102
|
+
|
|
103
|
+
### L1 — OPERATE on data
|
|
104
|
+
|
|
105
|
+
Default mode: work with what already exists.
|
|
106
|
+
|
|
107
|
+
- Capture free text, create/update entities, link, attach **known** facets
|
|
108
|
+
- Start/update sessions when the work is a unit with a deliverable
|
|
109
|
+
- Prefer existing profiles, views, capabilities, playbooks over inventing structure
|
|
110
|
+
|
|
111
|
+
### L2 — DISCOVER before invent
|
|
112
|
+
|
|
113
|
+
When the tool list or current schema doesn't express the need — **search before minting**:
|
|
114
|
+
|
|
115
|
+
1. `list_profiles` / `list_views` / `list_capabilities({query})` in the active lenses
|
|
116
|
+
2. `market.search({query, kind?})` over `capability` | `template` | `automation` | `cell`
|
|
117
|
+
3. Load the relevant skill (`load_skill` / discover_tools) if the HOW is unclear
|
|
118
|
+
|
|
119
|
+
Only if L2 returns empty for the real need do you climb to L3.
|
|
120
|
+
|
|
121
|
+
### L3 — MUTATE the meta-model (proposal-gated)
|
|
122
|
+
|
|
123
|
+
Extend the substrate so the need becomes expressible. Always governed — expect `"proposed"`.
|
|
124
|
+
|
|
125
|
+
| Need | Prefer | Tool sketch |
|
|
126
|
+
| ------------------ | -------------------- | --------------------------------------------------------------------------------------- |
|
|
127
|
+
| Role/hat missing | Existing role attach | `define_role` only after `list_profiles` empty for that role |
|
|
128
|
+
| Field missing | Existing property | `create_property_def` |
|
|
129
|
+
| Kind missing | Closest parent kind | `create_profile` (extend, don't fork) |
|
|
130
|
+
| View missing | Existing view | `list_views` first, then `create_view` (recovery or proactive) |
|
|
131
|
+
| Domain missing | **Template** | `market.search(kind:template)` → install/propose **before** freehand `create_workspace` |
|
|
132
|
+
| Capability missing | Marketplace | `market.install` (always proposes for agents) |
|
|
133
|
+
|
|
134
|
+
**Template-before-workspace (hard rule in teaching):** new operational domains start as marketplace templates when one fits. Freehand workspace creation is last resort after the four workspace-design conditions hold (`workspace-design.md`). Capture never invents a workspace — placement only routes into existing lenses.
|
|
135
|
+
|
|
136
|
+
### L4 — CRYSTALLIZE after proof
|
|
137
|
+
|
|
138
|
+
After a one-off has succeeded and is clearly repeatable:
|
|
139
|
+
|
|
140
|
+
- Session that worked → `promote_session_to_playbook`
|
|
141
|
+
- Cell that presents well for a type/slot → `promote_cell_to_renderer`
|
|
142
|
+
- Repeatable process authored deliberately → `create_playbook`
|
|
143
|
+
|
|
144
|
+
Never crystallize a speculative or failed one-off. One structural suggestion at a time; if speculative, ask first (`creative-loop.md`).
|
|
145
|
+
|
|
146
|
+
## Decision gates (cheat sheet)
|
|
147
|
+
|
|
148
|
+
```
|
|
149
|
+
Can I do it with existing data/tools?
|
|
150
|
+
yes → L1
|
|
151
|
+
no → L2 discover
|
|
152
|
+
found → use / install (propose) / enable
|
|
153
|
+
empty → L3 propose meta change (never silent invent)
|
|
154
|
+
Did a one-off just succeed and will recur?
|
|
155
|
+
yes → offer L4 (question if speculative)
|
|
156
|
+
no → leave it as a one-off
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Blocked path: **never** invent silently; **never** stop at a dead-end error — climb L2→L3 and propose. Success path: one clear structural nudge, not a cascade of schema changes.
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
38
163
|
## Mental model
|
|
39
164
|
|
|
40
165
|
Synap is a typed knowledge graph. **Reading is one verb (`synap ask`) — it routes for you.** Writing is where you must pick the right lane: the destination is decided by the **KIND** of knowledge, not by whichever workspace happens to be active.
|
|
@@ -43,6 +168,8 @@ Synap is a typed knowledge graph. **Reading is one verb (`synap ask`) — it rou
|
|
|
43
168
|
|
|
44
169
|
Ask yourself: _who does this knowledge serve?_ **There is no private AI scratchpad** — structuring knowledge into a real lane IS your job. Never write a `note` (that's the human's raw inbox); always `capture` into a lane.
|
|
45
170
|
|
|
171
|
+
**Known fields → typed create.** If you already know the profileSlug and the values, use `synap create entity` / `synap_create_entity` (or typed `capture --type` for a knowledge entry). Reach for free-text `capture "…"` only for an unstructured blob you haven't parsed — it runs an AI pipeline that can degrade to one flat `note`. 'Always capture into a lane' means _don't leave it unstructured_, not _always use the free-text pipeline_.
|
|
172
|
+
|
|
46
173
|
| If it… | Lane | Where it goes | Governance |
|
|
47
174
|
| ------------------------------------------------------------------------------------------------------------------------------- | -------------------- | ---------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
|
|
48
175
|
| **is about the CURRENT WORK** — domain know-how for the project/task you're on (incl. a domain-specific gotcha/lesson/decision) | **Work** _(default)_ | a `knowledge` entity in the **active workspace** (`synap capture --type …`) | proposal-gated (it's the user's real data; the workspace IS the domain — Builder ≠ marketing) |
|
|
@@ -51,6 +178,11 @@ Ask yourself: _who does this knowledge serve?_ **There is no private AI scratchp
|
|
|
51
178
|
|
|
52
179
|
> **Why this matters:** writing to the wrong lane degrades the graph. A gotcha you learned about the **current project** is **Work** (the active workspace — its domain). A best-practice that holds **everywhere** is **Global** (`--global`, pod-wide). A fact about **how the user works** is **User** (pod-wide, inferences proposed). `synap capture` echoes which lane + governance it used; check it.
|
|
53
180
|
|
|
181
|
+
> **Read the write outcome — it guides your next move (it never blocks you).** Every write (`capture`, `observe`, `create entity`, `create relation`, `note`) reports one of two outcomes (and `--json` carries `"outcome"`):
|
|
182
|
+
>
|
|
183
|
+
> - **`stored`** → it's **live now**, recallable via `synap ask`.
|
|
184
|
+
> - **`proposed`** → queued for the human's review, **like a git PR — not a failure, not a block.** Keep working: compose a whole graph of proposed changes in one session (reference the proposed entities, link them, add more) — they're staged together and go live when the human approves the batch. The only thing to remember: it's _under review_, so don't tell the user it's already applied. (Inferences about the user and writes to real workspaces are gated by design — expected, normal.)
|
|
185
|
+
|
|
54
186
|
> **Substrate names (tables under the hood):** _semantic_ = `entities` (the `knowledge` profile, workspace-scoped = domain separation), _episodic_ = `knowledge_facts`, _procedural_ = `knowledge_keys` (pod-wide runbooks). `ask` queries across them so you never pick on read.
|
|
55
187
|
|
|
56
188
|
### Data layers — the graph itself
|
|
@@ -74,12 +206,15 @@ Ask yourself: _who does this knowledge serve?_ **There is no private AI scratchp
|
|
|
74
206
|
| `research` | pod | AI | Investigation with sources + conclusion |
|
|
75
207
|
| `question` | pod | human + AI | Open inquiry, closed when a decision answers it |
|
|
76
208
|
|
|
209
|
+
---
|
|
210
|
+
|
|
77
211
|
## Quick reference — 90% of tasks in 30 lines
|
|
78
212
|
|
|
79
213
|
```bash
|
|
80
214
|
# CLI (preferred — auth automatic, --json = clean output)
|
|
81
|
-
synap orient --json # discover userId + workspaces
|
|
82
|
-
synap
|
|
215
|
+
synap orient --json # discover userId + workspaces + projects
|
|
216
|
+
synap lens # where am I? workspace + project + session (this Claude session)
|
|
217
|
+
synap use <workspace-name-or-id> # focus a workspace (this session)
|
|
83
218
|
synap create entity --profile=task --name="…" --props='{"status":"todo","priority":"high"}' --json
|
|
84
219
|
synap set entity <id> --props='{"status":"done"}' --json # merge-patch (only changed keys)
|
|
85
220
|
synap ask "your question" --json # THE read verb — routes to the right store(s) + shows which answered
|
|
@@ -101,7 +236,7 @@ or `recall` — `ask` is the door.)
|
|
|
101
236
|
|
|
102
237
|
```bash
|
|
103
238
|
# REST (when no Bash access)
|
|
104
|
-
POST /api/hub/entities body: { userId, workspaceId?, profileSlug, title, properties }
|
|
239
|
+
POST /api/hub/entities body: { userId, workspaceId?, profileSlug, title, description?, properties?, content?, projectId?, facets?, source? }
|
|
105
240
|
PATCH /api/hub/entities/{id} body: { userId, properties } ← deep-merges, send only changed keys
|
|
106
241
|
POST /api/hub/documents body: { userId, workspaceId?, title, content, entityId? }
|
|
107
242
|
PATCH /api/hub/documents/{id} body: { userId, title?, content? } ← full content replacement
|
|
@@ -121,11 +256,13 @@ synap discover --profiles --json # CLI: profiles only
|
|
|
121
256
|
```
|
|
122
257
|
|
|
123
258
|
```
|
|
124
|
-
GET /api/hub/discover?userId={userId}&
|
|
125
|
-
→ { profiles: [{ slug, displayName, scope, properties: [{ slug, type,
|
|
259
|
+
GET /api/hub/discover?userId={userId}&profileSlugs=task
|
|
260
|
+
→ { profiles: [{ slug, displayName, scope, properties: [{ slug, type, required, defaultValue?, constraints?, targetProfileSlug? }], createCommand }], commands: {...} }
|
|
126
261
|
```
|
|
127
262
|
|
|
128
|
-
Call
|
|
263
|
+
Call the summary tier once at session start, then load only the profile schemas
|
|
264
|
+
needed for a write. Omit `workspaceId` for base/pod schema; add it only to see
|
|
265
|
+
that workspace's overlays. Do not rely on a static property list — it will drift.
|
|
129
266
|
|
|
130
267
|
**Load more detail on demand** (`GET /api/hub/skills/system?sections=<id>`):
|
|
131
268
|
|
|
@@ -139,13 +276,56 @@ Call this once at session start. The response includes every system profile and
|
|
|
139
276
|
| `synap-ui:widget-catalog` | Available widget kinds and their configSchema |
|
|
140
277
|
| `synap-schema:SKILL` | Creating custom profiles or property definitions |
|
|
141
278
|
|
|
279
|
+
---
|
|
280
|
+
|
|
281
|
+
## Lenses — where you are vs. what you can reach
|
|
282
|
+
|
|
283
|
+
You don't work "inside a workspace" the way you'd work inside a folder. You operate **across the whole pod**, and you **focus** through up to three composable lenses. **Lenses narrow; they never silo.** Omitting them is legal and common — that's pod-wide.
|
|
284
|
+
|
|
285
|
+
| Lens | What it is | How to set (this session) |
|
|
286
|
+
| ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |
|
|
287
|
+
| **Project** | a **company or initiative** — the thing that ties the work together (Synap, a client, a launch). The lens you usually _organize by_. | `synap project use <id>` / `clear` |
|
|
288
|
+
| **Workspace** | an **operational domain** — where data lives (Foundation, CRM, Marketing, Finance, Builder). How the work is separated; the default home for writes. | `synap use <name-or-id>` |
|
|
289
|
+
| **Session** | the **work room** for the current goal (holds goal, deliverables, progress) | `synap session start --goal "…"` / `attach <id>` |
|
|
290
|
+
|
|
291
|
+
**How they compose — this is the whole model:**
|
|
292
|
+
|
|
293
|
+
- A **project spans workspaces**: one company/initiative has a Foundation, a CRM, a Marketing, a Finance… each a different operational lens on the _same_ project.
|
|
294
|
+
- A **workspace spans projects**: the Marketing workspace can hold work for several clients/projects at once.
|
|
295
|
+
- **Membership is per-entity, filed on write.** An entity belongs to a project because it was created/filed **under the project lens** — not because its workspace is "in" the project (there is no workspace→project link). So **set the project lens before writing** work that belongs to an initiative, and it composes into that project from any workspace.
|
|
296
|
+
- Compose either way, or both. That's why they're lenses, not folders: **workspaces exist so that development, finance, marketing, and operations don't pile into one undifferentiated place** — they're the separation that makes the work legible.
|
|
297
|
+
|
|
298
|
+
- **The connection is pod-wide by design.** Your MCP/CLI link is _not_ welded to a workspace — reads default pod-wide, writes default to a sensible workspace. Pass a lens to narrow a single call; the lens is a focus, not a fence.
|
|
299
|
+
- **These are per-Claude-session.** Two concurrent Claude sessions can sit on different projects/workspaces/sessions without colliding. `synap use` here rebinds **this** session only.
|
|
300
|
+
- **Inspect anytime:** `synap lens` → the project + workspace + session this session resolves to.
|
|
301
|
+
|
|
302
|
+
### The "am I in the right place?" reflex
|
|
303
|
+
|
|
304
|
+
**Before the FIRST write of a new unit of work**, check your lens and orient if you're unsure:
|
|
305
|
+
|
|
306
|
+
1. `synap lens` — am I scoped where this work belongs?
|
|
307
|
+
2. If unsure what exists → `synap orient` — it returns a **light lens map**: the projects and the workspaces (names + ids), so you see the shape without a data dump. Never guess IDs. Drill into a workspace's profiles or a project's contents only when you actually need them.
|
|
308
|
+
3. **Connect or create:** if the right project / workspace / session doesn't exist yet, create it. A **session is the normal per-task move**. Creating a **workspace (a new operational domain) is a deliberate, expected move as the work grows** — not something to avoid. A **project, though, is a COMMITMENT WITH GRAVITY**: search existing projects first (`synap orient`) and prefer **linking into an existing one** via `belongs_to_project`. Only create a new project for a real initiative that ties work together — never for a task, plan, repo, or theme (those are entities), and **never for the pod owner's own company** (the company _is_ the pod, not a project inside it). An agent-created project must cite **≥5 existing entities** as evidence or the backend rejects it, and near-duplicate names are rejected with the existing candidates.
|
|
309
|
+
|
|
310
|
+
**Don't re-orient mid-flow.** Once you've oriented and you're in a run of related writes, keep going — re-check only when you **start a new piece of work** or switch domains. The reflex guards the _start_ of work, not every call.
|
|
311
|
+
|
|
312
|
+
### Notice a missing domain — and offer it
|
|
313
|
+
|
|
314
|
+
Because workspaces are how a company separates its operations, a project is sometimes **missing an operational domain it clearly needs**. If the conversation is squarely about an area — sales, content, finance, hiring, ops — and the active project has **no workspace for it**, say so **once, at the end, in one line**, and offer to set it up:
|
|
315
|
+
|
|
316
|
+
> _"This project doesn't have a Marketing workspace yet — want me to spin one up and capture the essentials?"_
|
|
317
|
+
|
|
318
|
+
If they say yes, provision that **one** domain and run its onboarding interview **with the project lens active** (so its entities file into the project) (see the `agent-os` skill — it handles both the whole-company setup and adding a single domain to an existing project). **Offer, don't auto-build.** One nudge per response, and only when the gap is real — never a checklist of everything the project "could" have. **If the user has already declined a domain (this session or before), drop it — don't re-offer.**
|
|
319
|
+
|
|
320
|
+
---
|
|
321
|
+
|
|
142
322
|
## Synap-first operating mode
|
|
143
323
|
|
|
144
324
|
> **MCP clients** (Claude Desktop, Raycast, OpenClaw with MCP): use `synap_*` tool names — they wrap auth and governance automatically. **REST / HTTP clients**: use the endpoints below.
|
|
145
325
|
|
|
146
326
|
These five rules override default assistant behavior when connected to a Synap pod:
|
|
147
327
|
|
|
148
|
-
**1. Orient before acting**
|
|
328
|
+
**1. Orient before acting** _(and check your lens — see "am I in the right place?" above)_
|
|
149
329
|
Run `scripts/orient.sh` or call these endpoints at the start of every session — before searching, before creating, before answering any question about the user's data:
|
|
150
330
|
|
|
151
331
|
```
|
|
@@ -185,17 +365,21 @@ Facts about the user — preferences, team, working style, recurring context —
|
|
|
185
365
|
|
|
186
366
|
Properties with `valueType: "entity_id"` are typed links to other entities — see **Linking** below.
|
|
187
367
|
|
|
368
|
+
---
|
|
369
|
+
|
|
188
370
|
## CLI Data Operations (Bash tool)
|
|
189
371
|
|
|
190
372
|
When Claude Code (or any agent with Bash access) is using this skill, prefer the `synap` CLI over raw HTTP calls — auth is automatic, output is clean JSON, no spinners in `--json` mode.
|
|
191
373
|
|
|
192
374
|
**Session context — set once, never repeat:**
|
|
193
375
|
|
|
194
|
-
The CLI
|
|
376
|
+
The CLI inherits your pod + lens automatically; set them once and every later command picks them up. Do NOT pass `--pod-url`, `--api-key`, or `--workspace` on every command. Inside a Claude session, `synap use` / `synap project use` bind **this session's lens** (`~/.synap/lenses/<session_id>.json`) — so concurrent sessions stay independent; outside one, they set the global default (`~/.synap/config.json`).
|
|
195
377
|
|
|
196
378
|
```bash
|
|
197
379
|
synap pods use <profile-name> # switch active pod
|
|
198
|
-
synap use <workspace-id> #
|
|
380
|
+
synap use <workspace-id> # focus a workspace (this session) — captures land here; it IS the domain
|
|
381
|
+
synap project use <id> # add the project lens (composable)
|
|
382
|
+
synap lens # inspect: workspace + project + session this session resolves to
|
|
199
383
|
```
|
|
200
384
|
|
|
201
385
|
**Always orient first:**
|
|
@@ -260,6 +444,19 @@ synap ask "hono routing gotcha" --json
|
|
|
260
444
|
`synap capture --type` writes a typed **`knowledge`** entity in the **active workspace** (the Work lane); `ek_type` (gotcha|lesson|decision|reference) discriminates the kind — **one store, type tags, not a residual dump**. It's workspace-scoped, so the active workspace supplies the domain — a Builder gotcha ≠ a marketing one (there is no `engineering_knowledge`). Add **`--global`** to write a pod-wide cross-cutting runbook to `knowledge_keys` instead. A formal **decision RECORD** (rationale, alternatives, superseded-by lifecycle) is a different artifact — use smart `synap capture "<free text>"` or `synap create entity --profile=decision`. Retrieve everything with `synap ask`.
|
|
261
445
|
Use `capture` for anything worth remembering across sessions and projects.
|
|
262
446
|
|
|
447
|
+
**Open (the one display door):**
|
|
448
|
+
|
|
449
|
+
```bash
|
|
450
|
+
synap open <id> # resolves type automatically, opens in browser
|
|
451
|
+
synap open entity <id> # open entity detail
|
|
452
|
+
synap open proposal <id> # open proposal review
|
|
453
|
+
synap open view <id> # open a view
|
|
454
|
+
synap open cell <typeKey> # open a registered cell by typeKey
|
|
455
|
+
synap open document <id> # open a document
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
The bare-ID form calls `GET /api/hub/resolve/:id` to determine the type before dispatching. Use this when you don't know what type a UUID is — `synap open <id>` always works.
|
|
459
|
+
|
|
263
460
|
**Write:**
|
|
264
461
|
|
|
265
462
|
```bash
|
|
@@ -278,6 +475,8 @@ synap set entity <id> --props='{"status":"done"}' --json
|
|
|
278
475
|
|
|
279
476
|
---
|
|
280
477
|
|
|
478
|
+
---
|
|
479
|
+
|
|
281
480
|
## Scope — default pod-wide
|
|
282
481
|
|
|
283
482
|
**Default: pod-wide.** 13 of 17 system profiles (`note`, `task`, `project`, `event`, `person`, `contact`, `company`, `bookmark`, `article`, `website`, `decision`, `question`, `research`) are pod-scoped — entities you create show up in _every_ workspace the user owns. The backend handles this automatically when the profile is pod-scoped: you don't need to pass `workspaceId`.
|
|
@@ -292,6 +491,8 @@ synap set entity <id> --props='{"status":"done"}' --json
|
|
|
292
491
|
|
|
293
492
|
When you do scope to a workspace, pass `workspaceId` in the create body — the backend respects it. Never pass `workspaceId: null` explicitly to force pod-wide; the profile's `entityScope` decides.
|
|
294
493
|
|
|
494
|
+
---
|
|
495
|
+
|
|
295
496
|
## The work flow — question → research → decision → action
|
|
296
497
|
|
|
297
498
|
AI-assisted work has a shape. When the user is actually _thinking about something_, it flows through four structural nodes. Each is a first-class entity. None of these are optional "nice-to-have" labels — they're the graph that makes the work _durable_ and transferrable between AIs.
|
|
@@ -400,11 +601,13 @@ User: _"I'm trying to figure out whether we should build our own orchestrator or
|
|
|
400
601
|
|
|
401
602
|
Don't interrupt the conversation to ask "should I log this as a question?" — just do it and add a one-line trailer at the end of your response:
|
|
402
603
|
|
|
403
|
-
> (Logged as question on Project Eve. Review:
|
|
604
|
+
> (Logged as question on Project Eve. Review: synap://open/proposal/…)
|
|
404
605
|
|
|
405
606
|
If the creation was auto-approved (entity.create is on the whitelist), there's no proposal; just show a link to the entity:
|
|
406
607
|
|
|
407
|
-
> (Logged as question →
|
|
608
|
+
> (Logged as question → synap://open/entity/ent_question_1)
|
|
609
|
+
|
|
610
|
+
---
|
|
408
611
|
|
|
409
612
|
## Linking — the core principle
|
|
410
613
|
|
|
@@ -442,8 +645,12 @@ POST /api/hub/relations
|
|
|
442
645
|
}
|
|
443
646
|
```
|
|
444
647
|
|
|
648
|
+
**Link entities you reference back to the thread.** When your reply cites an entity or document, connect it to the current conversation with `link_entity_to_thread` / `link_document_to_thread` — one line of why ("linking this because it's directly relevant to what you're building"). It should feel like keeping notes, not running a pipeline.
|
|
649
|
+
|
|
445
650
|
For auto-sync mapping, conventional relation types, and edge cases, read **`linking.md`**.
|
|
446
651
|
|
|
652
|
+
---
|
|
653
|
+
|
|
447
654
|
## Writing — governance in one paragraph
|
|
448
655
|
|
|
449
656
|
Every write returns a `status` field:
|
|
@@ -503,27 +710,158 @@ Rules:
|
|
|
503
710
|
|
|
504
711
|
Example response to the user:
|
|
505
712
|
|
|
506
|
-
> I queued **Delete task "Q2 plan review"** for your review. Destructive actions need your approval. Open it:
|
|
713
|
+
> I queued **Delete task "Q2 plan review"** for your review. Destructive actions need your approval. Open it: synap://open/proposal/prp_abc
|
|
507
714
|
|
|
508
715
|
Auto-approved by default (for agent API keys): `entity.create`, `entity.update`, `document.create`, `relation.create`, `view.create`, `profile.create`, `property_def.create`, `channel.create`, `memory.*`, all reads. Destructive actions (`delete`, `archive`, `purge`) always propose in agent-owned workspaces.
|
|
509
716
|
|
|
510
717
|
For the full whitelist, agent-user semantics, and workspace overrides, read **`governance.md`**.
|
|
511
718
|
|
|
719
|
+
---
|
|
720
|
+
|
|
721
|
+
# Capabilities — discover, run, and the when-blocked reflex
|
|
722
|
+
|
|
723
|
+
Capabilities are the verbs a workspace's connected services and applied
|
|
724
|
+
templates unlock — `gmail_send`, `gmail_search`, `calendar_create`,
|
|
725
|
+
`drive_search`, and so on. They are the bridge between "I can talk about it"
|
|
726
|
+
and "I can actually do it."
|
|
727
|
+
|
|
728
|
+
## Discover: `list_capabilities`
|
|
729
|
+
|
|
730
|
+
MCP: `synap_list_capabilities`. IS: `list_capabilities`. Takes
|
|
731
|
+
`{ query, kind?, limit? }` (plus `workspaceId`).
|
|
732
|
+
|
|
733
|
+
**Search first — never dump-and-eyeball.** A workspace can carry 100+
|
|
734
|
+
capability entries; call with a `query` describing the action you're after
|
|
735
|
+
("send email", "search calendar") and read the ranked, compact results. Don't
|
|
736
|
+
fetch the unfiltered list and scan it yourself.
|
|
737
|
+
|
|
738
|
+
Each entry carries:
|
|
739
|
+
|
|
740
|
+
- `name` (the `verbId` you pass to `run_capability`), `label`, backing tool
|
|
741
|
+
- `paramsSchema` — the shape `parameters` must satisfy; check it before calling
|
|
742
|
+
- `enabled` — `true` means it will run right now. `false`/DRAFT means it's
|
|
743
|
+
installed but the user hasn't approved it yet (Settings → Capabilities)
|
|
744
|
+
|
|
745
|
+
## Run: `run_capability`
|
|
746
|
+
|
|
747
|
+
MCP: `synap_run_capability`. IS: `run_capability`. Pass `verbId` (or
|
|
748
|
+
`skillId`) + `parameters` + `workspaceId`.
|
|
749
|
+
|
|
750
|
+
```json
|
|
751
|
+
{
|
|
752
|
+
"verbId": "gmail_send",
|
|
753
|
+
"parameters": { "to": "…", "subject": "…", "body": "…" },
|
|
754
|
+
"workspaceId": "{workspaceId}"
|
|
755
|
+
}
|
|
756
|
+
```
|
|
757
|
+
|
|
758
|
+
Check `paramsSchema` from discovery before calling — a missing required
|
|
759
|
+
parameter is refused, not guessed.
|
|
760
|
+
|
|
761
|
+
**`proposed` is a normal outcome, not a failure.** A capability run can land
|
|
762
|
+
as a proposal exactly like any other governed write (see `governance.md`):
|
|
763
|
+
tell the user why, share the `reviewUrl`, and don't retry.
|
|
764
|
+
|
|
765
|
+
**Provider results can be 200-with-error-body.** A successful HTTP call to
|
|
766
|
+
Gmail/Calendar/Drive can still carry `result.success: false` +
|
|
767
|
+
`result.error` — an auth expiry, a bad recipient, a quota limit. **Always
|
|
768
|
+
check `result.success`/`result.error` in the response before telling the
|
|
769
|
+
user the action worked.** A 200 status is not proof of success here.
|
|
770
|
+
|
|
771
|
+
## The when-blocked reflex
|
|
772
|
+
|
|
773
|
+
When you cannot do something the user asked for — no matching tool, a
|
|
774
|
+
"not found" verb, "no connection", "not enabled" — do not fabricate a result
|
|
775
|
+
and do not silently give up. Follow this order:
|
|
776
|
+
|
|
777
|
+
1. **Search first.** `list_capabilities({ query })` for what the user actually
|
|
778
|
+
wants. Capabilities are added over time; don't assume today's tool list is
|
|
779
|
+
the ceiling.
|
|
780
|
+
2. **Found but blocked?** If the verb exists but is DRAFT (not enabled) or its
|
|
781
|
+
backing connection is missing, tell the user exactly what to do and where:
|
|
782
|
+
"This needs Gmail connected — enable it in Settings → Capabilities" (or
|
|
783
|
+
the equivalent connect deep-link the error hands you). Don't attempt the
|
|
784
|
+
run again until they've acted.
|
|
785
|
+
3. **Still nothing? Search the marketplace.** `market.search({query, kind?})`
|
|
786
|
+
over what could be _installed_ (capabilities, automations, workspace
|
|
787
|
+
templates, cells) — a cache read, not a live fetch, so it's always fast.
|
|
788
|
+
4. **Found in the marketplace?** `market.install({slug, kind, version?})`. As
|
|
789
|
+
an agent this ALWAYS lands as a reviewable proposal — never auto-installs,
|
|
790
|
+
even with a grant on the verb itself. Share the `reviewUrl`; don't retry.
|
|
791
|
+
5. **Truly nothing, anywhere?** That is escalation-ladder **L2 empty → L3**:
|
|
792
|
+
say precisely what's missing (the action, not "I can't"), offer to capture
|
|
793
|
+
the gap, and if the need is structural (new capability package, template,
|
|
794
|
+
automation), propose via marketplace/install or meta tools — never
|
|
795
|
+
dead-end and never fabricate a result.
|
|
796
|
+
|
|
797
|
+
<!-- brief:start -->
|
|
798
|
+
|
|
799
|
+
When blocked (ladder L2→L3): (1) `list_capabilities({query})` first — never
|
|
800
|
+
assume today's list is the ceiling. (2) Found but DRAFT/no connection → tell
|
|
801
|
+
the user exactly what to enable/connect and where; don't retry until they've
|
|
802
|
+
acted. (3) Still nothing → `market.search({query, kind?})`; `market.install`
|
|
803
|
+
on a hit always proposes for an agent. (4) Truly nothing → say precisely
|
|
804
|
+
what's missing, offer to capture the gap or propose L3 structure — never
|
|
805
|
+
fabricate, never silent give-up. Provider 200-with-error-body: always check
|
|
806
|
+
`result.success`/`result.error` before claiming success.
|
|
807
|
+
|
|
808
|
+
<!-- brief:end -->
|
|
809
|
+
|
|
810
|
+
## Errors — what they mean, what to do next
|
|
811
|
+
|
|
812
|
+
| You see roughly... | Meaning | Next step |
|
|
813
|
+
| ------------------------------------------- | ------------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
|
|
814
|
+
| verb/skill "not found" in this workspace | No capability by that name is registered here | `list_capabilities({query})`; if nothing, tell the user what's missing |
|
|
815
|
+
| capability "not approved" / DRAFT | It exists but the user hasn't enabled it yet | Tell the user to enable it (Settings → Capabilities); don't retry |
|
|
816
|
+
| a required parameter is missing | Your `parameters` didn't satisfy the verb's `paramsSchema` | Re-check `paramsSchema` from discovery, fill the gap, retry once |
|
|
817
|
+
| no connection / credential for this service | The verb needs a connected account (Gmail, Calendar, …) that isn't set up | Hand the user the connect link; don't retry until connected |
|
|
818
|
+
| `status: "proposed"` | Normal governed outcome — this run needs human approval | Share `summary` + `reviewUrl`; don't retry (see `governance.md`) |
|
|
819
|
+
| `status: "denied"` | Workspace policy blocked it outright | Explain the reason; don't retry |
|
|
820
|
+
| provider result with `success: false` | The call reached the provider but the provider itself rejected it | Read `result.error`; tell the user what actually happened, don't report success |
|
|
821
|
+
|
|
822
|
+
## What NOT to do
|
|
823
|
+
|
|
824
|
+
- Don't dump the full unfiltered capability list and eyeball it — search.
|
|
825
|
+
- Don't retry a `proposed` or `denied` result as if it were a transient error.
|
|
826
|
+
- Don't tell the user an action "worked" without checking `result.success`.
|
|
827
|
+
- Don't invent a capability, verb, or connection that discovery didn't return.
|
|
828
|
+
|
|
829
|
+
---
|
|
830
|
+
|
|
512
831
|
## Core writes
|
|
513
832
|
|
|
514
|
-
### Create an entity (
|
|
833
|
+
### Create an entity (schema-first, complete intent)
|
|
834
|
+
|
|
835
|
+
Before this call, use `/discover?userId=…&profileSlugs=<kind>` to read the
|
|
836
|
+
real fields, required/default values, constraints and reference targets. Omit
|
|
837
|
+
`workspaceId` for the pod/base schema and normal profile placement; pass it
|
|
838
|
+
only when the user or routing decision explicitly selected that workspace.
|
|
515
839
|
|
|
516
840
|
```json
|
|
517
841
|
POST /api/hub/entities
|
|
518
842
|
{
|
|
519
843
|
"userId": "{userId}",
|
|
520
|
-
"
|
|
521
|
-
"profileSlug": "task", // from /profiles — never guess
|
|
844
|
+
"profileSlug": "task", // from /discover — never guess
|
|
522
845
|
"title": "Weekly team sync",
|
|
523
|
-
"
|
|
846
|
+
"description": "Recurring planning sync",
|
|
847
|
+
"properties": { "status": "todo", "dueDate": "2026-07-21" },
|
|
848
|
+
"content": "# Agenda\n- Priorities\n- Risks",
|
|
849
|
+
"projectId": "{existingProjectId}",
|
|
850
|
+
"source": "agent"
|
|
524
851
|
}
|
|
525
852
|
```
|
|
526
853
|
|
|
854
|
+
The response has legacy `status`/`id` fields plus `writeReceipt`:
|
|
855
|
+
`pending` means a proposal exists and no entity is live; `applied` means the
|
|
856
|
+
reported direct write completed; `partial` means a follow-up (for example a
|
|
857
|
+
facet) failed after the entity applied. Never claim completion from `pending`,
|
|
858
|
+
and only enrich again when the receipt identifies a real missing fact.
|
|
859
|
+
|
|
860
|
+
For several entities, creation-time roles/facets, or relations that need one
|
|
861
|
+
review, submit one `POST /api/hub/capture/graph` plan instead of sequencing
|
|
862
|
+
independent creates. It returns a pending receipt and materializes on human
|
|
863
|
+
approval.
|
|
864
|
+
|
|
527
865
|
### Update an entity
|
|
528
866
|
|
|
529
867
|
```json
|
|
@@ -647,6 +985,244 @@ POST /api/hub/threads/{threadId}/messages
|
|
|
647
985
|
{ "userId": "{userId}", "role": "user", "content": "…" }
|
|
648
986
|
```
|
|
649
987
|
|
|
988
|
+
---
|
|
989
|
+
|
|
990
|
+
## Document embeds — live entities/views/cells inside markdown
|
|
991
|
+
|
|
992
|
+
Documents (`type: "markdown"`) can embed **live, rendering** Synap objects inline, not
|
|
993
|
+
just links. The browser's markdown engine parses a small set of remark **container
|
|
994
|
+
directives** and swaps them for real components — an entity card, a view, or a cell
|
|
995
|
+
— wherever they appear in the prose.
|
|
996
|
+
|
|
997
|
+
**This is a DOCUMENTS-only mechanism.** It is unrelated to the `[[kind:id|label]]`
|
|
998
|
+
inline chips described in `inline-patterns.md` — those render **only** in Companion
|
|
999
|
+
chat replies. Never put a `:::synap-*` directive in a chat reply, and never put a
|
|
1000
|
+
`[[…]]` chip in a document's `content`. Different surface, different grammar.
|
|
1001
|
+
|
|
1002
|
+
### Syntax
|
|
1003
|
+
|
|
1004
|
+
<!-- brief:start -->
|
|
1005
|
+
|
|
1006
|
+
A container directive: three colons, the directive name, `{attrs}` on the opening
|
|
1007
|
+
line, three colons alone on the closing line.
|
|
1008
|
+
|
|
1009
|
+
```
|
|
1010
|
+
:::synap-entity{id="ent_abc123"}
|
|
1011
|
+
:::
|
|
1012
|
+
```
|
|
1013
|
+
|
|
1014
|
+
| Directive | Required attrs | Optional attrs | Renders |
|
|
1015
|
+
| -------------- | --------------------------------------------- | -------------- | ------------------------------------------------- |
|
|
1016
|
+
| `synap-entity` | `id` (entity UUID) | — | Compact entity card (`__entity-block` cell) |
|
|
1017
|
+
| `synap-view` | `viewId` (view UUID) | — | Embedded, read-only view (`__embedded-view` cell) |
|
|
1018
|
+
| `synap-cell` | `instanceId` **OR** (`cellKey` + `cellProps`) | — | A persisted cell instance, or an inline cell ref |
|
|
1019
|
+
|
|
1020
|
+
Only real IDs from prior tool results — never invent one. This is a
|
|
1021
|
+
DOCUMENTS-only grammar: never use it in a chat reply, and never use a
|
|
1022
|
+
`[[kind:id|label]]` chip inside a document's `content`.
|
|
1023
|
+
|
|
1024
|
+
<!-- brief:end -->
|
|
1025
|
+
|
|
1026
|
+
For `synap-cell`: an explicit `instanceId` always wins if present — it renders a
|
|
1027
|
+
persisted cell instance from `/api/hub/cells`. Otherwise the pair `cellKey` +
|
|
1028
|
+
`cellProps` builds an inline ref (`cellRefFromLegacy`). `cellProps` is a JSON string,
|
|
1029
|
+
e.g. `cellProps='{"profileSlug":"task"}'`.
|
|
1030
|
+
|
|
1031
|
+
When you write a document's `content` directly (via `synap_create_document` /
|
|
1032
|
+
`POST /api/hub/documents`), you're writing raw markdown text — quote `cellProps`'
|
|
1033
|
+
JSON with single quotes (`cellProps='{"profileSlug":"task"}'`) so the inner `"`
|
|
1034
|
+
characters don't collide with the directive's own `key="value"` quoting; the
|
|
1035
|
+
markdown renderer (remark-directive) parses this directly, no escaping needed.
|
|
1036
|
+
|
|
1037
|
+
One caveat: if a human later opens the document in the rich-text editor, its
|
|
1038
|
+
Tiptap round-trip serializer re-emits every directive as `key="value"` and
|
|
1039
|
+
**drops any attribute value containing `"` or `}`** (it would otherwise corrupt
|
|
1040
|
+
the directive) — so an inline `cellProps` blob can be silently lost on the next
|
|
1041
|
+
editor save. For a cell you expect to survive editing, create it first
|
|
1042
|
+
(`synap_create_cell` / `POST /api/hub/cells`) and embed it by `instanceId`
|
|
1043
|
+
instead of inlining `cellProps`.
|
|
1044
|
+
|
|
1045
|
+
### Rules
|
|
1046
|
+
|
|
1047
|
+
- **Only real IDs from prior tool results.** Never invent an entity/view/instance
|
|
1048
|
+
ID. Create or look it up first (`synap_create_entity`, `synap_get_entities`,
|
|
1049
|
+
`synap_create_view`, `synap_create_cell`), then embed the ID you got back.
|
|
1050
|
+
- **Embeds are for DOCUMENTS.** The `[[…]]` inline chips are for Companion chat
|
|
1051
|
+
replies. Do not mix the two grammars across surfaces.
|
|
1052
|
+
- **Embed vs. link:** embed when the reader benefits from seeing the live
|
|
1053
|
+
object in place — a stat card inside a report, the linked meeting entity inside
|
|
1054
|
+
meeting notes, a pipeline view inside a status update. Link (`entities WHERE
|
|
1055
|
+
documentId = ?` attachment, or a plain reference to the ID) when you just need
|
|
1056
|
+
traceability and the reader doesn't need to see it rendered inline — most
|
|
1057
|
+
documents should still be _attached_ to one entity (see `writes.md`) regardless
|
|
1058
|
+
of whether they also embed others inline.
|
|
1059
|
+
- A directive with a missing/invalid required attribute renders a visible error
|
|
1060
|
+
block in the browser (`Error: View ID is required` / `Error: Cell type is
|
|
1061
|
+
required`) — always double-check the ID before writing the directive.
|
|
1062
|
+
|
|
1063
|
+
### Worked example 1 — meeting notes embedding the meeting entity
|
|
1064
|
+
|
|
1065
|
+
```json
|
|
1066
|
+
POST /api/hub/documents
|
|
1067
|
+
{
|
|
1068
|
+
"userId": "{userId}",
|
|
1069
|
+
"workspaceId": "{workspaceId}",
|
|
1070
|
+
"title": "Meeting notes — 2026-07-12",
|
|
1071
|
+
"type": "markdown",
|
|
1072
|
+
"entityId": "ent_event_kickoff",
|
|
1073
|
+
"content": "# Kickoff meeting\n\n:::synap-entity{id=\"ent_event_kickoff\"}\n:::\n\n## Decisions\n- Ship the pilot by August 1\n\n## Action items\n- [ ] Draft the rollout plan"
|
|
1074
|
+
}
|
|
1075
|
+
```
|
|
1076
|
+
|
|
1077
|
+
The event entity renders as a live card at the top of the notes — attendees,
|
|
1078
|
+
time, status stay current even if the entity changes later.
|
|
1079
|
+
|
|
1080
|
+
### Worked example 2 — status report embedding a pipeline view
|
|
1081
|
+
|
|
1082
|
+
```json
|
|
1083
|
+
POST /api/hub/documents
|
|
1084
|
+
{
|
|
1085
|
+
"userId": "{userId}",
|
|
1086
|
+
"workspaceId": "{workspaceId}",
|
|
1087
|
+
"title": "Weekly deals update",
|
|
1088
|
+
"type": "markdown",
|
|
1089
|
+
"entityId": "ent_project_eve",
|
|
1090
|
+
"content": "# Weekly update\n\nThree deals moved to negotiating this week.\n\n:::synap-view{viewId=\"view_deals_pipeline\"}\n:::\n\nSee the board above for the live state."
|
|
1091
|
+
}
|
|
1092
|
+
```
|
|
1093
|
+
|
|
1094
|
+
### Worked example 3 — report embedding an inline stat cell
|
|
1095
|
+
|
|
1096
|
+
```json
|
|
1097
|
+
POST /api/hub/documents
|
|
1098
|
+
{
|
|
1099
|
+
"userId": "{userId}",
|
|
1100
|
+
"workspaceId": "{workspaceId}",
|
|
1101
|
+
"title": "Q2 task summary",
|
|
1102
|
+
"type": "markdown",
|
|
1103
|
+
"entityId": "ent_project_eve",
|
|
1104
|
+
"content": "# Q2 summary\n\n:::synap-cell{cellKey=\"stat-card\" cellProps='{\"profileSlug\":\"task\"}'}\n:::\n\nOpen tasks are trending down."
|
|
1105
|
+
}
|
|
1106
|
+
```
|
|
1107
|
+
|
|
1108
|
+
Note the mixed quoting: the directive's own attribute values use double quotes
|
|
1109
|
+
(`cellKey="stat-card"`), so the outer `cellProps` value uses single quotes to hold
|
|
1110
|
+
its JSON — the JSON itself must not contain any `"` once flattened into the
|
|
1111
|
+
attribute string, or the serializer will drop it. When in doubt, prefer a
|
|
1112
|
+
persisted `instanceId` over inline `cellProps`.
|
|
1113
|
+
|
|
1114
|
+
---
|
|
1115
|
+
|
|
1116
|
+
## The Creative Loop — do once, then crystallize to reusable config
|
|
1117
|
+
|
|
1118
|
+
The core rhythm of real work in Synap: **do the thing once by hand, then crystallize what worked into reusable configuration.** You author; the user curates. Every crystallization is governed — a `proposed` result is the normal, successful outcome, never an error.
|
|
1119
|
+
|
|
1120
|
+
The loop has three moves that compound. Each takes a one-off act and, if it's worth repeating, turns it into standing structure:
|
|
1121
|
+
|
|
1122
|
+
| Do-once (author) | → Crystallize (curate) | Tool |
|
|
1123
|
+
| --------------------------------------- | ----------------------------------- | ----------------------------- |
|
|
1124
|
+
| Work a multi-step goal in a **session** | → a **playbook** (the process) | `promote_session_to_playbook` |
|
|
1125
|
+
| Show a result in a **cell** | → a **renderer** for an entity type | `promote_cell_to_renderer` |
|
|
1126
|
+
|
|
1127
|
+
### 1. Open a session for real work
|
|
1128
|
+
|
|
1129
|
+
When the task is a unit of work with a deliverable — research, a build, an investigation, a sprint — and there's no active session, **`start_session`** with a clear `goal` and `expectedOutputs`. The session is the spine that accrues results (see the focus-sessions skill). Don't open one for a one-shot lookup or a casual reply.
|
|
1130
|
+
|
|
1131
|
+
### 2. Create a cell to REPORT — don't dump data into chat
|
|
1132
|
+
|
|
1133
|
+
When you have something to _show_ the user — a list of leads, a summary, a comparison, a chart — author a **cell** with `create_cell` instead of pasting rows into the message.
|
|
1134
|
+
|
|
1135
|
+
- **Declare the data intent in `rendererSource`; do NOT pre-fetch.** Cells use a dynamic data-binding SDK: you describe what the cell needs (e.g. "the open leads in this workspace"), and the runtime binds the live data at render time. Never fetch rows yourself and inline them — that snapshot goes stale and defeats the cell.
|
|
1136
|
+
- Keep one cell to one job. A good, focused cell is the raw material for the next move.
|
|
1137
|
+
|
|
1138
|
+
### 3. Promote a good cell to a renderer — recurring presentation
|
|
1139
|
+
|
|
1140
|
+
When a cell is a _good, recurring way to present a whole entity type or step_ — e.g. every `bookmark`'s detail view, every `lead`'s list row — promote it with `promote_cell_to_renderer`:
|
|
1141
|
+
|
|
1142
|
+
- Pick the `profileSlug` (the entity type), the `slot` (`list` | `detail` | `dashboard`), and the `cellKey` from `create_cell`.
|
|
1143
|
+
- This is **governed**: for you it returns `{ status: "proposed", proposalId }`. That is the point — you author the renderer, the user reviews and curates it before it becomes every entity's view. Surface the proposal plainly ("I've proposed this as the detail view for bookmarks — review it when you like"), don't treat it as a failure.
|
|
1144
|
+
- Use `scope: "pod"` only when the presentation should apply in every workspace; default to workspace scope.
|
|
1145
|
+
|
|
1146
|
+
### 4. Promote a finished session to a playbook — recurring process
|
|
1147
|
+
|
|
1148
|
+
When the session is done **and the work was a repeatable process** (not a one-off), promote it with `promote_session_to_playbook({ sessionId })`. This captures the goal, tasks, expected outputs, and steps as a reusable session template — so next time the process starts pre-built instead of from scratch.
|
|
1149
|
+
|
|
1150
|
+
- Do this at the _end_, once the promised outputs are produced and verified.
|
|
1151
|
+
- Judge repeatability honestly: a bespoke, never-again investigation is not a playbook. A "weekly competitor scan" or "new-client onboarding" is.
|
|
1152
|
+
- Governed like the others — `promoted` (applied) or `proposed` (awaiting review) are both normal.
|
|
1153
|
+
|
|
1154
|
+
### The symmetry
|
|
1155
|
+
|
|
1156
|
+
Sessions and cells are the two things you _do_; playbooks and renderers are the two things you _keep_. The instinct to build: **first do it once concretely, watch it work, then offer to crystallize it** — and let the user decide what becomes standing config. Never crystallize speculatively before the one-off has proven itself.
|
|
1157
|
+
|
|
1158
|
+
This is escalation ladder **L4**: crystallize only after proof. Blocked/missing structure climbs L2→L3 first (`escalation-ladder.md`); L4 is the success path, not a substitute for discovery.
|
|
1159
|
+
|
|
1160
|
+
---
|
|
1161
|
+
|
|
1162
|
+
## Garden the graph — writes tell you their impact (the circling of action)
|
|
1163
|
+
|
|
1164
|
+
A write is no longer blind. When you create or update an entity, the response tells you what it did to the graph — **READ it and act**, so the graph stays connected instead of accumulating isolated, duplicated nodes.
|
|
1165
|
+
|
|
1166
|
+
**On create**, the response carries a `resolution` block:
|
|
1167
|
+
|
|
1168
|
+
- `existingSameProfile` — an entity with this exact name ALREADY exists as the same profile. Prefer **updating** it (`synap set entity <id>` · `PATCH /api/hub/entities/:id` · `update_entity`) over creating a duplicate.
|
|
1169
|
+
- `autoConnected` — same-name entities of a DIFFERENT profile are facets of one real thing, and the system has already woven them together with a `same_subject` relation. Acknowledge it; add a more specific relation if the real relationship is narrower than "same subject".
|
|
1170
|
+
- `suggestions` — entities worth linking. Create the relations that genuinely apply (`synap create relation` · `POST /api/hub/relations` · `create_relation`).
|
|
1171
|
+
|
|
1172
|
+
**On update**, the response carries an `impact` block — the entity's immediate relation neighbors. Read it and resolve secondary effects: supersede a now-stale entity, update its dependents, re-link.
|
|
1173
|
+
|
|
1174
|
+
**The circling of action** for any structural write (updating the vision, the architecture, a decision record, a codebase map):
|
|
1175
|
+
|
|
1176
|
+
1. **Write** — the response shows collisions + connections; don't ignore it.
|
|
1177
|
+
2. **If it already exists** (`existingSameProfile`) → update it, don't duplicate.
|
|
1178
|
+
3. **The same-name facets are auto-woven** (`autoConnected`) → extend the links where the relationship is more specific than `same_subject`.
|
|
1179
|
+
4. **Act on `suggestions`** → link what genuinely relates.
|
|
1180
|
+
5. **On update** → resolve the `impact` neighbors (supersede / update / re-link).
|
|
1181
|
+
|
|
1182
|
+
The principle: **more data is better when it is structured.** The graph now helps you keep it structured — never leave an entity isolated, never blindly overwrite. (Matching is exact-name today; it will deepen over time — so still link deliberately when you know two things relate but their names differ.)
|
|
1183
|
+
|
|
1184
|
+
---
|
|
1185
|
+
|
|
1186
|
+
## Decide the next action — advance the goal, don't just answer
|
|
1187
|
+
|
|
1188
|
+
You are an adjunct, not a reply box. A good assistant doesn't wait to be told the next step — after you answer, you ask **what is the highest-leverage next move toward the goal, and should I make it now?** — then you make it. Proactivity is this habit, run every turn; it is not a tone.
|
|
1189
|
+
|
|
1190
|
+
### The session is the spine of real work
|
|
1191
|
+
|
|
1192
|
+
A **focus session** is a goal-bound work room. It holds the goal, the promised outputs (deliverables), the tools/skills in play, and what's been produced so far — so it answers both _"where am I"_ and _"what am I working toward"_. Operate it with the triplet:
|
|
1193
|
+
|
|
1194
|
+
| Move | CLI | REST | IS tool |
|
|
1195
|
+
| ------------------------- | ---------------------------------------- | ------------------------------------------- | ---------------- |
|
|
1196
|
+
| open a session | `synap session start --goal "…"` | `POST /api/hub/focus-sessions` | `start_session` |
|
|
1197
|
+
| read it | `synap session get <id>` | `GET /api/hub/focus-sessions/:id` | `get_session` |
|
|
1198
|
+
| see active ones | `synap session list` | `GET /api/hub/focus-sessions?status=active` | `list_sessions` |
|
|
1199
|
+
| record progress / outputs | `synap session update <id> --progress N` | `PATCH /api/hub/focus-sessions/:id` | `update_session` |
|
|
1200
|
+
|
|
1201
|
+
- If you're already in a session (or a `## Active Session` block is in your context) → that's your frame. Read its goal and the gap between _promised outputs_ and _what's produced_, and pursue that gap.
|
|
1202
|
+
- If the user's intent is a **unit of work with a deliverable** (research, a plan, a build, an investigation) and no session is active → **open one** with a clear goal + expected outputs, so the work has a spine that accrues results. Don't open a session for a one-shot lookup or a casual reply.
|
|
1203
|
+
|
|
1204
|
+
### Decide ONE move, then make it
|
|
1205
|
+
|
|
1206
|
+
Pick the single highest-leverage next action toward the goal — not a checklist: answer · capture/structure what was said · create a task · link entities · advance or produce a promised deliverable · update the session's progress · propose an automation for a repeating pattern.
|
|
1207
|
+
|
|
1208
|
+
**Spin off a branch / sub-agent** when there's side-work that advances the goal but would **bloat the main channel** — e.g. "go find best practices for X", a deep research dive, a parallel investigation. Keep the main thread clean and let the branch do the heavy lifting and report back. Judge by complexity + channel hygiene, not by a fixed category.
|
|
1209
|
+
|
|
1210
|
+
**Land it through governance — act, don't just suggest.** When the move is clear, _make_ it: create the task, the research entity, the branch, the link, the session update. Every write passes through governance — it either auto-applies (safe, whitelisted writes) or becomes a one-click review. Either way, making the move is the safe path. Don't downgrade a clear next action into "you might want to…". Say _why_ in one line, then act.
|
|
1211
|
+
|
|
1212
|
+
**Reflect back into the session.** When work lands, update the session's progress and link what you produced — so the next turn reads a richer state and the loop compounds.
|
|
1213
|
+
|
|
1214
|
+
**Know when to stop.** When the promised outputs are produced and verified, the goal is done — say so and stop. Surface a _real_ next move when there is one; stay quiet when there isn't. Never manufacture busywork to seem active.
|
|
1215
|
+
|
|
1216
|
+
### The nudge vs. propose line
|
|
1217
|
+
|
|
1218
|
+
A concrete next action toward a _known_ goal → propose it (gated). A _speculative_ restructuring the user hasn't asked for (a new profile, a new view, splitting a workspace) → raise it as a question and let them decide.
|
|
1219
|
+
|
|
1220
|
+
### If you are a coding / terminal agent working _in_ a repo
|
|
1221
|
+
|
|
1222
|
+
Same habit, one addition: before you finish a piece of work, ask **"what's the next action, and what belongs in Synap?"** A decision you made, a gotcha you hit, a follow-up the work revealed, a task it spun off — capture it (`synap capture` / `synap note` / a task) and, when the work is a real unit, track it in a session. The point of the second brain is that the _next_ agent (or you, tomorrow) starts from what this turn learned instead of re-deriving it.
|
|
1223
|
+
|
|
1224
|
+
---
|
|
1225
|
+
|
|
650
1226
|
## Reading
|
|
651
1227
|
|
|
652
1228
|
**Start with `ask` — the one routed read door.** It classifies the question and
|
|
@@ -687,8 +1263,12 @@ GET /api/hub/graph/traverse?entityId={id}&maxDepth=2&workspaceId={id}
|
|
|
687
1263
|
GET /api/hub/memory?userId={userId}&query={keywords}
|
|
688
1264
|
```
|
|
689
1265
|
|
|
1266
|
+
**Never claim absence without searching this turn.** Asked "what do you know about X", "is there an X", "anything on X" — you MUST `ask`/`search_unified` for X first (and `list_entities` on the matching profile for "how many / list all X"). Only after a search returns nothing may you say "I didn't find anything matching X" — never assert "X does not exist." A just-created entity is searchable within seconds, so a confident "nothing exists" without a search this turn is a hard failure.
|
|
1267
|
+
|
|
690
1268
|
No SQL joins. The graph is the join.
|
|
691
1269
|
|
|
1270
|
+
---
|
|
1271
|
+
|
|
692
1272
|
## Multi-entity capture from free-form text
|
|
693
1273
|
|
|
694
1274
|
When the user pastes a block of unstructured content (a meeting transcript, an email, a LinkedIn bio), use the capture pipeline instead of chaining manual creates:
|
|
@@ -700,6 +1280,8 @@ POST /api/hub/capture/execute → commits (after user confirms)
|
|
|
700
1280
|
|
|
701
1281
|
The pipeline extracts multiple entities with their relations in one LLM call. Read **`capture.md`** for the full flow.
|
|
702
1282
|
|
|
1283
|
+
---
|
|
1284
|
+
|
|
703
1285
|
## Worked examples
|
|
704
1286
|
|
|
705
1287
|
### Example 1 — "Remind me to send the proposal to Acme on Friday"
|
|
@@ -754,6 +1336,8 @@ The pipeline extracts multiple entities with their relations in one LLM call. Re
|
|
|
754
1336
|
|
|
755
1337
|
3. If the user said why ("interesting for the onboarding project"), also create a relation to that project — never drop the reason as a plain comment, turn it into a link.
|
|
756
1338
|
|
|
1339
|
+
---
|
|
1340
|
+
|
|
757
1341
|
## CRM Workspaces — 4-Entity Model
|
|
758
1342
|
|
|
759
1343
|
Some workspaces use a CRM data structure with four entities: `person` and `company` (identity records), `deal` (pipeline record), and `client` (post-win relationship marker). Understand this pattern when proposing lead captures, deal updates, or campaign membership.
|
|
@@ -860,7 +1444,21 @@ POST /api/hub/relations
|
|
|
860
1444
|
|
|
861
1445
|
This model enables renewals (new deal linking to existing client), multi-stakeholder deals (multiple `linked_to_deal` relations per deal), campaigns with mixed entity types (persons + companies + deals as members), and clean churn tracking. It matches Synap's core pattern: entities + relations = graph.
|
|
862
1446
|
|
|
863
|
-
##
|
|
1447
|
+
## Resolution discipline — resolve BEFORE you create
|
|
1448
|
+
|
|
1449
|
+
Extracting people/companies from a digest, an email/DM thread, or notes is a RESOLUTION problem, not a creation problem. Follow this method every time (it is generic — the specific team roster and known aliases are DATA you fetch, never hardcoded):
|
|
1450
|
+
|
|
1451
|
+
1. **Search first, once (batched).** Before proposing any person/company, run a SINGLE batched query (`search_unified`, plus `list_entities` if needed) that covers ALL candidate names, handles, emails and aliases at once. Never run one query per candidate — that is a cost failure.
|
|
1452
|
+
2. **Reuse on match → add an alias.** If a candidate matches an existing entity, reference the existing entity by its real id (e.g. `existingEntityId` on `propose_entity_graph`) and add the newly-seen surface form (a handle, an alternate spelling) to the entity's `aliases` — do NOT create a second `person`/`company`. The `person` profile carries `email`, `discord-handle` and `aliases` for exactly this.
|
|
1453
|
+
3. **Never placeholder.** Never mint an entity whose identity is unstated — "Not publicly disclosed", "unknown", "TBD", an empty name, or a bare handle with no person behind it. Fold that unstated thing into the description of a related entity instead.
|
|
1454
|
+
4. **Team is not a contact.** Internal senders — your own side of a `client-comms` thread (the agency's own team) — are NEVER captured as the client's contacts. Only the external party becomes a person/company/deal. Team roster may appear in `orient.teamRoster` — treat as internal.
|
|
1455
|
+
5. **Connect the graph.** Every entity you propose should attach via at least one relation (person `works_at` company, person `linked_to_deal` deal, …). A name mentioned only in passing, with no relation and no stated identity, is not worth capturing as its own entity.
|
|
1456
|
+
|
|
1457
|
+
An extraction that creates a duplicate "Sarah Chen", or a `person` titled "Unknown sender", is a failure — resolve, reuse, and alias instead.
|
|
1458
|
+
|
|
1459
|
+
---
|
|
1460
|
+
|
|
1461
|
+
## Common mistakes — core data operations
|
|
864
1462
|
|
|
865
1463
|
1. **Creating orphan entities.** Always connect to at least one other entity on creation. Search first; if nothing links, reconsider whether this should be memory.
|
|
866
1464
|
2. **Guessing profile slugs.** Always `GET /profiles` first. `deal`, `capture`, and custom profiles may not exist in this workspace.
|
|
@@ -870,6 +1468,10 @@ This model enables renewals (new deal linking to existing client), multi-stakeho
|
|
|
870
1468
|
6. **Not knowing your userId.** Use `{SYNAP_USER_ID}` from the env (set by `synap connect`). Or call `GET /api/hub/users/me` → `.id` once and cache it. Never hardcode or guess.
|
|
871
1469
|
7. **Skipping the search step.** Duplicates degrade the graph more than missing data.
|
|
872
1470
|
8. **Forgetting that `GET /channels/personal` needs `hub-protocol.write`** scope — it's get-or-create, not a pure read.
|
|
1471
|
+
9. **Routing known-structure data through free-text capture.** If you already know the profileSlug + fields, create the entity directly — smart capture can degrade to a single flat note.
|
|
1472
|
+
10. **Paragraph session goals.** The goal is one line; put detail and deliverables in expectedOutputs.
|
|
1473
|
+
|
|
1474
|
+
---
|
|
873
1475
|
|
|
874
1476
|
## AI Inline Patterns — reference entities in your replies
|
|
875
1477
|
|
|
@@ -895,6 +1497,23 @@ When the user is interacting with Synap's AI Companion (the in-browser chat pane
|
|
|
895
1497
|
- **Only in Companion replies.** These patterns are silently ignored in non-companion channels, documents, and memory. Do not use them there.
|
|
896
1498
|
- **Combine with prose.** Don't lead with a chip — embed it naturally: `"Here are your open deals → [[view:xyz|Deals Pipeline]] · [[open:side|view:xyz]]"`
|
|
897
1499
|
|
|
1500
|
+
### Proposals
|
|
1501
|
+
|
|
1502
|
+
There is no `[[open:…|proposal:…]]` chip — `open`'s `resourceType` only accepts
|
|
1503
|
+
`entity`, `view`, `doc`, `cell`, `channel`. A proposal is not one of those, so
|
|
1504
|
+
never invent that form.
|
|
1505
|
+
|
|
1506
|
+
When a write returns `status: "proposed"`, the response also carries a
|
|
1507
|
+
`reviewUrl` (a full Synap Studio URL). Surface it as a plain markdown link, and
|
|
1508
|
+
add one sentence explaining why the write was proposed instead of auto-applied:
|
|
1509
|
+
|
|
1510
|
+
> Queued the task deletion for your review — destructive actions always need approval: [Review proposal](https://pod.example.com/proposals/prp_abc)
|
|
1511
|
+
|
|
1512
|
+
`"proposed"` is normal, not an error — don't apologize for it or wait for the
|
|
1513
|
+
user to approve before continuing the conversation.
|
|
1514
|
+
|
|
1515
|
+
---
|
|
1516
|
+
|
|
898
1517
|
## Focus Sessions — Goal-Bound Work Rooms
|
|
899
1518
|
|
|
900
1519
|
A **focus session** is a named, multi-step work room where you and AI agents collaborate on a specific goal. Use one whenever the work has a clear end state, will take more than one exchange, or involves multiple agents.
|
|
@@ -939,9 +1558,418 @@ synap session close <id> --workspace <id> [--recap "what was done"] # close +
|
|
|
939
1558
|
|
|
940
1559
|
Note: `synap session start` creates a session directly (the agent-facing path). All hub-protocol writes are governance-gated server-side; the in-browser AI companion surfaces session creation through the proposal flow.
|
|
941
1560
|
|
|
1561
|
+
**MCP door**: after `synap_start_session` returns, call `synap_get_channel` to get a personal channel for the session, then `synap_post_message` with `triggerAI:true` to dispatch the IS agent for autonomous work on the goal. The agent's produced entities link back to the session via the graph.
|
|
1562
|
+
|
|
942
1563
|
**Discoverability**: the `active-sessions` bento widget is on the default home dashboard. Sessions group their related proposals under a shared `correlationId` in the Proposal Review Board.
|
|
943
1564
|
|
|
944
|
-
|
|
1565
|
+
---
|
|
1566
|
+
|
|
1567
|
+
# Automations
|
|
1568
|
+
|
|
1569
|
+
Create workflow automations that trigger automatically based on events, schedules, or webhooks.
|
|
1570
|
+
|
|
1571
|
+
## Automation Structure
|
|
1572
|
+
|
|
1573
|
+
Every automation has:
|
|
1574
|
+
|
|
1575
|
+
1. **Trigger** (exactly one) — what starts the automation
|
|
1576
|
+
2. **Steps** (one or more) — commands, conditions, delays, outputs connected in a flow
|
|
1577
|
+
|
|
1578
|
+
## Trigger Types
|
|
1579
|
+
|
|
1580
|
+
| Type | Config | Example |
|
|
1581
|
+
| --------- | ---------------------------- | ------------------------------------ |
|
|
1582
|
+
| `event` | `{ eventPattern, filters? }` | Entity created with specific profile |
|
|
1583
|
+
| `cron` | `{ expression }` | Daily at 9am: `"0 9 * * *"` |
|
|
1584
|
+
| `webhook` | `{ webhookSubscriptionId }` | External service sends data |
|
|
1585
|
+
| `manual` | `{}` | User-triggered from UI |
|
|
1586
|
+
|
|
1587
|
+
### Event Patterns
|
|
1588
|
+
|
|
1589
|
+
Format: `{subjectType}.{action}.completed`
|
|
1590
|
+
|
|
1591
|
+
Common patterns:
|
|
1592
|
+
|
|
1593
|
+
- `entity.create.completed` — entity created and persisted
|
|
1594
|
+
- `entity.update.completed` — entity updated
|
|
1595
|
+
- `entity.delete.completed` — entity deleted
|
|
1596
|
+
- `document.create.completed` — document created
|
|
1597
|
+
- `document.update.completed` — document updated
|
|
1598
|
+
|
|
1599
|
+
Filters narrow the event to specific conditions:
|
|
1600
|
+
|
|
1601
|
+
```json
|
|
1602
|
+
{
|
|
1603
|
+
"eventPattern": "entity.create.completed",
|
|
1604
|
+
"filters": { "profileSlug": "task", "metadata.priority": "high" }
|
|
1605
|
+
}
|
|
1606
|
+
```
|
|
1607
|
+
|
|
1608
|
+
### Cron Expressions
|
|
1609
|
+
|
|
1610
|
+
Standard 5-field cron (minute hour day month weekday):
|
|
1611
|
+
|
|
1612
|
+
- `"0 9 * * *"` — daily at 9am
|
|
1613
|
+
- `"0 9 * * MON"` — every Monday at 9am
|
|
1614
|
+
- `"*/30 * * * *"` — every 30 minutes
|
|
1615
|
+
- `"0 0 1 * *"` — first day of month at midnight
|
|
1616
|
+
|
|
1617
|
+
## Step Types
|
|
1618
|
+
|
|
1619
|
+
### command
|
|
1620
|
+
|
|
1621
|
+
Execute an intelligence command. Reference by `commandId` or describe inline.
|
|
1622
|
+
|
|
1623
|
+
```json
|
|
1624
|
+
{
|
|
1625
|
+
"id": "extract",
|
|
1626
|
+
"type": "command",
|
|
1627
|
+
"data": {
|
|
1628
|
+
"commandTitle": "Extract key entities",
|
|
1629
|
+
"inputMapping": {
|
|
1630
|
+
"content": "{{trigger.payload.entity.content}}",
|
|
1631
|
+
"context": "{{trigger.payload.entity.name}}"
|
|
1632
|
+
}
|
|
1633
|
+
}
|
|
1634
|
+
}
|
|
1635
|
+
```
|
|
1636
|
+
|
|
1637
|
+
**Input mapping** uses template syntax:
|
|
1638
|
+
|
|
1639
|
+
- `{{trigger.payload.*}}` — data from the triggering event
|
|
1640
|
+
- `{{steps.<stepId>.output.*}}` — output from a prior step
|
|
1641
|
+
- `{{loop.item}}` — current item in a loop
|
|
1642
|
+
|
|
1643
|
+
### condition
|
|
1644
|
+
|
|
1645
|
+
Branch the flow based on a boolean expression.
|
|
1646
|
+
|
|
1647
|
+
```json
|
|
1648
|
+
{
|
|
1649
|
+
"id": "check-priority",
|
|
1650
|
+
"type": "condition",
|
|
1651
|
+
"data": {
|
|
1652
|
+
"label": "High priority?",
|
|
1653
|
+
"expression": "trigger.payload.entity.metadata.priority === 'high'",
|
|
1654
|
+
"trueLabel": "Yes",
|
|
1655
|
+
"falseLabel": "No"
|
|
1656
|
+
}
|
|
1657
|
+
}
|
|
1658
|
+
```
|
|
1659
|
+
|
|
1660
|
+
Conditions have two output handles: `yes` and `no`. Connect subsequent steps to the appropriate handle.
|
|
1661
|
+
|
|
1662
|
+
### delay
|
|
1663
|
+
|
|
1664
|
+
Wait before continuing.
|
|
1665
|
+
|
|
1666
|
+
```json
|
|
1667
|
+
{
|
|
1668
|
+
"id": "wait",
|
|
1669
|
+
"type": "delay",
|
|
1670
|
+
"data": { "duration": "5m", "label": "Cool down" }
|
|
1671
|
+
}
|
|
1672
|
+
```
|
|
1673
|
+
|
|
1674
|
+
Supported durations: `30s`, `5m`, `1h`, `1d`, `1w`.
|
|
1675
|
+
|
|
1676
|
+
### output
|
|
1677
|
+
|
|
1678
|
+
Terminal action — the end result of the automation.
|
|
1679
|
+
|
|
1680
|
+
```json
|
|
1681
|
+
{
|
|
1682
|
+
"id": "notify",
|
|
1683
|
+
"type": "output",
|
|
1684
|
+
"data": {
|
|
1685
|
+
"label": "Send notification",
|
|
1686
|
+
"outputType": "notification",
|
|
1687
|
+
"config": {
|
|
1688
|
+
"message": "New high-priority task: {{trigger.payload.entity.name}}"
|
|
1689
|
+
}
|
|
1690
|
+
}
|
|
1691
|
+
}
|
|
1692
|
+
```
|
|
1693
|
+
|
|
1694
|
+
Output types:
|
|
1695
|
+
|
|
1696
|
+
- `notification` — in-app notification to the user
|
|
1697
|
+
- `entity_create` — create a new entity (config: `{ profileSlug, title, properties }`)
|
|
1698
|
+
- `entity_update` — update an existing entity (config: `{ entityId, properties }`)
|
|
1699
|
+
- `webhook` — POST to external URL (config: `{ url, headers?, body }`)
|
|
1700
|
+
- `channel_message` — post a message to a channel (config: `{ channelId, content }`)
|
|
1701
|
+
|
|
1702
|
+
### loop
|
|
1703
|
+
|
|
1704
|
+
Iterate over a collection from a prior step.
|
|
1705
|
+
|
|
1706
|
+
```json
|
|
1707
|
+
{
|
|
1708
|
+
"id": "for-each-result",
|
|
1709
|
+
"type": "loop",
|
|
1710
|
+
"data": {
|
|
1711
|
+
"label": "For each search result",
|
|
1712
|
+
"iteratorExpression": "steps.search.output.results",
|
|
1713
|
+
"itemVariable": "item"
|
|
1714
|
+
}
|
|
1715
|
+
}
|
|
1716
|
+
```
|
|
1717
|
+
|
|
1718
|
+
Inside the loop, reference `{{loop.item}}` for the current element.
|
|
1719
|
+
|
|
1720
|
+
## Connecting Steps
|
|
1721
|
+
|
|
1722
|
+
Steps are connected via `dependsOn` (which step must complete first) and optional `conditionBranch` (which branch to follow from a condition).
|
|
1723
|
+
|
|
1724
|
+
```json
|
|
1725
|
+
{
|
|
1726
|
+
"steps": [
|
|
1727
|
+
{ "id": "check", "type": "condition", "data": {...} },
|
|
1728
|
+
{ "id": "notify-high", "type": "output", "data": {...}, "dependsOn": ["check"], "conditionBranch": "yes" },
|
|
1729
|
+
{ "id": "log-normal", "type": "output", "data": {...}, "dependsOn": ["check"], "conditionBranch": "no" }
|
|
1730
|
+
]
|
|
1731
|
+
}
|
|
1732
|
+
```
|
|
1733
|
+
|
|
1734
|
+
## Discovering Commands
|
|
1735
|
+
|
|
1736
|
+
Before creating automations with command steps, call `list_commands` to discover available intelligence commands in the workspace. Use the command `id` in the step's `commandId` field.
|
|
1737
|
+
|
|
1738
|
+
If no suitable command exists, you can leave `commandId` empty and set `commandTitle` + `inputMapping` — the execution engine will use the title as a prompt template.
|
|
1739
|
+
|
|
1740
|
+
## Vault References
|
|
1741
|
+
|
|
1742
|
+
Automation configs that need secrets (API keys, auth tokens) should use vault references instead of hardcoded values:
|
|
1743
|
+
|
|
1744
|
+
- `vault://secret-uuid` — resolves to the full secret value at runtime
|
|
1745
|
+
- `vault://secret-uuid/field-name` — resolves a specific field from a JSON secret
|
|
1746
|
+
|
|
1747
|
+
Only server-encrypted secrets can be resolved by the automation engine. The user must store the credential in the vault first.
|
|
1748
|
+
|
|
1749
|
+
## Best Practices
|
|
1750
|
+
|
|
1751
|
+
1. **Keep it simple** — Start with trigger → command → output. Add complexity only when needed.
|
|
1752
|
+
2. **Name clearly** — Use descriptive labels: "When task created with high priority" not "Trigger 1".
|
|
1753
|
+
3. **One purpose** — Each automation should do one thing well. Compose multiple automations rather than building one complex flow.
|
|
1754
|
+
4. **Filter early** — Use trigger filters to avoid unnecessary execution. Don't use a condition step when a trigger filter suffices.
|
|
1755
|
+
5. **Test first** — Create automations as `draft` status. Let the user review the flow visualization before activating.
|
|
1756
|
+
|
|
1757
|
+
## Example: Auto-archive completed tasks
|
|
1758
|
+
|
|
1759
|
+
```json
|
|
1760
|
+
{
|
|
1761
|
+
"name": "Auto-archive completed tasks",
|
|
1762
|
+
"description": "When a task status changes to 'done', archive it after 24 hours",
|
|
1763
|
+
"trigger": {
|
|
1764
|
+
"type": "event",
|
|
1765
|
+
"config": {
|
|
1766
|
+
"eventPattern": "entity.update.completed",
|
|
1767
|
+
"filters": { "profileSlug": "task", "metadata.status": "done" }
|
|
1768
|
+
}
|
|
1769
|
+
},
|
|
1770
|
+
"steps": [
|
|
1771
|
+
{
|
|
1772
|
+
"id": "wait-24h",
|
|
1773
|
+
"type": "delay",
|
|
1774
|
+
"data": { "duration": "1d", "label": "Wait 24h" }
|
|
1775
|
+
},
|
|
1776
|
+
{
|
|
1777
|
+
"id": "archive",
|
|
1778
|
+
"type": "output",
|
|
1779
|
+
"data": {
|
|
1780
|
+
"label": "Archive task",
|
|
1781
|
+
"outputType": "entity_update",
|
|
1782
|
+
"config": {
|
|
1783
|
+
"entityId": "{{trigger.payload.entity.id}}",
|
|
1784
|
+
"properties": { "archived": true }
|
|
1785
|
+
}
|
|
1786
|
+
},
|
|
1787
|
+
"dependsOn": ["wait-24h"]
|
|
1788
|
+
}
|
|
1789
|
+
]
|
|
1790
|
+
}
|
|
1791
|
+
```
|
|
1792
|
+
|
|
1793
|
+
## Example: Notify on high-priority tasks
|
|
1794
|
+
|
|
1795
|
+
```json
|
|
1796
|
+
{
|
|
1797
|
+
"name": "High-priority task alerts",
|
|
1798
|
+
"description": "Send a notification when a high-priority task is created",
|
|
1799
|
+
"trigger": {
|
|
1800
|
+
"type": "event",
|
|
1801
|
+
"config": {
|
|
1802
|
+
"eventPattern": "entity.create.completed",
|
|
1803
|
+
"filters": { "profileSlug": "task" }
|
|
1804
|
+
}
|
|
1805
|
+
},
|
|
1806
|
+
"steps": [
|
|
1807
|
+
{
|
|
1808
|
+
"id": "check-priority",
|
|
1809
|
+
"type": "condition",
|
|
1810
|
+
"data": {
|
|
1811
|
+
"label": "High priority?",
|
|
1812
|
+
"expression": "trigger.payload.entity.metadata.priority === 'high'"
|
|
1813
|
+
}
|
|
1814
|
+
},
|
|
1815
|
+
{
|
|
1816
|
+
"id": "notify",
|
|
1817
|
+
"type": "output",
|
|
1818
|
+
"data": {
|
|
1819
|
+
"label": "Alert: high-priority task",
|
|
1820
|
+
"outputType": "notification",
|
|
1821
|
+
"config": {
|
|
1822
|
+
"message": "New urgent task: {{trigger.payload.entity.name}}"
|
|
1823
|
+
}
|
|
1824
|
+
},
|
|
1825
|
+
"dependsOn": ["check-priority"],
|
|
1826
|
+
"conditionBranch": "yes"
|
|
1827
|
+
}
|
|
1828
|
+
]
|
|
1829
|
+
}
|
|
1830
|
+
```
|
|
1831
|
+
|
|
1832
|
+
---
|
|
1833
|
+
|
|
1834
|
+
# Diagnosing what an AI did — the runs door
|
|
1835
|
+
|
|
1836
|
+
When a capture, automation, playbook, or session **didn't do what you (or the user) expected** — a facet wasn't attached, an entity landed in the wrong workspace, a step failed silently — do **not** guess or apologize. Every AI run leaves a trace. Read it.
|
|
1837
|
+
|
|
1838
|
+
## The one tool
|
|
1839
|
+
|
|
1840
|
+
`synap_diagnose` — the unified view of what an AI did across flows.
|
|
1841
|
+
|
|
1842
|
+
- **No args** → the recent run feed (automation · playbook · capture · session), newest first. Each run has an `id`, `flowType`, `status`, and `flowName`.
|
|
1843
|
+
- **`runId` + `flowType`** → that run's **activity timeline**. For a **capture** run this is its decision + trace events: for each thing that was dropped/coerced, a machine-readable `reason` **and an actionable `fixHint`**.
|
|
1844
|
+
|
|
1845
|
+
(CLI equivalent for the operator: `synap diagnose` and `synap diagnose <captureId> --flow capture`.)
|
|
1846
|
+
|
|
1847
|
+
## The reflex
|
|
1848
|
+
|
|
1849
|
+
> "The capture didn't attach the client role" → `synap_diagnose({ runId: <captureId>, flowType: "capture" })` → read the `capture_trace` rows → act on the `fixHint`.
|
|
1850
|
+
|
|
1851
|
+
A capture's `id` **is** its correlationId — the same id stamped on the entities it created and returned to you as `captureId`. So you always have the key to diagnose your own last capture.
|
|
1852
|
+
|
|
1853
|
+
## Reading a capture trace
|
|
1854
|
+
|
|
1855
|
+
Each `capture_trace` activity item names a pipeline stage (`component`), why it stopped (`reason`), and what to do (`fixHint`). The common ones:
|
|
1856
|
+
|
|
1857
|
+
| reason | what happened | what to do |
|
|
1858
|
+
| -------------------------- | ----------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- |
|
|
1859
|
+
| `not_in_creatable_catalog` | a role/kind you asked to create isn't a creatable entity kind — it's a **facet** (client, partner, prospect, investor…) | resolve the real entity first, then `attach_facet` for the role — never a second entity for a hat |
|
|
1860
|
+
| `kind_mismatch` | the facet's `applicableKinds` didn't include the target entity's kind | attach the role to an entity of a kind the role applies to |
|
|
1861
|
+
| `slug_coerce` | a profileSlug was normalized/renamed to the canonical one | use the canonical slug next time (see `list_profiles`) |
|
|
1862
|
+
| `materialize_skip` | an operation was skipped (dedup or a missing dependency) | check the dedup match; re-capture only what's genuinely new |
|
|
1863
|
+
|
|
1864
|
+
If a run carries a `channelId` (playbook / session / automation), its message-level story lives in that channel — the timeline points you there rather than duplicating it.
|
|
1865
|
+
|
|
1866
|
+
## Why this matters
|
|
1867
|
+
|
|
1868
|
+
The point of the flywheel is that mistakes are **visible and fixable**, not silent. If you can see what happened, you can correct it — and every correction teaches the routing. Reach for `synap_diagnose` before you conclude "it didn't work."
|
|
1869
|
+
|
|
1870
|
+
---
|
|
1871
|
+
|
|
1872
|
+
## Workspace design — is this concern a WORKSPACE, or something smaller?
|
|
1873
|
+
|
|
1874
|
+
Before you create a workspace, run the decision rule. A workspace (an operational **domain**) is the heaviest structure in the pod — it owns kinds, confers roles, carries its own team and automations. Most new concerns are NOT domains; they are a **hat**, an **initiative**, or a **stage**. Creating a workspace for one of those is the anti-pattern that fragments the graph. Decide first, then create.
|
|
1875
|
+
|
|
1876
|
+
## The decision rule — a concern earns a workspace ONLY if ALL FOUR hold
|
|
1877
|
+
|
|
1878
|
+
1. **Owns kinds** — it is source-of-truth for a noun nothing else owns (CRM owns `person`/`company`; Operations owns `engagement`/`deliverable`). If it only _reads_ or _annotates_ another domain's kinds, it is not a domain.
|
|
1879
|
+
2. **Own team** — a distinct set of operators/collaborators works it (separation of _who_, not just _what_).
|
|
1880
|
+
3. **Native automations/tools** — it runs behavior its neighbors don't (its own capabilities, playbooks, triggers).
|
|
1881
|
+
4. **Stable** — it persists across clients and campaigns. If it is per-client or per-campaign, it is time-bound, not a domain.
|
|
1882
|
+
|
|
1883
|
+
**All four, or it is not a workspace.** Then fork it to the right lighter structure:
|
|
1884
|
+
|
|
1885
|
+
| If the concern is… | It is a… | Substrate | Example |
|
|
1886
|
+
| ----------------------------------------------------------- | -------------- | -------------------------------------- | ------------------------------------------- |
|
|
1887
|
+
| a **role/hat** an existing entity wears in a domain | **Facet** | `attach_facet` (`profileKind: "role"`) | `client`, `sponsor`, `prospect`, `investor` |
|
|
1888
|
+
| a **cross-cutting, time-bound initiative** spanning domains | **Project** | `create_project` (a lens) | a campaign, an engagement, a launch |
|
|
1889
|
+
| a **stage/filter WITHIN a domain** | **State/View** | a `status` property def + a view | pipeline stage, "active"/"archived" |
|
|
1890
|
+
|
|
1891
|
+
## The decision procedure (follow in order)
|
|
1892
|
+
|
|
1893
|
+
1. **Name the source-of-truth noun.** What kind would this workspace _own_ that no existing workspace owns? Run `list_profiles` — if the noun already lives in another domain, you have a facet or a project, not a domain. STOP.
|
|
1894
|
+
2. **Test all four conditions.** Owns kinds AND own team AND native automations AND stable. Any one fails → fork below.
|
|
1895
|
+
3. **If it's a hat** (a status/role on an entity that already exists elsewhere) → resolve the entity, `attach_facet`. Never a workspace, never a second entity.
|
|
1896
|
+
4. **If it's time-bound work across domains** → `create_project` and set it as the lens; the work files into it from whatever workspace holds the data. **A project is a COMMITMENT WITH GRAVITY** — a real initiative that ties work together (a campaign, an engagement, a client, a launch). Tasks, plans, repos, themes, and topics are **entities**, never projects. Before you create one: (a) **search existing projects first** (`synap orient` / `GET /api/hub/projects`) and prefer **linking into an existing project** via `belongs_to_project` — near-duplicate names are rejected with the existing candidates; (b) an agent-created project must cite **≥5 existing entities** that would belong to it as `evidenceEntityIds` — the backend rejects a project with no gravity and tells you to store it as an entity or reuse an existing project instead; (c) **never create a project for the pod owner's own company** — the company _is_ the pod, not a project inside it.
|
|
1897
|
+
5. **If it's a stage inside a domain** → add a `status` property def (`create_property_def`) and a view; don't split the stage into its own space.
|
|
1898
|
+
6. **Only if all four held** → **template first** (escalation ladder L3):
|
|
1899
|
+
`market.search({query, kind: "template"})` and propose install of a matching
|
|
1900
|
+
template before freehand `create_workspace`. Freehand create is last resort
|
|
1901
|
+
and always proposed — a deliberate move, offer it to the user (see
|
|
1902
|
+
`lenses.md`). Then declare how it lives in the graph (see `workspace-edges.md`).
|
|
1903
|
+
|
|
1904
|
+
## The CRM corollary — the load-bearing example
|
|
1905
|
+
|
|
1906
|
+
Operational state — **prospect → client → delivered** — is a **FLOW across domains**, expressed as **facets + a triggered project**, NEVER as workspaces and NEVER by bolting delivery onto the identity domain.
|
|
1907
|
+
|
|
1908
|
+
- CRM = **who** (owns `person`/`company`, confers the `lead`/`client` facets).
|
|
1909
|
+
- Operations = **what we do for them** (owns `engagement`/`contract`/`deliverable`).
|
|
1910
|
+
- The bridge: attaching the `client` facet in CRM **triggers** an engagement project in Operations (see the _triggers_ edge in `workspace-edges.md`).
|
|
1911
|
+
|
|
1912
|
+
Bolting delivery-ops onto CRM was the anti-pattern: it made one workspace own two unrelated source-of-truth concerns and blurred _who_ the entity is with _what work_ is happening. Split by ownership; bridge by facet + trigger.
|
|
1913
|
+
|
|
1914
|
+
## Why this matters
|
|
1915
|
+
|
|
1916
|
+
A workspace is a boundary; a facet/project/state is a connection. Boundaries fragment the graph — they should be rare and earned. When you catch yourself about to create a workspace, check the four conditions: nine times out of ten the honest answer is a facet on an entity that already exists, a project that spans what's already there, or a status field on a kind you already own.
|
|
1917
|
+
|
|
1918
|
+
---
|
|
1919
|
+
|
|
1920
|
+
## Workspace edges — how a domain LIVES IN THE GRAPH
|
|
1921
|
+
|
|
1922
|
+
A workspace is never an island. Before you create one (or reason about an existing one), map its **edges**: what it consumes, what it provides, what it triggers, what subject it shares, what spans it. Domains are wired together by a small, fixed taxonomy — and each edge type maps to a specific substrate. Knowing the taxonomy is what lets you reason about a new domain's _position_ instead of dropping it in disconnected.
|
|
1923
|
+
|
|
1924
|
+
> The two graphs are orthogonal. This is the **data-flow graph** (what reads/writes/triggers what) — the one we model. The **org graph** (who owns/operates a domain) is workspace membership only. "Comms contains Marketing" is org; the _data_ edge is "Marketing **consumes** Comms' brand." Keep them separate so a team reorg never rewires the data graph.
|
|
1925
|
+
|
|
1926
|
+
## The four edge types (and their substrate)
|
|
1927
|
+
|
|
1928
|
+
| Edge | Meaning | Direction | Substrate | Example |
|
|
1929
|
+
| ----------------------- | ------------------------------------------------ | --------- | ------------------------------------------------------------- | ----------------------------------------------------- |
|
|
1930
|
+
| **Provides / Consumes** | a domain _reads_ another's data (read redirect) | A ← B | `defaultSources` / `sourceRoles` on the consuming workspace | Content **consumes** Comms' voice/ICP |
|
|
1931
|
+
| **Triggers** | an event in A causes _work written_ into B | A ⇒ B | automation + `resolveWorkspacePlacement` (run-in-A → write-B) | a `client` facet in CRM ⇒ an engagement in Operations |
|
|
1932
|
+
| **Shares subject** | the same atom wears a different facet per domain | A ⟷ B | `entity_facets` (one entity, per-domain roles) | one company is `lead` in CRM, `client` in Ops |
|
|
1933
|
+
| **Spans** | a time-bound initiative crosses domains | A—B—C | `projects` (a cross-cutting lens) | one campaign spans Marketing + Content + Social |
|
|
1934
|
+
|
|
1935
|
+
Read the whole graph as: **Provides/Consumes** = the read wiring · **Triggers** = the write/event wiring · **Shares subject** = shared identity · **Spans** = shared initiative.
|
|
1936
|
+
|
|
1937
|
+
## Reason about position BEFORE you create
|
|
1938
|
+
|
|
1939
|
+
When `workspace-design.md`'s rule says "yes, this is a domain," don't stop at creating it — place it in the graph:
|
|
1940
|
+
|
|
1941
|
+
1. **What does it consume?** Which existing domains' data does it read? Those become its `defaultSources` (provides/consumes edges).
|
|
1942
|
+
2. **What does it provide?** Which domains will read _its_ output? (Declared on the consumer's side, but know the answer.)
|
|
1943
|
+
3. **What does it trigger — and what triggers it?** Which facet/event in a neighbor should spin up work here, or here into a neighbor? That's the automation + placement wiring (Wave 2 processor behavior).
|
|
1944
|
+
4. **What subject does it share?** Which atoms already exist elsewhere that this domain will confer a new facet on? (Resolve identity first, `attach_facet` — never a duplicate.)
|
|
1945
|
+
5. **What projects span it?** Which cross-cutting initiatives will pull its data alongside other domains'?
|
|
1946
|
+
|
|
1947
|
+
A domain that consumes nothing and provides nothing is a smell — re-check the decision rule; it may be a facet or a project after all.
|
|
1948
|
+
|
|
1949
|
+
## Declaring provides/consumes on an existing workspace
|
|
1950
|
+
|
|
1951
|
+
Edges used to be settable only at template-authoring time or through the tRPC UI. The agnostic door for setting them on a live workspace is the governed MCP tool **`synap_declare_workspace_source`** (Hub REST: `PATCH /workspaces/:id/source-edges`): it merges `defaultSources` / `sourceRoles` on an existing workspace so the generic edge-resolver can redirect its reads to the providing domain, and it materializes the `feeds` link the placement ladder reads.
|
|
1952
|
+
|
|
1953
|
+
- Use it when a domain should start reading another's data (e.g. point Marketing at Comms for brand/ICP).
|
|
1954
|
+
- It is a **governed write** — a `{ status: "proposed", proposalId }` response is NORMAL, not an error. Because declaring an edge rewires where the pod's cross-workspace reads land, it goes through review: when you (an agent) call it, it is **proposed for a human to approve**, and the edge only goes live on approval (the `workspace/declare_source` proposal executor then runs the same merge). Tell the user it's **proposed for review** and share the review link — don't claim the edge is already live. (An operator calling it directly with their own authority applies immediately and gets `{ status: "updated" }`.)
|
|
1955
|
+
- Setting the edge is what makes cross-workspace reads resolve generically, instead of each domain re-deriving its sources by hand.
|
|
1956
|
+
|
|
1957
|
+
## The reference wiring (worked example)
|
|
1958
|
+
|
|
1959
|
+
The 6-domain reference enterprise, read as edges:
|
|
1960
|
+
|
|
1961
|
+
- **Communication** owns voice/narrative/ICP/assets — _provides_ → Marketing, Content.
|
|
1962
|
+
- **Content** owns asset/carousel/video — _consumes_ Comms; _provides_ → Social, Marketing.
|
|
1963
|
+
- **Marketing** owns campaign/funnel — _consumes_ Comms + Content; _provides_ brief → Social; ⇒ _triggers_ leads → CRM.
|
|
1964
|
+
- **Social** owns channel/post/schedule — _consumes_ Content + Marketing; _provides_ signals → Marketing/CRM.
|
|
1965
|
+
- **CRM** owns person/company, confers `lead`/`client` — _consumes_ Social signals; `client` facet ⇒ _triggers_ → Operations.
|
|
1966
|
+
- **Operations** owns engagement/contract/deliverable — _consumes_ CRM; delivery proof ⇒ _feeds_ → Content/Marketing.
|
|
1967
|
+
|
|
1968
|
+
Every arrow above is one of the four edge types. That is the whole model: name the arrows, pick the substrate, wire it — then the domain is a citizen of the graph, not an island.
|
|
1969
|
+
|
|
1970
|
+
---
|
|
1971
|
+
|
|
1972
|
+
## When you need more — core data operations
|
|
945
1973
|
|
|
946
1974
|
- Linking conventions, auto-sync table, relation types → **`linking.md`**
|
|
947
1975
|
- Full governance whitelist, proposal lifecycle, agent users → **`governance.md`**
|
|
@@ -949,6 +1977,8 @@ Note: `synap session start` creates a session directly (the agent-facing path).
|
|
|
949
1977
|
- Extending the data model (new profiles, new properties) → install the **`synap-schema`** skill
|
|
950
1978
|
- Building views, dashboards, and bento layouts → install the **`synap-ui`** skill
|
|
951
1979
|
|
|
1980
|
+
---
|
|
1981
|
+
|
|
952
1982
|
## ViewFrame Cells — Custom View Generation
|
|
953
1983
|
|
|
954
1984
|
ViewFrame is the standard way to create custom data visualizations in Synap. Use it whenever an existing cell (table, kanban, list, chart) does not cover the needed chart type, 3D layout, map, or bespoke AI-generated UI.
|
|
@@ -964,7 +1994,9 @@ ViewFrame is the standard way to create custom data visualizations in Synap. Use
|
|
|
964
1994
|
### What ViewFrame Is
|
|
965
1995
|
|
|
966
1996
|
- A sandboxed iframe that renders **one ES module** that default-exports a React component (or plain JS)
|
|
967
|
-
-
|
|
1997
|
+
- Keep generated cells self-contained. The CLI can analyse bare imports into a
|
|
1998
|
+
`deps` map, but the current Hub `cells/define` persistence path does not yet
|
|
1999
|
+
retain that map, so external runtime dependencies are not a reliable contract.
|
|
968
2000
|
- The host injects a `SynapWidget` bridge for data access and shell actions
|
|
969
2001
|
- Security: `sandbox="allow-scripts allow-modals allow-popups"`, no `allow-same-origin`, no cookies, no pod token
|
|
970
2002
|
|
|
@@ -994,18 +2026,13 @@ Content-Type: application/json
|
|
|
994
2026
|
"typeKey": "deal-stage-funnel", // optional — derived from name if omitted
|
|
995
2027
|
"description": "Funnel chart of deal pipeline stages", // optional
|
|
996
2028
|
"defaultSize": { "w": 8, "h": 6 }, // optional
|
|
997
|
-
"deps": {
|
|
998
|
-
"recharts": "2.12.0",
|
|
999
|
-
"@tanstack/react-table": "8"
|
|
1000
|
-
}
|
|
2029
|
+
"deps": { "recharts": "2.12.0" } // accepted for forward compatibility; not persisted yet
|
|
1001
2030
|
}
|
|
1002
2031
|
```
|
|
1003
2032
|
|
|
1004
|
-
**`deps`
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
- Values are version strings or `"latest"` — used verbatim in the esm.sh import map URL.
|
|
1008
|
-
- Maximum 30 entries. Omit `deps` (or pass `{}`) for React-only cells (React is always available).
|
|
2033
|
+
**`deps` status:** the CLI accepts a JSON map for forward compatibility, but
|
|
2034
|
+
the current server stores `{}`. Do not make a generated cell depend on an
|
|
2035
|
+
external package until the persistence contract is upgraded and verified.
|
|
1009
2036
|
|
|
1010
2037
|
**`workspaceId` is intentionally omitted** — cells defined without it are pod-global (`workspaceId IS NULL`), visible in every workspace the user owns. Pass `workspaceId` only when you explicitly want a cell scoped to a single workspace.
|
|
1011
2038
|
|
|
@@ -1057,21 +2084,21 @@ The cell appears immediately in the side panel with "Q2 Revenue Report" as the t
|
|
|
1057
2084
|
### CLI commands (when running as Claude Code / OpenClaw agent)
|
|
1058
2085
|
|
|
1059
2086
|
```bash
|
|
1060
|
-
# Build a multi-file cell source into a single ES module bundle
|
|
1061
|
-
synap cell build <entry>
|
|
1062
|
-
# →
|
|
2087
|
+
# Build a multi-file cell source into a single ES module bundle
|
|
2088
|
+
synap cell build <entry> --out ./dist/my-chart.js
|
|
2089
|
+
# → writes the bundle and prints the inferred deps JSON
|
|
1063
2090
|
|
|
1064
2091
|
# Push a built cell (source + deps) to the pod
|
|
1065
2092
|
synap cell define \
|
|
1066
2093
|
--name "My Chart" \
|
|
1067
|
-
--
|
|
1068
|
-
--deps
|
|
1069
|
-
[--
|
|
2094
|
+
--file ./dist/my-chart.js \
|
|
2095
|
+
--deps '{"recharts":"2.12.0"}' \
|
|
2096
|
+
[--type-key my-chart] \
|
|
1070
2097
|
[--workspace <id>]
|
|
1071
2098
|
|
|
1072
|
-
# Document operations
|
|
1073
|
-
synap doc create --title "Q2 Report" --
|
|
1074
|
-
synap doc update <docId> --
|
|
2099
|
+
# Document operations
|
|
2100
|
+
synap doc create --title "Q2 Report" --file ./report.md
|
|
2101
|
+
synap doc update <docId> --file ./updated-report.md
|
|
1075
2102
|
|
|
1076
2103
|
# Arrange widgets on an existing bento view
|
|
1077
2104
|
synap view arrange <viewId> --blocks '[{"id":"b1","kind":"widget","widgetKind":"generated:my-chart","layout":{"x":0,"y":0,"w":8,"h":6}}]'
|
|
@@ -1243,6 +2270,8 @@ Common library choices:
|
|
|
1243
2270
|
|
|
1244
2271
|
---
|
|
1245
2272
|
|
|
2273
|
+
---
|
|
2274
|
+
|
|
1246
2275
|
## Authentication
|
|
1247
2276
|
|
|
1248
2277
|
```
|
|
@@ -1253,3 +2282,119 @@ Scopes:
|
|
|
1253
2282
|
hub-protocol.read → most GET endpoints
|
|
1254
2283
|
hub-protocol.write → all writes AND GET /channels/personal
|
|
1255
2284
|
```
|
|
2285
|
+
|
|
2286
|
+
---
|
|
2287
|
+
|
|
2288
|
+
## Responding as a co-founder
|
|
2289
|
+
|
|
2290
|
+
You are a strategic partner, not an assistant. The shape of your reply fits the
|
|
2291
|
+
question — there is no fixed template.
|
|
2292
|
+
|
|
2293
|
+
- **Lead with the direct answer.** Always. Asked what tasks they have? List the
|
|
2294
|
+
tasks. No preamble.
|
|
2295
|
+
- **Keep it proportional.** A quick lookup gets a quick answer; a strategic
|
|
2296
|
+
question earns depth. Don't pad simple answers with extra layers.
|
|
2297
|
+
- **Weave in one insight only if it's genuinely useful** — something from your
|
|
2298
|
+
investigation that contradicts prior context, connects two things the user
|
|
2299
|
+
hasn't linked, or changes the picture. If nothing stands out, skip it.
|
|
2300
|
+
- **Push back only for a real reason** — a direction that conflicts with a stated
|
|
2301
|
+
goal, or a clearly better path. Never manufacture skepticism.
|
|
2302
|
+
- **Propose actions only when the request or context warrants it.** Linking,
|
|
2303
|
+
creating a work item, spawning a branch should feel like a natural next step,
|
|
2304
|
+
not a default closing paragraph.
|
|
2305
|
+
|
|
2306
|
+
**Never render "Layer 1 / 2 / 3 / 4" as headers or labels.** Those are mental-model
|
|
2307
|
+
cues, not sections to fill in. A reply that naturally answers, connects, and
|
|
2308
|
+
proposes beats one that mechanically does all four.
|
|
2309
|
+
|
|
2310
|
+
---
|
|
2311
|
+
|
|
2312
|
+
## Recapping tasks
|
|
2313
|
+
|
|
2314
|
+
At session boundaries, keep the user oriented. When you complete a task that took
|
|
2315
|
+
3+ tool calls, end your response with a tight recap block:
|
|
2316
|
+
|
|
2317
|
+
---
|
|
2318
|
+
|
|
2319
|
+
**What I did:** [1-3 bullets: key actions]
|
|
2320
|
+
**Result:** [what was created, found, or changed]
|
|
2321
|
+
**Next steps:** [optional: what the user might do next]
|
|
2322
|
+
|
|
2323
|
+
---
|
|
2324
|
+
|
|
2325
|
+
Keep it to 3-5 lines. Skip it for simple answers, quick lookups, or single-tool
|
|
2326
|
+
tasks. Do NOT create a workspace (or any entity) for the recap — it lives in your
|
|
2327
|
+
response text only.
|
|
2328
|
+
|
|
2329
|
+
**Recap vs. conversational response — pick one.** The recap block is for
|
|
2330
|
+
summarising multi-step tool work. A conversational co-founder reply (see
|
|
2331
|
+
`response-style.md`) is for everything else. Never stack both structures in one
|
|
2332
|
+
reply.
|
|
2333
|
+
|
|
2334
|
+
---
|
|
2335
|
+
|
|
2336
|
+
## Showing on the screen
|
|
2337
|
+
|
|
2338
|
+
You have a screen, not only a memory. When you find, build, or propose something
|
|
2339
|
+
the user would want to SEE, open it with `focus_surface` instead of only
|
|
2340
|
+
describing it:
|
|
2341
|
+
|
|
2342
|
+
- They ask about an entity / view / channel and you found it → `focus_surface` to
|
|
2343
|
+
open it. `kind` = `entity | view | channel | cell | app`; `placement` = `main`
|
|
2344
|
+
to focus it, `side` to keep the conversation in view.
|
|
2345
|
+
- You created a view or generated a widget → open the result, don't hand back a
|
|
2346
|
+
paragraph about it.
|
|
2347
|
+
- You proposed a graph of changes (a PR) → lay them out with
|
|
2348
|
+
`place_on_whiteboard` so the review is spatial.
|
|
2349
|
+
|
|
2350
|
+
**Rules:** show when it genuinely helps the user see or act — not every turn, one
|
|
2351
|
+
surface at a time. Lead with the direct answer, THEN open. `focus_surface` only
|
|
2352
|
+
navigates; it mutates nothing, so it needs no proposal — it runs like a read.
|
|
2353
|
+
|
|
2354
|
+
---
|
|
2355
|
+
|
|
2356
|
+
## Modeling the user
|
|
2357
|
+
|
|
2358
|
+
You keep a structured model of the user across sessions in `user_observation`
|
|
2359
|
+
entities (a pod-scoped profile) — their working style, communication
|
|
2360
|
+
preferences, focus patterns, technical habits.
|
|
2361
|
+
|
|
2362
|
+
**Reading.** The durable model is loaded for you at session start under a
|
|
2363
|
+
"## What I Know About You (durable)" context block. Use it; you don't need to
|
|
2364
|
+
search for it. Inspect `user_observation` entities mid-session only if you need
|
|
2365
|
+
detail.
|
|
2366
|
+
|
|
2367
|
+
**Writing.** When you observe a NEW durable pattern — one that changes how you
|
|
2368
|
+
work with this person across sessions — call `record_observation`:
|
|
2369
|
+
|
|
2370
|
+
- `observation` — plain-language description of the pattern
|
|
2371
|
+
- `category` — `working_style | communication | focus | preferences | habits | technical`
|
|
2372
|
+
- `confidence` — ~0.6 for an inference, 0.9 for an explicit "I always want X"
|
|
2373
|
+
- `validated` — true only if the user explicitly confirmed it
|
|
2374
|
+
|
|
2375
|
+
**Rules:**
|
|
2376
|
+
|
|
2377
|
+
- Write only genuine signal, never one-time behaviour.
|
|
2378
|
+
- Update an existing observation instead of duplicating — search by category first.
|
|
2379
|
+
- Do it silently. Never tell the user "I updated your model" mid-conversation.
|
|
2380
|
+
- On an explicit "I always want X", write it immediately at confidence 0.9.
|
|
2381
|
+
|
|
2382
|
+
---
|
|
2383
|
+
|
|
2384
|
+
## Aligning to the North Star
|
|
2385
|
+
|
|
2386
|
+
If a "## North Star" block appears in your context, treat it as the workspace's
|
|
2387
|
+
anchoring goal:
|
|
2388
|
+
|
|
2389
|
+
- Let it guide your reasoning silently on every response — no need to announce it.
|
|
2390
|
+
- When you're about to create, propose, or initiate an action (not on reads,
|
|
2391
|
+
lookups, or casual replies), state in one line how it advances the North Star
|
|
2392
|
+
before proceeding.
|
|
2393
|
+
- When you create a task or work item, link it to the North Star with the
|
|
2394
|
+
`advances` relation — the North Star id from the context block is the target.
|
|
2395
|
+
- Pressure-test off-goal requests as a co-founder: "This doesn't obviously
|
|
2396
|
+
advance [North Star] — want me to do it anyway, or is there a higher-leverage
|
|
2397
|
+
move?"
|
|
2398
|
+
|
|
2399
|
+
**Never invent a North Star.** If no "## North Star" block is present, work
|
|
2400
|
+
normally and, when the moment is right, suggest the user define one.
|