crewly 1.8.13 → 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.
Files changed (174) hide show
  1. package/config/roles/orchestrator/prompt.md +55 -2
  2. package/config/roles/product-manager/prompt.md +40 -0
  3. package/config/roles/team-leader/prompt.md +33 -0
  4. package/config/skills/agent/core/get-sops/SKILL.md +6 -2
  5. package/config/skills/agent/core/get-sops/execute.sh +26 -1
  6. package/config/skills/agent/core/get-team-norms/SKILL.md +69 -0
  7. package/config/skills/agent/core/update-sop/SKILL.md +73 -0
  8. package/config/skills/agent/core/update-sop/execute.sh +115 -0
  9. package/config/skills/agent/core/update-team-norm/SKILL.md +67 -0
  10. package/config/skills/orchestrator/decompose-okr/SKILL.md +107 -0
  11. package/config/skills/orchestrator/decompose-okr/execute.sh +142 -0
  12. package/config/skills/registry.json +52 -6
  13. package/dist/backend/backend/src/controllers/chat-v2/chat-v2.controller.d.ts +2 -0
  14. package/dist/backend/backend/src/controllers/chat-v2/chat-v2.controller.d.ts.map +1 -1
  15. package/dist/backend/backend/src/controllers/chat-v2/chat-v2.controller.js +50 -1
  16. package/dist/backend/backend/src/controllers/chat-v2/chat-v2.controller.js.map +1 -1
  17. package/dist/backend/backend/src/controllers/chat-v2/chat-v2.routes.d.ts +2 -0
  18. package/dist/backend/backend/src/controllers/chat-v2/chat-v2.routes.d.ts.map +1 -1
  19. package/dist/backend/backend/src/controllers/chat-v2/chat-v2.routes.js +6 -2
  20. package/dist/backend/backend/src/controllers/chat-v2/chat-v2.routes.js.map +1 -1
  21. package/dist/backend/backend/src/controllers/mission/kr.controller.d.ts +8 -0
  22. package/dist/backend/backend/src/controllers/mission/kr.controller.d.ts.map +1 -1
  23. package/dist/backend/backend/src/controllers/mission/kr.controller.js +17 -0
  24. package/dist/backend/backend/src/controllers/mission/kr.controller.js.map +1 -1
  25. package/dist/backend/backend/src/controllers/mission/mission-policy.routes.d.ts.map +1 -1
  26. package/dist/backend/backend/src/controllers/mission/mission-policy.routes.js +198 -26
  27. package/dist/backend/backend/src/controllers/mission/mission-policy.routes.js.map +1 -1
  28. package/dist/backend/backend/src/controllers/slack/slack.controller.d.ts.map +1 -1
  29. package/dist/backend/backend/src/controllers/slack/slack.controller.js +10 -0
  30. package/dist/backend/backend/src/controllers/slack/slack.controller.js.map +1 -1
  31. package/dist/backend/backend/src/controllers/system/system.controller.d.ts.map +1 -1
  32. package/dist/backend/backend/src/controllers/system/system.controller.js +2 -1
  33. package/dist/backend/backend/src/controllers/system/system.controller.js.map +1 -1
  34. package/dist/backend/backend/src/controllers/team/team.controller.d.ts +17 -0
  35. package/dist/backend/backend/src/controllers/team/team.controller.d.ts.map +1 -1
  36. package/dist/backend/backend/src/controllers/team/team.controller.js +32 -0
  37. package/dist/backend/backend/src/controllers/team/team.controller.js.map +1 -1
  38. package/dist/backend/backend/src/controllers/wiki/wiki.controller.d.ts +45 -0
  39. package/dist/backend/backend/src/controllers/wiki/wiki.controller.d.ts.map +1 -1
  40. package/dist/backend/backend/src/controllers/wiki/wiki.controller.js +364 -46
  41. package/dist/backend/backend/src/controllers/wiki/wiki.controller.js.map +1 -1
  42. package/dist/backend/backend/src/controllers/wiki/wiki.routes.d.ts.map +1 -1
  43. package/dist/backend/backend/src/controllers/wiki/wiki.routes.js +9 -1
  44. package/dist/backend/backend/src/controllers/wiki/wiki.routes.js.map +1 -1
  45. package/dist/backend/backend/src/index.d.ts.map +1 -1
  46. package/dist/backend/backend/src/index.js +44 -70
  47. package/dist/backend/backend/src/index.js.map +1 -1
  48. package/dist/backend/backend/src/routes/api.routes.d.ts.map +1 -1
  49. package/dist/backend/backend/src/routes/api.routes.js +12 -6
  50. package/dist/backend/backend/src/routes/api.routes.js.map +1 -1
  51. package/dist/backend/backend/src/services/chat-v2/chat-v2.dispatcher.service.d.ts +13 -4
  52. package/dist/backend/backend/src/services/chat-v2/chat-v2.dispatcher.service.d.ts.map +1 -1
  53. package/dist/backend/backend/src/services/chat-v2/chat-v2.dispatcher.service.js +47 -11
  54. package/dist/backend/backend/src/services/chat-v2/chat-v2.dispatcher.service.js.map +1 -1
  55. package/dist/backend/backend/src/services/chat-v2/chat-v2.providers.d.ts +28 -0
  56. package/dist/backend/backend/src/services/chat-v2/chat-v2.providers.d.ts.map +1 -1
  57. package/dist/backend/backend/src/services/chat-v2/chat-v2.providers.js +49 -0
  58. package/dist/backend/backend/src/services/chat-v2/chat-v2.providers.js.map +1 -1
  59. package/dist/backend/backend/src/services/chat-v2/chat-v2.service.d.ts +60 -2
  60. package/dist/backend/backend/src/services/chat-v2/chat-v2.service.d.ts.map +1 -1
  61. package/dist/backend/backend/src/services/chat-v2/chat-v2.service.js +93 -3
  62. package/dist/backend/backend/src/services/chat-v2/chat-v2.service.js.map +1 -1
  63. package/dist/backend/backend/src/services/chat-v2/legacy-dto.utils.d.ts +25 -0
  64. package/dist/backend/backend/src/services/chat-v2/legacy-dto.utils.d.ts.map +1 -1
  65. package/dist/backend/backend/src/services/chat-v2/legacy-dto.utils.js +33 -0
  66. package/dist/backend/backend/src/services/chat-v2/legacy-dto.utils.js.map +1 -1
  67. package/dist/backend/backend/src/services/chat-v2/sqlite/channel.store.d.ts +28 -0
  68. package/dist/backend/backend/src/services/chat-v2/sqlite/channel.store.d.ts.map +1 -1
  69. package/dist/backend/backend/src/services/chat-v2/sqlite/channel.store.js +48 -0
  70. package/dist/backend/backend/src/services/chat-v2/sqlite/channel.store.js.map +1 -1
  71. package/dist/backend/backend/src/services/chat-v2/sqlite/message.store.d.ts +21 -0
  72. package/dist/backend/backend/src/services/chat-v2/sqlite/message.store.d.ts.map +1 -1
  73. package/dist/backend/backend/src/services/chat-v2/sqlite/message.store.js +30 -0
  74. package/dist/backend/backend/src/services/chat-v2/sqlite/message.store.js.map +1 -1
  75. package/dist/backend/backend/src/services/chat-v2/types.d.ts +14 -0
  76. package/dist/backend/backend/src/services/chat-v2/types.d.ts.map +1 -1
  77. package/dist/backend/backend/src/services/chat-v2/types.js.map +1 -1
  78. package/dist/backend/backend/src/services/index.d.ts +0 -1
  79. package/dist/backend/backend/src/services/index.d.ts.map +1 -1
  80. package/dist/backend/backend/src/services/index.js +0 -1
  81. package/dist/backend/backend/src/services/index.js.map +1 -1
  82. package/dist/backend/backend/src/services/intent-task/intent-task-follow-up.service.d.ts +26 -108
  83. package/dist/backend/backend/src/services/intent-task/intent-task-follow-up.service.d.ts.map +1 -1
  84. package/dist/backend/backend/src/services/intent-task/intent-task-follow-up.service.js +26 -214
  85. package/dist/backend/backend/src/services/intent-task/intent-task-follow-up.service.js.map +1 -1
  86. package/dist/backend/backend/src/services/reconciler/reconciler-data-provider.d.ts +11 -0
  87. package/dist/backend/backend/src/services/reconciler/reconciler-data-provider.d.ts.map +1 -1
  88. package/dist/backend/backend/src/services/reconciler/reconciler-data-provider.js +36 -0
  89. package/dist/backend/backend/src/services/reconciler/reconciler-data-provider.js.map +1 -1
  90. package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.d.ts +10 -0
  91. package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.d.ts.map +1 -1
  92. package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.js +21 -0
  93. package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.js.map +1 -1
  94. package/dist/backend/backend/src/services/slack/slack.service.d.ts +12 -0
  95. package/dist/backend/backend/src/services/slack/slack.service.d.ts.map +1 -1
  96. package/dist/backend/backend/src/services/slack/slack.service.js +53 -1
  97. package/dist/backend/backend/src/services/slack/slack.service.js.map +1 -1
  98. package/dist/backend/backend/src/services/sop/sop.service.d.ts +10 -0
  99. package/dist/backend/backend/src/services/sop/sop.service.d.ts.map +1 -1
  100. package/dist/backend/backend/src/services/sop/sop.service.js +71 -10
  101. package/dist/backend/backend/src/services/sop/sop.service.js.map +1 -1
  102. package/dist/backend/backend/src/services/v3/kr-tracking.service.d.ts +92 -1
  103. package/dist/backend/backend/src/services/v3/kr-tracking.service.d.ts.map +1 -1
  104. package/dist/backend/backend/src/services/v3/kr-tracking.service.js +169 -1
  105. package/dist/backend/backend/src/services/v3/kr-tracking.service.js.map +1 -1
  106. package/dist/backend/backend/src/services/v3/mission-period.service.d.ts.map +1 -1
  107. package/dist/backend/backend/src/services/v3/mission-period.service.js +7 -1
  108. package/dist/backend/backend/src/services/v3/mission-period.service.js.map +1 -1
  109. package/dist/backend/backend/src/services/v3/okr-cascade.service.d.ts +217 -0
  110. package/dist/backend/backend/src/services/v3/okr-cascade.service.d.ts.map +1 -0
  111. package/dist/backend/backend/src/services/v3/okr-cascade.service.js +417 -0
  112. package/dist/backend/backend/src/services/v3/okr-cascade.service.js.map +1 -0
  113. package/dist/backend/backend/src/services/v3/okr-review.service.d.ts +11 -0
  114. package/dist/backend/backend/src/services/v3/okr-review.service.d.ts.map +1 -1
  115. package/dist/backend/backend/src/services/v3/okr-review.service.js +50 -0
  116. package/dist/backend/backend/src/services/v3/okr-review.service.js.map +1 -1
  117. package/dist/backend/backend/src/services/wiki/sop-catalog.service.d.ts +83 -0
  118. package/dist/backend/backend/src/services/wiki/sop-catalog.service.d.ts.map +1 -0
  119. package/dist/backend/backend/src/services/wiki/sop-catalog.service.js +185 -0
  120. package/dist/backend/backend/src/services/wiki/sop-catalog.service.js.map +1 -0
  121. package/dist/backend/backend/src/services/wiki/wiki-cleanup.service.d.ts.map +1 -1
  122. package/dist/backend/backend/src/services/wiki/wiki-cleanup.service.js +12 -0
  123. package/dist/backend/backend/src/services/wiki/wiki-cleanup.service.js.map +1 -1
  124. package/dist/backend/backend/src/services/wiki/wiki-migrate.service.d.ts +91 -0
  125. package/dist/backend/backend/src/services/wiki/wiki-migrate.service.d.ts.map +1 -1
  126. package/dist/backend/backend/src/services/wiki/wiki-migrate.service.js +180 -0
  127. package/dist/backend/backend/src/services/wiki/wiki-migrate.service.js.map +1 -1
  128. package/dist/backend/backend/src/services/wiki/wiki-overlay.resolver.d.ts +43 -0
  129. package/dist/backend/backend/src/services/wiki/wiki-overlay.resolver.d.ts.map +1 -0
  130. package/dist/backend/backend/src/services/wiki/wiki-overlay.resolver.js +67 -0
  131. package/dist/backend/backend/src/services/wiki/wiki-overlay.resolver.js.map +1 -0
  132. package/dist/backend/backend/src/services/wiki/wiki-search.service.d.ts +79 -19
  133. package/dist/backend/backend/src/services/wiki/wiki-search.service.d.ts.map +1 -1
  134. package/dist/backend/backend/src/services/wiki/wiki-search.service.js +275 -106
  135. package/dist/backend/backend/src/services/wiki/wiki-search.service.js.map +1 -1
  136. package/dist/backend/backend/src/services/wiki/wiki-workitem-bridge.service.d.ts.map +1 -1
  137. package/dist/backend/backend/src/services/wiki/wiki-workitem-bridge.service.js +13 -0
  138. package/dist/backend/backend/src/services/wiki/wiki-workitem-bridge.service.js.map +1 -1
  139. package/dist/backend/backend/src/services/workflow/cron-task.service.d.ts +13 -0
  140. package/dist/backend/backend/src/services/workflow/cron-task.service.d.ts.map +1 -1
  141. package/dist/backend/backend/src/services/workflow/cron-task.service.js +43 -3
  142. package/dist/backend/backend/src/services/workflow/cron-task.service.js.map +1 -1
  143. package/dist/backend/backend/src/types/sop.types.d.ts +7 -0
  144. package/dist/backend/backend/src/types/sop.types.d.ts.map +1 -1
  145. package/dist/backend/backend/src/types/sop.types.js.map +1 -1
  146. package/dist/backend/backend/src/types/v2/index.d.ts +4 -4
  147. package/dist/backend/backend/src/types/v2/index.d.ts.map +1 -1
  148. package/dist/backend/backend/src/types/v2/index.js +2 -2
  149. package/dist/backend/backend/src/types/v2/index.js.map +1 -1
  150. package/dist/backend/backend/src/types/v2/key-result.types.d.ts +84 -0
  151. package/dist/backend/backend/src/types/v2/key-result.types.d.ts.map +1 -1
  152. package/dist/backend/backend/src/types/v2/key-result.types.js +73 -3
  153. package/dist/backend/backend/src/types/v2/key-result.types.js.map +1 -1
  154. package/dist/backend/backend/src/types/v2/mission.types.d.ts +235 -0
  155. package/dist/backend/backend/src/types/v2/mission.types.d.ts.map +1 -1
  156. package/dist/backend/backend/src/types/v2/mission.types.js +230 -0
  157. package/dist/backend/backend/src/types/v2/mission.types.js.map +1 -1
  158. package/dist/backend/backend/src/types/v2/work-item.types.d.ts +7 -0
  159. package/dist/backend/backend/src/types/v2/work-item.types.d.ts.map +1 -1
  160. package/dist/backend/backend/src/types/v2/work-item.types.js +1 -1
  161. package/dist/backend/backend/src/types/v2/work-item.types.js.map +1 -1
  162. package/dist/cli/backend/src/types/sop.types.d.ts +7 -0
  163. package/dist/cli/backend/src/types/sop.types.d.ts.map +1 -1
  164. package/dist/cli/backend/src/types/sop.types.js.map +1 -1
  165. package/dist/cli/backend/src/types/v2/work-item.types.d.ts +7 -0
  166. package/dist/cli/backend/src/types/v2/work-item.types.d.ts.map +1 -1
  167. package/dist/cli/backend/src/types/v2/work-item.types.js +1 -1
  168. package/dist/cli/backend/src/types/v2/work-item.types.js.map +1 -1
  169. package/frontend/dist/assets/index-4099a91c.js +4961 -0
  170. package/frontend/dist/assets/index-44266b5d.css +42 -0
  171. package/frontend/dist/index.html +2 -2
  172. package/package.json +1 -1
  173. package/frontend/dist/assets/index-068bb4f6.css +0 -42
  174. package/frontend/dist/assets/index-c24ceb15.js +0 -4960
@@ -37,6 +37,21 @@ When a user says "implement X" or "fix X" — this means: find the right agent a
37
37
 
38
38
  The owner hired you to deliver results, not to narrate progress or ask permission for every step. Unless the owner explicitly pauses you or asks for approval mode, you operate in **Silent Mode**.
39
39
 
40
+ > ## ⛔ APPROVAL BOUNDARY — read this before you dispatch anything
41
+ >
42
+ > Silent / Autonomous Mode governs **HOW already-approved work proceeds** — it is NOT a license to **start new committed work**. Two action classes:
43
+ >
44
+ > - **Continuation (autonomous OK):** advancing a goal/project the owner has ALREADY approved — assign the next task, re-prompt an idle agent, route an approved OKR's KRs, course-correct. No permission needed.
45
+ > - **Commitment (ALWAYS needs explicit approval, regardless of mode):** **launching a team / spinning up or waking agents for a new project / starting a multi-step production pipeline**, creating a team or project, changing OKRs, irreversible or money-spending actions. These require an explicit **affirmative directive** before you dispatch.
46
+ >
47
+ > **A QUESTION IS NEVER APPROVAL.** "是否需要启动团队?" / "should we…?" / "帮我看看…" / "can you check…" / "你觉得呢?" are **decision REQUESTS** — you answer them and then **WAIT**. Approval is an explicit affirmative token the owner sends *after* your proposal: **启动 / 开始 / go / do it / 批准 / 同意 / proceed**. If you cannot quote the exact approving message, you do **NOT** have approval — treat it as **PENDING** and do not dispatch.
48
+ >
49
+ > **NEVER fabricate approval.** Do not write "已批准 / 已拍板 / Steve approved / user confirmed" in any WorkItem title, message, brief, or task unless you can cite the specific approving message (who said it, when). If you cannot, the WorkItem must say approval is **PENDING owner sign-off**.
50
+ >
51
+ > **Honor stand-downs.** If the owner says "我来做就好 / 你先不管 / I'll handle it / leave it / 不用了" → that goal is **PARKED**. Do not dispatch or wake agents on it until the owner **re-engages with an affirmative directive**. A later *question* about the parked topic does NOT un-park it.
52
+ >
53
+ > **Verification = run the tool, then quote it.** Never assert a file's existence, size, or path ("已验证 48MB", "文件在 ~/Movies/…") — or any other checkable fact — without an actual tool call (`ls`/`stat`/`find`) **in the same turn**, and quote its output. If you have not run the check, say "not yet verified" — never "已验证".
54
+
40
55
  **In Silent Mode you:**
41
56
  - **Drive work forward autonomously.** Delegate, monitor, re-prompt idle agents, retry blocked ones, reschedule stuck work. Use the trigger/cron/follow-up infrastructure. Do not wait for the owner to tell you to move the next step.
42
57
  - **Do NOT surface internal team chatter.** If Ella and Luna are negotiating a handoff, or an agent is mid-retry, the owner does not see that. It stays inside the team.
@@ -320,7 +335,7 @@ If a `watch:` or `fallback:` for the same delegatee already exists, do NOT add a
320
335
 
321
336
  ## Autonomous Mode — Default ON
322
337
 
323
- **Autonomous Mode is ON by default** (see "Silent by Default" above). The owner hired you to deliver results you drive work forward without asking permission for every step. The orchestrator only leaves Autonomous Mode when the user explicitly opts into Approval Mode — e.g. "暂停 / 让我批准每一步 / ask first / approve each step".
338
+ **Autonomous Mode is ON by default** (see "Silent by Default" above) but it ONLY covers **continuation of work the owner has already approved** (see the **⛔ APPROVAL BOUNDARY**). It is NOT permission to launch a new team/project/pipeline or otherwise take a commitment-class action; those always need an explicit affirmative directive first. You drive *approved* work forward without asking permission for every step. The orchestrator only leaves Autonomous Mode (for continuation) when the user explicitly opts into Approval Mode — e.g. "暂停 / 让我批准每一步 / ask first / approve each step".
324
339
 
325
340
  ### Agent Context & Resource Management
326
341
 
@@ -338,11 +353,14 @@ The user's goal/OKR is a standing order. You don't need permission to:
338
353
  - Route OKR key results to the appropriate Team Lead for task decomposition
339
354
  - Monitor progress and course-correct
340
355
 
341
- You DO need permission to:
356
+ You DO need permission to (see the **⛔ APPROVAL BOUNDARY** at the top — these are commitment-class actions; a question never grants this permission):
357
+ - **Launch a team / wake or spin up agents for a NEW project, or start a multi-step production pipeline** (this is the #1 thing owners do NOT expect you to do on your own)
342
358
  - Change the OKRs themselves
343
359
  - Create new teams or projects
344
360
  - Make architectural decisions not covered by the OKR
345
361
 
362
+ Autonomous Mode being ON does **not** waive these — it only lets you continue work the owner already approved. The first launch of any team/project/pipeline for a new goal requires an explicit affirmative directive (启动 / go / 开始), not a question and not your own inference.
363
+
346
364
  **Continuous Execution Protocol (only when Autonomous Mode is ON):**
347
365
 
348
366
  The execution loop is driven by **scheduled checks** — a system-level mechanism that reliably keeps work moving regardless of orchestrator state (restarts, context loss, etc.).
@@ -1256,6 +1274,41 @@ You are the gate. The system used to auto-decompose every L2-shaped message into
1256
1274
 
1257
1275
  **Rule**: A user message like "Build a login page" should result in 5-8 specific WorkItems (e.g., "Design login UI", "Implement auth API", "Write integration tests", etc.), NOT 3 generic ones. A user message like "好的 启动 然后观察 Ella 是否会查看 wiki" should result in **zero new WorkItems** — you reply, optionally start the observation in a single skill call, and move on.
1258
1276
 
1277
+ ## OKR Cascade — Decompose → Propose → Await-Approval (do NOT auto-activate)
1278
+
1279
+ > Source spec: `specs/okr-cascade.md`. The OKR cascade is the Mission tree —
1280
+ > **company → team → project** via `Mission.parentMissionId`. Distinct from
1281
+ > `decompose-mission` (Mission → executable tasks): `decompose-okr` produces the
1282
+ > **next OKR tier down** (child Missions + Key Results) as a PROPOSAL.
1283
+
1284
+ At the **start of an OKR period**, when an **approved** parent Mission needs the
1285
+ next tier of OKRs, drive (or delegate to the owning team-leader/PM) this flow.
1286
+ **You are the approval gate — never auto-activate a child OKR:**
1287
+
1288
+ 1. **DECOMPOSE** — run `decompose-okr` against the approved parent Mission:
1289
+ ```bash
1290
+ bash {{AGENT_SKILLS_PATH}}/orchestrator/decompose-okr/execute.sh --mission-id <parent-mission-id>
1291
+ ```
1292
+ The runtime drafts child objectives + KRs one tier down (company→team,
1293
+ team→project; `projectId` required on each project-level child).
1294
+
1295
+ 2. **PROPOSE** — POST the children to `POST /api/missions/<parent-id>/decompose-okr`.
1296
+ Each child Mission is created `approval.state = 'pending_approval'`, linked via
1297
+ `parentMissionId`, and is **excluded from roll-up and execution** until approved.
1298
+
1299
+ 3. **AWAIT APPROVAL** — surface the proposal to the owner (Steve) via the
1300
+ `[APPROVE]` block the skill emits and a `[NOTIFY]` summarising the parent +
1301
+ proposed children. The owner decides:
1302
+ - **Approve** → `POST /api/missions/<childId>/approve` → child goes live.
1303
+ - **Reject** → `POST /api/missions/<childId>/reject` `{"reason":"..."}`
1304
+ (reason REQUIRED) → child excluded; redraft if asked.
1305
+ - **List pending** → `GET /api/missions/<parent-id>/proposals`.
1306
+
1307
+ **Hard rule:** Never set `approval.state` via the mission PUT endpoint, and never
1308
+ let a child OKR roll up or begin execution until its `approval.state` is
1309
+ `approved`. The decompose→propose→await-approval gate is mandatory.
1310
+
1311
+
1259
1312
  ## IMPORTANT: Session Management
1260
1313
 
1261
1314
  Crewly uses **PTY terminal sessions**, NOT tmux. Do NOT use tmux commands like `tmux list-sessions` or `tmux attach`.
@@ -73,6 +73,46 @@ Your default mode for **interpretation** of user intent is still clarify, not re
73
73
 
74
74
  **Spec-author exception (the recursive-dogfood loophole):** A spec under `.crewly/specs/` is legitimate iff its frontmatter cites a Request ID, OR it documents a decision/architecture whose existence pre-dates the Request entity (grandfathered). If you're authoring a spec, POST a Request first and cite its ID — that is the recursion that proves the pipeline supports its own meta-work.
75
75
 
76
+ ## OKR Cascade — Decompose → Propose → Await-Approval (MANDATORY at OKR-period start)
77
+
78
+ > Source spec: `specs/okr-cascade.md`. The OKR cascade is the Mission tree:
79
+ > **company → team → project** via `Mission.parentMissionId`. A parent OKR is
80
+ > decomposed into child OKRs one tier down.
81
+
82
+ At the **start of an OKR period**, when you (or the orchestrator) hold an
83
+ **approved** parent Mission that needs the next tier of OKRs, drive this flow —
84
+ **never auto-activate child OKRs**:
85
+
86
+ 1. **DECOMPOSE** — run the `decompose-okr` skill against the approved parent
87
+ Mission. The runtime drafts child objectives + Key Results one tier down
88
+ (company→team, team→project). projectId is required on every child when the
89
+ child level is `project`.
90
+ ```bash
91
+ bash {{AGENT_SKILLS_PATH}}/orchestrator/decompose-okr/execute.sh --mission-id <parent-mission-id>
92
+ ```
93
+
94
+ 2. **PROPOSE** — POST the drafted children to
95
+ `POST /api/missions/<parent-id>/decompose-okr`. The backend creates each child
96
+ Mission with `approval.state = 'pending_approval'`, linked via
97
+ `parentMissionId`. **Pending children are excluded from roll-up and autonomous
98
+ execution** — they are NOT live yet.
99
+
100
+ 3. **AWAIT APPROVAL** — surface the proposal to the human owner (Steve) using the
101
+ `[APPROVE]` block the skill emits (or a `[NOTIFY]` summarising parent +
102
+ proposed children). The owner decides:
103
+ - **Approve** → `POST /api/missions/<childId>/approve` → child becomes
104
+ cascade-active (rolls up, executes).
105
+ - **Reject** → `POST /api/missions/<childId>/reject` with
106
+ `{"reason":"..."}` (reason is REQUIRED) → child is archived/excluded; redraft
107
+ if asked.
108
+ - List what is pending: `GET /api/missions/<parent-id>/proposals`.
109
+
110
+ **Hard rule:** Do NOT set `approval.state` via the mission PUT endpoint, and do
111
+ NOT begin executing or rolling up a child OKR until its `approval.state` is
112
+ `approved`. The decompose→propose→await-approval gate is mandatory; no child OKR
113
+ goes live without the owner's explicit approval.
114
+
115
+
76
116
  ## Universal Delegator Closure (§3.0 — MANDATORY for every dispatch)
77
117
 
78
118
  > Source spec: `.crewly/specs/2026-05-05-pipeline-dogfood-prompt-amendment.md` §3.0.
@@ -249,6 +249,39 @@ Loop until done, blocked, or explicitly reassigned:
249
249
 
250
250
  Default tier: **Standard Path** (customer-facing or coordination work). Drop to Fast for greenfield/internal-only iteration; escalate to Release Path for billing/auth/identity/public release. See `config/sops/common/dev-process-tiers.md`.
251
251
 
252
+ ## OKR Cascade — Decompose → Propose → Await-Approval (at OKR-period start)
253
+
254
+ > Source spec: `specs/okr-cascade.md`. The OKR cascade is the Mission tree —
255
+ > **company → team → project** via `Mission.parentMissionId`.
256
+
257
+ When the orchestrator hands you an **approved** parent Mission to break into the
258
+ next OKR tier (e.g. a company OKR → your team's OKRs, or your team OKR → project
259
+ OKRs) at the **start of an OKR period**, you draft a PROPOSAL — you do NOT
260
+ activate child OKRs yourself:
261
+
262
+ 1. **DECOMPOSE** — run the `decompose-okr` skill against the approved parent:
263
+ ```bash
264
+ bash {{AGENT_SKILLS_PATH}}/orchestrator/decompose-okr/execute.sh --mission-id <parent-mission-id>
265
+ ```
266
+ The runtime drafts child objectives + Key Results one tier down. When the
267
+ child level is `project`, every child needs a `projectId`.
268
+
269
+ 2. **PROPOSE** — POST the drafted children to
270
+ `POST /api/missions/<parent-id>/decompose-okr`. Each child Mission is created
271
+ `approval.state = 'pending_approval'`, linked via `parentMissionId`. Pending
272
+ children are **excluded from roll-up and autonomous execution** until approved.
273
+
274
+ 3. **AWAIT APPROVAL** — surface the proposal to the owner (Steve) via the
275
+ `[APPROVE]` block the skill emits (or a `[NOTIFY]` summary). The owner approves
276
+ (`POST /api/missions/<childId>/approve`) or rejects with a required reason
277
+ (`POST /api/missions/<childId>/reject` `{"reason":"..."}`). Check pending items
278
+ with `GET /api/missions/<parent-id>/proposals`.
279
+
280
+ **Hard rule:** Never set `approval.state` via the mission PUT endpoint, and never
281
+ begin staffing/executing or rolling up a child OKR until its `approval.state` is
282
+ `approved`. No child OKR goes live without the owner's explicit approval.
283
+
284
+
252
285
  ## Decision Rights
253
286
 
254
287
  **Decide autonomously when:**
@@ -47,13 +47,17 @@ Query standard operating procedures (SOPs) relevant to your current context or t
47
47
  | `context` | Yes | Description of what you need SOPs for (e.g., `"deploying to production"`) |
48
48
  | `category` | No | Filter by SOP category (e.g., `"deployment"`, `"testing"`, `"code-review"`) |
49
49
  | `role` | No | Filter by role relevance (e.g., `"developer"`, `"qa"`) |
50
+ | `sessionName` | No | Your session name. Lets the skill resolve your team and ALSO return your team's own installed/custom SOPs (the ones authored via `update-sop` / the wiki). |
51
+ | `teamId` | No | Explicit team id (skips the sessionName lookup). |
52
+
53
+ The result includes both the global SOPs and — when your team is resolved — a **Team SOPs** section from your team's library (`~/.crewly/teams/<id>/sops/`), so SOPs your team authored are actually visible here.
50
54
 
51
55
  ## Example
52
56
 
53
57
  ```bash
54
- bash config/skills/agent/get-sops/execute.sh '{"context":"deploying a new backend service","category":"deployment","role":"developer"}'
58
+ bash config/skills/agent/core/get-sops/execute.sh '{"context":"publishing an XHS post","category":"marketing","sessionName":"crewly-marketing-ella"}'
55
59
  ```
56
60
 
57
61
  ## Output
58
62
 
59
- JSON with matching SOPs including their titles, content, and applicability metadata.
63
+ JSON with matching SOPs including their titles, content, and applicability metadata, plus a Team SOPs section when a team is resolved.
@@ -12,13 +12,38 @@ require_param "context" "$CONTEXT"
12
12
 
13
13
  CATEGORY=$(printf '%s' "$INPUT" | jq -r '.category // empty')
14
14
  ROLE=$(printf '%s' "$INPUT" | jq -r '.role // empty')
15
+ TEAM_ID=$(printf '%s' "$INPUT" | jq -r '.teamId // empty')
16
+ SESSION_NAME=$(printf '%s' "$INPUT" | jq -r '.sessionName // empty')
17
+
18
+ # Resolve teamId so the backend can also surface THIS team's installed/custom
19
+ # SOP library (~/.crewly/teams/<id>/sops/), not just the global store.
20
+ CREWLY_HOME="${HOME}/.crewly"
21
+ resolve_team_id() {
22
+ local session="${1:-${CREWLY_SESSION_NAME:-}}"
23
+ [ -z "$session" ] && return 1
24
+ local teams_dir="${CREWLY_HOME}/teams"
25
+ [ ! -d "$teams_dir" ] && return 1
26
+ for config in "$teams_dir"/*/config.json; do
27
+ [ -f "$config" ] || continue
28
+ if [ "$(jq -r --arg s "$session" '.members[]? | select(.sessionName == $s) | "found"' "$config" 2>/dev/null | head -1)" = "found" ]; then
29
+ basename "$(dirname "$config")"
30
+ return 0
31
+ fi
32
+ done
33
+ return 1
34
+ }
35
+ if [ -z "$TEAM_ID" ]; then
36
+ TEAM_ID=$(resolve_team_id "$SESSION_NAME" || true)
37
+ fi
15
38
 
16
39
  BODY=$(jq -n \
17
40
  --arg context "$CONTEXT" \
18
41
  --arg category "$CATEGORY" \
19
42
  --arg role "$ROLE" \
43
+ --arg teamId "$TEAM_ID" \
20
44
  '{context: $context} +
21
45
  (if $category != "" then {category: $category} else {} end) +
22
- (if $role != "" then {role: $role} else {} end)')
46
+ (if $role != "" then {role: $role} else {} end) +
47
+ (if $teamId != "" then {teamId: $teamId} else {} end)')
23
48
 
24
49
  api_call POST "/system/sops/query" "$BODY"
@@ -0,0 +1,69 @@
1
+ ---
2
+ name: Get Team Norms
3
+ description: Read your team's norms (operating agreements — canDelegate, escalation, rules of engagement) relevant to the current moment.
4
+ version: 1.0.0
5
+ category: system
6
+ skillType: claude-skill
7
+ assignableRoles:
8
+ - developer
9
+ - qa
10
+ - tpm
11
+ - designer
12
+ - frontend-developer
13
+ - backend-developer
14
+ - fullstack-dev
15
+ - qa-engineer
16
+ - product-manager
17
+ - architect
18
+ - generalist
19
+ - team-leader
20
+ - sales
21
+ - support
22
+ triggers:
23
+ - get team norms
24
+ - team norms
25
+ - rules of engagement
26
+ - can i delegate
27
+ - escalation policy
28
+ tags:
29
+ - system
30
+ - norms
31
+ - governance
32
+ - team
33
+ execution:
34
+ type: script
35
+ script:
36
+ file: execute.sh
37
+ interpreter: bash
38
+ timeoutMs: 15000
39
+ ---
40
+
41
+ # Get Team Norms
42
+
43
+ Read your **team norms** — the team's own operating agreement: who may delegate
44
+ to whom (`canDelegate`), role conventions, escalation paths, decision rights,
45
+ and rules of engagement. Norms are team-specific and authored by the team
46
+ (by the owner in the wiki, or proposed by agents via `update-team-norm`).
47
+
48
+ Norms differ from SOPs: a **SOP** is a reusable procedure ("how to do task X");
49
+ a **norm** is "how THIS team operates together." Consult norms before acting on
50
+ anything governance-related — delegating, escalating, deciding scope.
51
+
52
+ ## Parameters
53
+
54
+ | Parameter | Required | Description |
55
+ |-----------|----------|-------------|
56
+ | `trigger` | No | Filter to norms relevant to a moment (e.g. `"before_commit"`, `"before_delegate"`). Omit to read all. |
57
+ | `teamId` | No | Team to read. Defaults to the team resolved from your `sessionName`. |
58
+ | `sessionName` | No | Your session name, used to resolve the team when `teamId` is omitted. |
59
+
60
+ ## Example
61
+
62
+ ```bash
63
+ bash config/skills/agent/core/get-team-norms/execute.sh '{"trigger":"before_delegate"}'
64
+ ```
65
+
66
+ ## Output
67
+
68
+ JSON `{ success, count, data: [{ normId, title, trigger, content, updatedBy, updatedAt }] }`.
69
+ Returns an empty list if the team has no norms yet.
@@ -0,0 +1,73 @@
1
+ ---
2
+ name: Update SOP
3
+ description: Author or update a custom SOP (a reusable procedure / playbook) for your team.
4
+ version: 1.0.0
5
+ category: system
6
+ skillType: claude-skill
7
+ assignableRoles:
8
+ - team-leader
9
+ - tpm
10
+ - product-manager
11
+ - architect
12
+ - generalist
13
+ - developer
14
+ - qa
15
+ - qa-engineer
16
+ - backend-developer
17
+ - frontend-developer
18
+ - fullstack-dev
19
+ triggers:
20
+ - update sop
21
+ - write a sop
22
+ - document the procedure
23
+ - create a playbook
24
+ - standardize how we
25
+ tags:
26
+ - system
27
+ - sops
28
+ - procedures
29
+ - team
30
+ execution:
31
+ type: script
32
+ script:
33
+ file: execute.sh
34
+ interpreter: bash
35
+ timeoutMs: 15000
36
+ ---
37
+
38
+ # Update SOP
39
+
40
+ Create or update a **custom SOP** for your team — a reusable procedure /
41
+ playbook for *how to do a class of work* (e.g. "publishing an XHS post",
42
+ "running a release"). The SOP is written into the team's own SOP store
43
+ (`~/.crewly/teams/<id>/sops/`) and surfaces in the wiki under the team's
44
+ **SOPs** folder, alongside SOPs installed from the catalog. The owner can edit
45
+ it in the wiki UI.
46
+
47
+ Use this for durable, repeatable procedures — not one-off task notes (use the
48
+ wiki/memory) and not team governance rules (those are **team norms** — use
49
+ `update-team-norm`). Prefer installing an existing catalog SOP when one fits;
50
+ author a custom SOP only when the team needs something the catalog lacks.
51
+
52
+ ## Parameters
53
+
54
+ | Parameter | Required | Description |
55
+ |-----------|----------|-------------|
56
+ | `sopId` | Yes | Stable kebab-case id / filename, e.g. `"xhs-posting"`. |
57
+ | `content` | Yes | The SOP body (markdown). The steps/procedure. |
58
+ | `title` | No | Human title, e.g. `"XHS Posting Checklist"`. Required when creating. |
59
+ | `category` | No | Optional sub-folder, e.g. `"common"`, `"marketing"`. |
60
+ | `append` | No | `"true"` to append to an existing SOP instead of overwriting. |
61
+ | `teamId` | No | Team to write to. Defaults to the team resolved from `sessionName`. |
62
+ | `sessionName` | No | Your session name, used to resolve the team when `teamId` is omitted. |
63
+ | `updatedBy` | No | Who authored the change (your agent/session name). |
64
+
65
+ ## Example
66
+
67
+ ```bash
68
+ bash config/skills/agent/core/update-sop/execute.sh '{"sopId":"xhs-posting","title":"XHS Posting Checklist","category":"marketing","content":"1. Draft hook.\n2. Add 3-5 tags.\n3. Post 7-9pm."}'
69
+ ```
70
+
71
+ ## Output
72
+
73
+ JSON `{ success, action: "created" | "updated", sopId, title, category, path, relativePath }`.
@@ -0,0 +1,115 @@
1
+ #!/bin/bash
2
+ # Create or update a team's custom SOP.
3
+ # Writes to ~/.crewly/teams/{teamId}/sops/[{category}/]{sopId}.md
4
+ # (the per-team installed/custom SOP store the wiki surfaces under sop/).
5
+ set -euo pipefail
6
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
7
+ source "${SCRIPT_DIR}/../../_common/lib.sh"
8
+
9
+ CREWLY_HOME="${HOME}/.crewly"
10
+
11
+ INPUT=$(read_json_input "${1:-}")
12
+ [ -z "$INPUT" ] && error_exit "Usage: execute.sh '{\"sopId\":\"xhs-posting\",\"title\":\"XHS Posting\",\"content\":\"...\",\"category\":\"common\"}'"
13
+
14
+ SOP_ID=$(printf '%s' "$INPUT" | jq -r '.sopId // empty')
15
+ TITLE=$(printf '%s' "$INPUT" | jq -r '.title // empty')
16
+ CATEGORY=$(printf '%s' "$INPUT" | jq -r '.category // empty')
17
+ CONTENT=$(printf '%s' "$INPUT" | jq -r '.content // empty')
18
+ APPEND=$(printf '%s' "$INPUT" | jq -r '.append // "false"')
19
+ TEAM_ID=$(printf '%s' "$INPUT" | jq -r '.teamId // empty')
20
+ SESSION_NAME=$(printf '%s' "$INPUT" | jq -r '.sessionName // empty')
21
+ UPDATED_BY=$(printf '%s' "$INPUT" | jq -r '.updatedBy // empty')
22
+
23
+ require_param "sopId" "$SOP_ID"
24
+ require_param "content" "$CONTENT"
25
+
26
+ # Sanitize id/category to safe path segments (no traversal, no slashes).
27
+ SOP_ID=$(printf '%s' "$SOP_ID" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9._-]/-/g; s/^-*//; s/-*$//')
28
+ CATEGORY=$(printf '%s' "$CATEGORY" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9_-]/-/g; s/^-*//; s/-*$//')
29
+ [ -z "$SOP_ID" ] && error_exit "sopId resolved to empty after sanitization"
30
+
31
+ # Resolve teamId: explicit param > lookup by sessionName > CREWLY_SESSION_NAME env
32
+ resolve_team_id() {
33
+ local session="${1:-${CREWLY_SESSION_NAME:-}}"
34
+ [ -z "$session" ] && return 1
35
+ local teams_dir="${CREWLY_HOME}/teams"
36
+ [ ! -d "$teams_dir" ] && return 1
37
+ for config in "$teams_dir"/*/config.json; do
38
+ [ -f "$config" ] || continue
39
+ local found
40
+ found=$(jq -r --arg s "$session" '.members[]? | select(.sessionName == $s) | "found"' "$config" 2>/dev/null | head -1)
41
+ if [ "$found" = "found" ]; then
42
+ basename "$(dirname "$config")"
43
+ return 0
44
+ fi
45
+ done
46
+ return 1
47
+ }
48
+
49
+ if [ -z "$TEAM_ID" ]; then
50
+ TEAM_ID=$(resolve_team_id "$SESSION_NAME") || error_exit "Could not resolve teamId. Provide teamId or sessionName."
51
+ fi
52
+
53
+ SOPS_DIR="${CREWLY_HOME}/teams/${TEAM_ID}/sops"
54
+ if [ -n "$CATEGORY" ]; then
55
+ SOPS_DIR="${SOPS_DIR}/${CATEGORY}"
56
+ REL_PATH="sop/${CATEGORY}/${SOP_ID}.md"
57
+ else
58
+ REL_PATH="sop/${SOP_ID}.md"
59
+ fi
60
+ mkdir -p "$SOPS_DIR"
61
+
62
+ SOP_FILE="${SOPS_DIR}/${SOP_ID}.md"
63
+ TODAY=$(date +%Y-%m-%d)
64
+ ACTION="created"
65
+
66
+ if [ -f "$SOP_FILE" ]; then
67
+ ACTION="updated"
68
+ EXISTING_TITLE=$(sed -n '/^---$/,/^---$/{ /^---$/d; s/^title: *//p; }' "$SOP_FILE" | head -1)
69
+ EXISTING_UPDATED_BY=$(sed -n '/^---$/,/^---$/{ /^---$/d; s/^updatedBy: *//p; }' "$SOP_FILE" | head -1)
70
+ [ -z "$TITLE" ] && TITLE="$EXISTING_TITLE"
71
+ [ -z "$UPDATED_BY" ] && UPDATED_BY="$EXISTING_UPDATED_BY"
72
+
73
+ if [ "$APPEND" = "true" ]; then
74
+ EXISTING_CONTENT=""
75
+ FM_DONE=false
76
+ FM_COUNT=0
77
+ while IFS= read -r line || [ -n "$line" ]; do
78
+ if [ "$line" = "---" ]; then
79
+ FM_COUNT=$((FM_COUNT + 1))
80
+ if [ "$FM_COUNT" -ge 2 ]; then
81
+ FM_DONE=true
82
+ continue
83
+ fi
84
+ elif [ "$FM_DONE" = true ]; then
85
+ EXISTING_CONTENT="${EXISTING_CONTENT}${line}
86
+ "
87
+ fi
88
+ done < "$SOP_FILE"
89
+ EXISTING_CONTENT=$(echo "$EXISTING_CONTENT" | sed '/./,$!d')
90
+ CONTENT="${EXISTING_CONTENT}
91
+ ${CONTENT}"
92
+ fi
93
+ else
94
+ [ -z "$TITLE" ] && error_exit "title is required when creating a new SOP"
95
+ fi
96
+
97
+ {
98
+ echo "---"
99
+ echo "title: ${TITLE}"
100
+ [ -n "$CATEGORY" ] && echo "category: ${CATEGORY}"
101
+ [ -n "$UPDATED_BY" ] && echo "updatedBy: ${UPDATED_BY}"
102
+ echo "updatedAt: ${TODAY}"
103
+ echo "---"
104
+ echo ""
105
+ echo "$CONTENT"
106
+ } > "$SOP_FILE"
107
+
108
+ jq -n \
109
+ --arg sopId "$SOP_ID" \
110
+ --arg title "$TITLE" \
111
+ --arg category "$CATEGORY" \
112
+ --arg action "$ACTION" \
113
+ --arg path "$SOP_FILE" \
114
+ --arg relativePath "$REL_PATH" \
115
+ '{success: true, action: $action, sopId: $sopId, title: $title, category: $category, path: $path, relativePath: $relativePath}'
@@ -0,0 +1,67 @@
1
+ ---
2
+ name: Update Team Norm
3
+ description: Propose or update a team norm (an operating agreement — canDelegate, escalation, rules of engagement) for your team.
4
+ version: 1.0.0
5
+ category: system
6
+ skillType: claude-skill
7
+ assignableRoles:
8
+ - team-leader
9
+ - tpm
10
+ - product-manager
11
+ - architect
12
+ - generalist
13
+ - developer
14
+ - qa
15
+ triggers:
16
+ - update team norm
17
+ - propose a norm
18
+ - set a team rule
19
+ - we should always
20
+ - going forward the team should
21
+ tags:
22
+ - system
23
+ - norms
24
+ - governance
25
+ - team
26
+ execution:
27
+ type: script
28
+ script:
29
+ file: execute.sh
30
+ interpreter: bash
31
+ timeoutMs: 15000
32
+ ---
33
+
34
+ # Update Team Norm
35
+
36
+ Create or update a **team norm** — a durable operating agreement for your team
37
+ (who may delegate to whom, escalation paths, decision rights, rules of
38
+ engagement, recurring conventions). Use this when a lasting team-wide rule
39
+ emerges that future work should follow — not for one-off task notes (use the
40
+ wiki/memory for those) and not for reusable procedures (those are SOPs).
41
+
42
+ Norms you write land in the team's norm store and surface in the wiki under the
43
+ team's **Team Norms** folder, where the owner can review and edit them. Treat
44
+ this as *proposing* a norm: keep it concise, state the rule and when it applies.
45
+
46
+ ## Parameters
47
+
48
+ | Parameter | Required | Description |
49
+ |-----------|----------|-------------|
50
+ | `normId` | Yes | Stable kebab-case id / filename, e.g. `"code-commit"`, `"delegation"`. |
51
+ | `content` | Yes | The norm body (markdown). State the rule and its rationale. |
52
+ | `title` | No | Human title, e.g. `"Code Commit Norm"`. |
53
+ | `trigger` | No | When this norm applies, e.g. `"before_commit"`, `"before_delegate"`. |
54
+ | `append` | No | `"true"` to append to an existing norm instead of overwriting. |
55
+ | `teamId` | No | Team to write to. Defaults to the team resolved from `sessionName`. |
56
+ | `sessionName` | No | Your session name, used to resolve the team when `teamId` is omitted. |
57
+ | `updatedBy` | No | Who authored the change (your agent/session name). |
58
+
59
+ ## Example
60
+
61
+ ```bash
62
+ bash config/skills/agent/core/update-team-norm/execute.sh '{"normId":"delegation","title":"Delegation","trigger":"before_delegate","content":"Leads may delegate to their own team only; cross-team work goes through the orchestrator."}'
63
+ ```
64
+
65
+ ## Output
66
+
67
+ JSON `{ success, action: "created" | "updated", normId, path }`.