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
@@ -0,0 +1,417 @@
1
+ /**
2
+ * OKR Cascade Service
3
+ *
4
+ * Owns the decompose → propose → approve/reject workflow for the OKR cascade
5
+ * (company → team → project). A team-leader/PM agent drafts child OKRs as a
6
+ * PROPOSAL; the child Missions are persisted with
7
+ * `approval.state = 'pending_approval'` and are NOT cascade-active until the
8
+ * human owner approves. Rejection carries a required reason.
9
+ *
10
+ * This service is the SINGLE owner of `approval.state` transitions — the
11
+ * `updateMission` PUT route blocks direct `approval` writes so that all
12
+ * transitions flow through {@link OKRCascadeService.approveDecomposition} and
13
+ * {@link OKRCascadeService.rejectDecomposition} (spec §3.2/§3.3).
14
+ *
15
+ * FLAG (spec §2.5): the approval *workflow* (this service + its routes) is a
16
+ * plausible future move to `crewly-pro` as a gated governance feature. The
17
+ * approval *types* live in `mission.types.ts` and stay there; only this
18
+ * workflow layer would relocate. Do NOT split now.
19
+ *
20
+ * Persistence reuses the existing on-disk layout:
21
+ * - Missions: `.crewly/missions/{id}.json`
22
+ * - KRs: `.crewly/missions/{id}/key-results/` (via {@link KRTrackingService})
23
+ *
24
+ * @module services/v3/okr-cascade.service
25
+ */
26
+ import * as fs from 'fs/promises';
27
+ import * as path from 'path';
28
+ import { LoggerService } from '../core/logger.service.js';
29
+ import { KRTrackingService } from './kr-tracking.service.js';
30
+ import { createMission, validateCascadeLink, resolveMissionLevel, isMission, isValidProposalState, isValidProposalTransition, MISSION_LEVELS, MISSION_LEVEL_DEPTH, } from '../../types/v2/mission.types.js';
31
+ // ---------------------------------------------------------------------------
32
+ // Constants
33
+ // ---------------------------------------------------------------------------
34
+ /** Workspace root folder for Crewly runtime state. */
35
+ const CREWLY_HOME = '.crewly';
36
+ /** Folder under {@link CREWLY_HOME} that holds one JSON file per Mission. */
37
+ const MISSIONS_DIR = 'missions';
38
+ /** Proposal state stamped on a freshly-proposed child mission. */
39
+ const PROPOSED_STATE = 'pending_approval';
40
+ /** Proposal state after a successful owner approval. */
41
+ const APPROVED_STATE = 'approved';
42
+ /** Proposal state after an owner rejection. */
43
+ const REJECTED_STATE = 'rejected';
44
+ /**
45
+ * State a corrupt/unrecognized persisted `approval.state` is normalized to on
46
+ * read. `rejected` is a recognized, INACTIVE, non-approvable state: a corrupt
47
+ * doc must NOT be trusted as cascade-active and must NOT be approvable directly
48
+ * (the only approve path is `pending_approval → approved`). See
49
+ * {@link OKRCascadeService.sanitizeMission}.
50
+ */
51
+ const CORRUPT_APPROVAL_FALLBACK = REJECTED_STATE;
52
+ // ---------------------------------------------------------------------------
53
+ // Service
54
+ // ---------------------------------------------------------------------------
55
+ /**
56
+ * Singleton service implementing the OKR decomposition / approval workflow.
57
+ */
58
+ export class OKRCascadeService {
59
+ static instance = null;
60
+ logger;
61
+ constructor() {
62
+ this.logger = LoggerService.getInstance().createComponentLogger('OKRCascade');
63
+ }
64
+ /**
65
+ * Get the singleton instance.
66
+ *
67
+ * @returns The shared {@link OKRCascadeService}
68
+ */
69
+ static getInstance() {
70
+ if (!OKRCascadeService.instance) {
71
+ OKRCascadeService.instance = new OKRCascadeService();
72
+ }
73
+ return OKRCascadeService.instance;
74
+ }
75
+ /**
76
+ * Reset the singleton (for testing).
77
+ */
78
+ static resetInstance() {
79
+ OKRCascadeService.instance = null;
80
+ }
81
+ // -------------------------------------------------------------------------
82
+ // Public API
83
+ // -------------------------------------------------------------------------
84
+ /**
85
+ * Draft a child OKR decomposition for a parent mission as a PROPOSAL.
86
+ *
87
+ * The parent must exist and be cascade-active (approval absent or
88
+ * `approval.state === 'approved'`). The child level is computed as exactly one
89
+ * tier below the parent; decomposing a `project`-level parent is rejected
90
+ * (there is no tier below project). Each child mission is created with
91
+ * `approval.state = 'pending_approval'` and is NOT active until the owner
92
+ * approves. Each child's Key Results are created via {@link KRTrackingService}
93
+ * (no duplicate KR persistence here).
94
+ *
95
+ * The decomposition is "fail before writing": EVERY child is fully validated
96
+ * (project-id presence + cascade-link adjacency/cycle) BEFORE any mission or
97
+ * KR is persisted, so a later invalid child never leaves an earlier child
98
+ * half-written to disk.
99
+ *
100
+ * @param parentId - ID of the parent mission to decompose
101
+ * @param input - The proposed child OKRs (objectives + KRs)
102
+ * @param proposedBy - Agent session that drafted the proposal
103
+ * @returns The parent id, resolved child level, and created child IDs
104
+ * @throws If the parent is missing, not approved, already at `project` level,
105
+ * the input has no children, or a child fails cascade-link validation
106
+ *
107
+ * @example
108
+ * ```ts
109
+ * const result = await OKRCascadeService.getInstance().proposeDecomposition(
110
+ * 'co-1',
111
+ * { children: [{ objective: 'Ship v2', currentStrategy: '...', successCriteria: [], keyResults: [] }] },
112
+ * 'team-leader-session',
113
+ * );
114
+ * ```
115
+ */
116
+ async proposeDecomposition(parentId, input, proposedBy) {
117
+ if (!input.children || input.children.length === 0) {
118
+ throw new Error('Decomposition requires at least one child OKR');
119
+ }
120
+ const all = await this.loadAllMissions();
121
+ const byId = new Map(all.map((m) => [m.id, m]));
122
+ const parent = byId.get(parentId);
123
+ if (!parent) {
124
+ throw new Error(`Parent mission ${parentId} does not exist`);
125
+ }
126
+ if (!this.isCascadeActive(parent)) {
127
+ throw new Error(`Parent mission ${parentId} is not approved; only approved missions can be decomposed`);
128
+ }
129
+ const parentLevel = this.resolveLevel(parent, byId);
130
+ const childLevel = this.nextLevel(parentLevel);
131
+ if (childLevel === null) {
132
+ throw new Error(`A ${parentLevel}-level mission is the bottom of the cascade and cannot be decomposed further`);
133
+ }
134
+ // ----- Pre-write validation pass (fail BEFORE any persist) -------------
135
+ // Build every child mission up-front and run the FULL cascade guard
136
+ // (project-id presence + validateCascadeLink) against the live tree plus
137
+ // the siblings already built in this same proposal. Only once ALL children
138
+ // pass do we persist anything — so a later invalid child can never leave an
139
+ // earlier child (or its KRs) on disk (transactional "all-or-nothing").
140
+ const now = new Date().toISOString();
141
+ const linkById = new Map(all.map((m) => [m.id, { id: m.id, parentMissionId: m.parentMissionId, level: m.level }]));
142
+ const prepared = [];
143
+ for (const child of input.children) {
144
+ if (childLevel === 'project' && !child.projectId) {
145
+ throw new Error('A project-level child OKR requires projectId');
146
+ }
147
+ const mission = createMission({
148
+ objective: child.objective,
149
+ ownerTeamId: parent.ownerTeamId,
150
+ successCriteria: child.successCriteria,
151
+ currentStrategy: child.currentStrategy,
152
+ parentMissionId: parentId,
153
+ level: childLevel,
154
+ projectId: child.projectId,
155
+ approval: {
156
+ state: PROPOSED_STATE,
157
+ proposedBy,
158
+ proposedAt: now,
159
+ },
160
+ });
161
+ const reason = validateCascadeLink(mission.id, childLevel, parentId, linkById);
162
+ if (reason) {
163
+ throw new Error(`Invalid cascade link for child OKR "${child.objective}": ${reason}`);
164
+ }
165
+ // Register the just-validated child so subsequent siblings see it.
166
+ linkById.set(mission.id, {
167
+ id: mission.id,
168
+ parentMissionId: mission.parentMissionId,
169
+ level: mission.level,
170
+ });
171
+ prepared.push({ mission, child });
172
+ }
173
+ // ----- Write pass (only reached when ALL children validated) -----------
174
+ const childMissionIds = [];
175
+ const krService = KRTrackingService.getInstance();
176
+ for (const { mission, child } of prepared) {
177
+ await this.persistMission(mission);
178
+ // KRs created via KRTrackingService — single source of KR persistence.
179
+ for (const kr of child.keyResults) {
180
+ await krService.create({ ...kr, missionId: mission.id });
181
+ }
182
+ childMissionIds.push(mission.id);
183
+ }
184
+ this.logger.info('Proposed OKR decomposition', {
185
+ parentId,
186
+ childLevel,
187
+ childCount: childMissionIds.length,
188
+ proposedBy,
189
+ });
190
+ return { parentId, childLevel, childMissionIds };
191
+ }
192
+ /**
193
+ * Approve a pending decomposition proposal, making the child mission
194
+ * cascade-active. Guarded by {@link isValidProposalTransition} so only a
195
+ * `pending_approval` mission may transition to `approved`.
196
+ *
197
+ * @param missionId - ID of the proposed (pending_approval) mission
198
+ * @param decidedBy - Human owner who approved (e.g. "steve")
199
+ * @returns The updated mission
200
+ * @throws If the mission is missing, has no approval state, or the
201
+ * `pending_approval → approved` transition is illegal
202
+ */
203
+ async approveDecomposition(missionId, decidedBy) {
204
+ const mission = await this.loadMission(missionId);
205
+ if (!mission) {
206
+ throw new Error(`Mission ${missionId} does not exist`);
207
+ }
208
+ const current = mission.approval?.state;
209
+ if (!current) {
210
+ throw new Error(`Mission ${missionId} has no proposal to approve`);
211
+ }
212
+ if (!isValidProposalTransition(current, APPROVED_STATE)) {
213
+ throw new Error(`Cannot approve a mission in state "${current}"`);
214
+ }
215
+ const decided = {
216
+ ...mission,
217
+ approval: {
218
+ ...mission.approval,
219
+ state: APPROVED_STATE,
220
+ decidedBy,
221
+ decidedAt: new Date().toISOString(),
222
+ },
223
+ updatedAt: new Date().toISOString(),
224
+ };
225
+ await this.persistMission(decided);
226
+ this.logger.info('Approved OKR decomposition', { missionId, decidedBy });
227
+ return decided;
228
+ }
229
+ /**
230
+ * Reject a pending decomposition proposal with a required reason. The
231
+ * rejected mission is excluded from roll-up and autonomous execution.
232
+ *
233
+ * @param missionId - ID of the proposed (pending_approval) mission
234
+ * @param decidedBy - Human owner who rejected (e.g. "steve")
235
+ * @param reason - Non-empty explanation surfaced back to the proposing agent
236
+ * @returns The updated mission
237
+ * @throws If the reason is empty, the mission is missing, has no approval
238
+ * state, or the `pending_approval → rejected` transition is illegal
239
+ */
240
+ async rejectDecomposition(missionId, decidedBy, reason) {
241
+ if (!reason || reason.trim().length === 0) {
242
+ throw new Error('Rejection requires a non-empty reason');
243
+ }
244
+ const mission = await this.loadMission(missionId);
245
+ if (!mission) {
246
+ throw new Error(`Mission ${missionId} does not exist`);
247
+ }
248
+ const current = mission.approval?.state;
249
+ if (!current) {
250
+ throw new Error(`Mission ${missionId} has no proposal to reject`);
251
+ }
252
+ if (!isValidProposalTransition(current, REJECTED_STATE)) {
253
+ throw new Error(`Cannot reject a mission in state "${current}"`);
254
+ }
255
+ const decided = {
256
+ ...mission,
257
+ approval: {
258
+ ...mission.approval,
259
+ state: REJECTED_STATE,
260
+ decidedBy,
261
+ decidedAt: new Date().toISOString(),
262
+ rejectionReason: reason.trim(),
263
+ },
264
+ updatedAt: new Date().toISOString(),
265
+ };
266
+ await this.persistMission(decided);
267
+ this.logger.info('Rejected OKR decomposition', { missionId, decidedBy, reason: reason.trim() });
268
+ return decided;
269
+ }
270
+ /**
271
+ * List missions awaiting an owner decision (`approval.state ===
272
+ * 'pending_approval'`). Optionally scope to the direct children of one parent.
273
+ *
274
+ * @param parentId - When provided, only pending children of this parent
275
+ * @returns Pending-approval missions
276
+ */
277
+ async listPendingApprovals(parentId) {
278
+ const all = await this.loadAllMissions();
279
+ return all.filter((m) => {
280
+ if (m.approval?.state !== PROPOSED_STATE)
281
+ return false;
282
+ if (parentId !== undefined && m.parentMissionId !== parentId)
283
+ return false;
284
+ return true;
285
+ });
286
+ }
287
+ // -------------------------------------------------------------------------
288
+ // Internal helpers
289
+ // -------------------------------------------------------------------------
290
+ /**
291
+ * Whether a mission counts as cascade-active: approval absent (legacy) or
292
+ * explicitly approved.
293
+ *
294
+ * @param mission - Mission to test
295
+ * @returns `true` if the mission participates in the cascade
296
+ */
297
+ isCascadeActive(mission) {
298
+ return mission.approval === undefined || mission.approval.state === APPROVED_STATE;
299
+ }
300
+ /**
301
+ * Resolve a mission's effective level through the SINGLE canonical resolver
302
+ * shared with the routes' read-migration ({@link resolveMissionLevel}), which
303
+ * prefers an explicit `level` and otherwise falls back to `deriveLevel` over
304
+ * the parent chain. A parentless legacy mission therefore resolves to
305
+ * `company` consistently across every read path.
306
+ *
307
+ * @param mission - Mission whose level to resolve
308
+ * @param byId - All missions indexed by ID for the parent-chain walk
309
+ * @returns The mission's resolved level
310
+ */
311
+ resolveLevel(mission, byId) {
312
+ return resolveMissionLevel(mission, byId);
313
+ }
314
+ /**
315
+ * Compute the cascade level exactly one tier below the given level.
316
+ *
317
+ * @param level - Parent level
318
+ * @returns The child level, or `null` if `level` is already the bottom tier
319
+ */
320
+ nextLevel(level) {
321
+ const childDepth = MISSION_LEVEL_DEPTH[level] + 1;
322
+ return MISSION_LEVELS[childDepth] ?? null;
323
+ }
324
+ /** Absolute path to the missions directory under the current cwd. */
325
+ getMissionsDir() {
326
+ return path.join(process.cwd(), CREWLY_HOME, MISSIONS_DIR);
327
+ }
328
+ /** Absolute path to a single mission's JSON file. */
329
+ getMissionPath(missionId) {
330
+ return path.join(this.getMissionsDir(), `${missionId}.json`);
331
+ }
332
+ /**
333
+ * Validate + normalize a parsed mission document read from disk.
334
+ *
335
+ * - Returns `null` for anything that is not structurally a {@link Mission}
336
+ * (corrupt / hand-edited / wrong shape), so callers never trust junk.
337
+ * - When `approval` is present but `approval.state` is NOT a recognized
338
+ * {@link isValidProposalState}, the state is normalized to
339
+ * {@link CORRUPT_APPROVAL_FALLBACK} (`rejected`). This deliberately treats a
340
+ * corrupt governance state as INACTIVE — neither cascade-active nor a
341
+ * recognized pending state — rather than silently trusting an unknown
342
+ * string (which would otherwise be neither active nor pending and could
343
+ * crash transition guards).
344
+ *
345
+ * @param value - Raw JSON-parsed value
346
+ * @returns A validated, normalized Mission, or `null` if not a valid Mission
347
+ */
348
+ sanitizeMission(value) {
349
+ if (!isMission(value))
350
+ return null;
351
+ const mission = value;
352
+ if (mission.approval !== undefined && !isValidProposalState(mission.approval.state)) {
353
+ this.logger.warn('Normalized corrupt approval.state to inactive', {
354
+ missionId: mission.id,
355
+ rawState: mission.approval.state,
356
+ });
357
+ return {
358
+ ...mission,
359
+ approval: { ...mission.approval, state: CORRUPT_APPROVAL_FALLBACK },
360
+ };
361
+ }
362
+ return mission;
363
+ }
364
+ /**
365
+ * Load a single mission from disk, validated + normalized via
366
+ * {@link sanitizeMission}.
367
+ *
368
+ * @param missionId - Mission ID
369
+ * @returns The mission, or `null` if missing/unreadable/not a valid Mission
370
+ */
371
+ async loadMission(missionId) {
372
+ try {
373
+ const raw = await fs.readFile(this.getMissionPath(missionId), 'utf-8');
374
+ return this.sanitizeMission(JSON.parse(raw));
375
+ }
376
+ catch {
377
+ return null;
378
+ }
379
+ }
380
+ /**
381
+ * Load all missions from disk, skipping unreadable AND structurally-invalid
382
+ * files (each validated + normalized via {@link sanitizeMission}).
383
+ *
384
+ * @returns Array of valid missions
385
+ */
386
+ async loadAllMissions() {
387
+ const dir = this.getMissionsDir();
388
+ let files = [];
389
+ try {
390
+ files = (await fs.readdir(dir)).filter((f) => f.endsWith('.json'));
391
+ }
392
+ catch {
393
+ return [];
394
+ }
395
+ const missions = await Promise.all(files.map(async (f) => {
396
+ try {
397
+ const raw = await fs.readFile(path.join(dir, f), 'utf-8');
398
+ return this.sanitizeMission(JSON.parse(raw));
399
+ }
400
+ catch {
401
+ return null;
402
+ }
403
+ }));
404
+ return missions.filter((m) => m !== null);
405
+ }
406
+ /**
407
+ * Persist a mission to disk (creating the missions directory if needed).
408
+ *
409
+ * @param mission - Mission to write
410
+ */
411
+ async persistMission(mission) {
412
+ const dir = this.getMissionsDir();
413
+ await fs.mkdir(dir, { recursive: true });
414
+ await fs.writeFile(this.getMissionPath(mission.id), JSON.stringify(mission, null, 2));
415
+ }
416
+ }
417
+ //# sourceMappingURL=okr-cascade.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"okr-cascade.service.js","sourceRoot":"","sources":["../../../../../../backend/src/services/v3/okr-cascade.service.ts"],"names":[],"mappings":"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"}
@@ -50,6 +50,17 @@ export declare class OKRReviewService {
50
50
  * Update mission fields on disk (partial merge).
51
51
  */
52
52
  private updateMissionReview;
53
+ /**
54
+ * Refresh a parent mission's `lastReviewSummary` using the cascade roll-up.
55
+ *
56
+ * Called on-demand after a child review so the parent reflects its children's
57
+ * latest progress without a separate scheduled review. No-op when the
58
+ * reviewed mission has no parent or the parent has vanished. Errors are
59
+ * swallowed and logged — a roll-up refresh must never fail the child review.
60
+ *
61
+ * @param parentMissionId - The reviewed mission's parent (may be undefined)
62
+ */
63
+ private refreshParentRollup;
53
64
  /**
54
65
  * Extract previous overall progress from review summary string.
55
66
  */
@@ -1 +1 @@
1
- {"version":3,"file":"okr-review.service.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/v3/okr-review.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAOH,OAAO,KAAK,EACV,eAAe,EACf,cAAc,EAEf,MAAM,oCAAoC,CAAC;AAgB5C;;GAEG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAiC;IACxD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAkB;IAEzC,OAAO;IAIP,MAAM,CAAC,WAAW,IAAI,gBAAgB;IAOtC,MAAM,CAAC,aAAa,IAAI,IAAI;IAQ5B;;;;;;;;;;;;OAYG;IACG,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IAyEhE;;;;;OAKG;IACG,qBAAqB,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAmEvF;;OAEG;YACW,WAAW;IAUzB;;OAEG;YACW,mBAAmB;IAYjC;;OAEG;IACH,OAAO,CAAC,uBAAuB;CAKhC"}
1
+ {"version":3,"file":"okr-review.service.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/v3/okr-review.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAOH,OAAO,KAAK,EACV,eAAe,EACf,cAAc,EAGf,MAAM,oCAAoC,CAAC;AA6B5C;;GAEG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAiC;IACxD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAkB;IAEzC,OAAO;IAIP,MAAM,CAAC,WAAW,IAAI,gBAAgB;IAOtC,MAAM,CAAC,aAAa,IAAI,IAAI;IAQ5B;;;;;;;;;;;;OAYG;IACG,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IA8EhE;;;;;OAKG;IACG,qBAAqB,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAmEvF;;OAEG;YACW,WAAW;IAUzB;;OAEG;YACW,mBAAmB;IAYjC;;;;;;;;;OASG;YACW,mBAAmB;IAwBjC;;OAEG;IACH,OAAO,CAAC,uBAAuB;CAKhC"}
@@ -22,6 +22,16 @@ import { MissionExecutorService } from './mission-executor.service.js';
22
22
  function getMissionsDir() {
23
23
  return path.join(process.cwd(), '.crewly', 'missions');
24
24
  }
25
+ /**
26
+ * Build the one-line review summary string persisted on a mission. Kept in one
27
+ * place so the on-demand parent roll-up refresh and the self-review write stay
28
+ * consistent and machine-parseable (see {@link OKRReviewService}).
29
+ */
30
+ function formatCascadeSummary(summary) {
31
+ return (`Roll-up: ${summary.rolledUpProgress}% ` +
32
+ `(own ${summary.overallProgress}%, ${summary.childMissionCount} child OKR(s)) | ` +
33
+ `Recommendation: ${summary.recommendation}`);
34
+ }
25
35
  // ---------------------------------------------------------------------------
26
36
  // Service
27
37
  // ---------------------------------------------------------------------------
@@ -109,6 +119,10 @@ export class OKRReviewService {
109
119
  staleCycles: newStaleCycles,
110
120
  lastReviewAt: result.reviewedAt,
111
121
  });
122
+ // Cross-level roll-up: on-demand at review time, refresh this mission's
123
+ // parent so the parent's lastReviewSummary reflects the (now-updated) child.
124
+ // Event-driven refresh on every child measurement is a follow-up (spec §4.3).
125
+ await this.refreshParentRollup(mission.parentMissionId);
112
126
  this.logger.info('OKR review completed', {
113
127
  missionId,
114
128
  progress: finalSummary.overallProgress,
@@ -208,6 +222,42 @@ export class OKRReviewService {
208
222
  const filePath = path.join(getMissionsDir(), `${missionId}.json`);
209
223
  await fs.writeFile(filePath, JSON.stringify(merged, null, 2));
210
224
  }
225
+ /**
226
+ * Refresh a parent mission's `lastReviewSummary` using the cascade roll-up.
227
+ *
228
+ * Called on-demand after a child review so the parent reflects its children's
229
+ * latest progress without a separate scheduled review. No-op when the
230
+ * reviewed mission has no parent or the parent has vanished. Errors are
231
+ * swallowed and logged — a roll-up refresh must never fail the child review.
232
+ *
233
+ * @param parentMissionId - The reviewed mission's parent (may be undefined)
234
+ */
235
+ async refreshParentRollup(parentMissionId) {
236
+ if (!parentMissionId)
237
+ return;
238
+ const parent = await this.loadMission(parentMissionId);
239
+ if (!parent)
240
+ return;
241
+ try {
242
+ const krService = KRTrackingService.getInstance();
243
+ const rollup = await krService.computeCascadeOKRProgress(parentMissionId);
244
+ await this.updateMissionReview(parentMissionId, {
245
+ lastReviewSummary: formatCascadeSummary(rollup),
246
+ lastReviewAt: new Date().toISOString(),
247
+ });
248
+ this.logger.info('Refreshed parent roll-up summary', {
249
+ parentMissionId,
250
+ rolledUpProgress: rollup.rolledUpProgress,
251
+ childMissionCount: rollup.childMissionCount,
252
+ });
253
+ }
254
+ catch (err) {
255
+ this.logger.warn('Parent roll-up refresh failed', {
256
+ parentMissionId,
257
+ error: err.message,
258
+ });
259
+ }
260
+ }
211
261
  /**
212
262
  * Extract previous overall progress from review summary string.
213
263
  */
@@ -1 +1 @@
1
- {"version":3,"file":"okr-review.service.js","sourceRoot":"","sources":["../../../../../../backend/src/services/v3/okr-review.service.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"okr-review.service.js","sourceRoot":"","sources":["../../../../../../backend/src/services/v3/okr-review.service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,83 @@
1
+ /**
2
+ * SOP catalog service.
3
+ *
4
+ * `config/sops/` is a *catalog* of reusable SOPs (a marketplace), NOT content a
5
+ * team owns automatically. A team owns a SOP only once it is INSTALLED — copied
6
+ * into the team's own `~/.crewly/teams/<id>/sops/` store, which the wiki then
7
+ * surfaces (via the overlay resolver) under the team's `sop/` folder.
8
+ *
9
+ * This service lists the catalog, reports which entries a given team has
10
+ * installed, and performs install / uninstall (copy / remove of the per-team
11
+ * copy — the catalog original is never touched).
12
+ *
13
+ * @module services/wiki/sop-catalog.service
14
+ */
15
+ /** A single SOP available in the catalog. */
16
+ export interface SopCatalogEntry {
17
+ /** Catalog-relative path, e.g. `pm/progress-tracking.md`. Stable id. */
18
+ path: string;
19
+ /** Display title (from frontmatter `title:` or the filename). */
20
+ title: string;
21
+ /** Top-level category folder (`common`, `developer`, …) or `general`. */
22
+ category: string;
23
+ /** File size in bytes. */
24
+ bytes: number;
25
+ /** Whether the queried team has this SOP installed. */
26
+ installed: boolean;
27
+ }
28
+ /**
29
+ * Resolve the catalog directory (`<configDir>/sops`). `<configDir>` mirrors the
30
+ * convention used by other services (`<cwd>/config`), overridable via
31
+ * `CREWLY_CONFIG_DIR`.
32
+ *
33
+ * @returns Absolute path to the SOP catalog directory.
34
+ */
35
+ export declare function sopCatalogDir(): string;
36
+ /**
37
+ * Resolve a team's installed-SOP store from its vault path. The store is a
38
+ * sibling of the vault (`<vault>/../sops`), matching the overlay resolver.
39
+ *
40
+ * @param vaultPath - Absolute path to the team vault (`.../teams/<id>/wiki`).
41
+ * @returns Absolute path to the team's installed-SOP directory.
42
+ */
43
+ export declare function teamSopsDir(vaultPath: string): string;
44
+ /**
45
+ * The SOP catalog service. Stateless; methods take the team vault path so the
46
+ * same instance serves any team.
47
+ */
48
+ export declare class SopCatalogService {
49
+ /**
50
+ * List the catalog, annotated with whether the given team has each installed.
51
+ *
52
+ * @param vaultPath - The team vault path (to resolve its installed store).
53
+ * @returns Catalog entries sorted by category then path.
54
+ */
55
+ list(vaultPath: string): Promise<SopCatalogEntry[]>;
56
+ /**
57
+ * Install a catalog SOP into the team's store (copy, preserving the relative
58
+ * path). Idempotent — re-installing refreshes the copy to the catalog version.
59
+ *
60
+ * @param vaultPath - The team vault path.
61
+ * @param sopPath - Catalog-relative SOP path (e.g. `pm/progress-tracking.md`).
62
+ * @returns `{ installed: true, path }`.
63
+ * @throws Error when the path is unsafe or the catalog file is missing.
64
+ */
65
+ install(vaultPath: string, sopPath: string): Promise<{
66
+ installed: true;
67
+ path: string;
68
+ }>;
69
+ /**
70
+ * Remove a SOP from the team's store. The catalog original is untouched.
71
+ * Idempotent — removing a not-installed SOP is a no-op.
72
+ *
73
+ * @param vaultPath - The team vault path.
74
+ * @param sopPath - Catalog-relative SOP path.
75
+ * @returns `{ installed: false, path }`.
76
+ * @throws Error when the path is unsafe.
77
+ */
78
+ uninstall(vaultPath: string, sopPath: string): Promise<{
79
+ installed: false;
80
+ path: string;
81
+ }>;
82
+ }
83
+ //# sourceMappingURL=sop-catalog.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sop-catalog.service.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/wiki/sop-catalog.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAKH,6CAA6C;AAC7C,MAAM,WAAW,eAAe;IAC9B,wEAAwE;IACxE,IAAI,EAAE,MAAM,CAAC;IACb,iEAAiE;IACjE,KAAK,EAAE,MAAM,CAAC;IACd,yEAAyE;IACzE,QAAQ,EAAE,MAAM,CAAC;IACjB,0BAA0B;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,uDAAuD;IACvD,SAAS,EAAE,OAAO,CAAC;CACpB;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,IAAI,MAAM,CAKtC;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAErD;AA0DD;;;GAGG;AACH,qBAAa,iBAAiB;IAC5B;;;;;OAKG;IACG,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;IA4BzD;;;;;;;;OAQG;IACG,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,SAAS,EAAE,IAAI,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IAU7F;;;;;;;;OAQG;IACG,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,SAAS,EAAE,KAAK,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;CAsBjG"}