crewly 1.6.5 → 1.7.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/config/roles/auditor/prompt.md +24 -0
- package/config/roles/developer/prompt.md +2 -1
- package/config/roles/orchestrator/prompt.md +74 -2
- package/config/roles/team-leader/prompt.md +6 -0
- package/config/skills/agent/core/create-request/SKILL.md +1 -1
- package/config/skills/agent/core/create-request/execute.sh +29 -2
- package/config/skills/agent/core/create-request/execute.test.sh +168 -0
- package/config/skills/agent/core/report-status/SKILL.md +8 -1
- package/config/skills/agent/core/report-status/execute.sh +23 -1
- package/config/skills/orchestrator/heartbeat/execute.sh +48 -6
- package/config/sops/common/mid-flight-milestone-surface.md +128 -0
- package/config/sops/common/owner-facing-communication.md +46 -2
- package/config/sops/developer/git-workflow.md +33 -0
- package/dist/backend/backend/src/constants.d.ts +12 -0
- package/dist/backend/backend/src/constants.d.ts.map +1 -1
- package/dist/backend/backend/src/constants.js +12 -0
- package/dist/backend/backend/src/constants.js.map +1 -1
- package/dist/backend/backend/src/controllers/browser/browser.controller.js +2 -2
- package/dist/backend/backend/src/controllers/browser/browser.controller.js.map +1 -1
- package/dist/backend/backend/src/controllers/chat/chat.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/chat/chat.controller.js +6 -0
- package/dist/backend/backend/src/controllers/chat/chat.controller.js.map +1 -1
- package/dist/backend/backend/src/controllers/chat-v2/chat-v2.controller.d.ts +73 -0
- package/dist/backend/backend/src/controllers/chat-v2/chat-v2.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/chat-v2/chat-v2.controller.js +128 -0
- package/dist/backend/backend/src/controllers/chat-v2/chat-v2.controller.js.map +1 -1
- package/dist/backend/backend/src/controllers/chat-v2/chat-v2.routes.d.ts +3 -0
- package/dist/backend/backend/src/controllers/chat-v2/chat-v2.routes.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/chat-v2/chat-v2.routes.js +8 -0
- package/dist/backend/backend/src/controllers/chat-v2/chat-v2.routes.js.map +1 -1
- package/dist/backend/backend/src/controllers/session/session.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/session/session.controller.js +50 -8
- package/dist/backend/backend/src/controllers/session/session.controller.js.map +1 -1
- package/dist/backend/backend/src/controllers/slack/slack.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/slack/slack.controller.js +215 -94
- package/dist/backend/backend/src/controllers/slack/slack.controller.js.map +1 -1
- package/dist/backend/backend/src/index.d.ts +1 -0
- package/dist/backend/backend/src/index.d.ts.map +1 -1
- package/dist/backend/backend/src/index.js +185 -36
- package/dist/backend/backend/src/index.js.map +1 -1
- package/dist/backend/backend/src/routes/api.routes.d.ts.map +1 -1
- package/dist/backend/backend/src/routes/api.routes.js +11 -1
- package/dist/backend/backend/src/routes/api.routes.js.map +1 -1
- package/dist/backend/backend/src/services/agent/agent-registration.service.d.ts +42 -0
- package/dist/backend/backend/src/services/agent/agent-registration.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/agent/agent-registration.service.js +218 -6
- package/dist/backend/backend/src/services/agent/agent-registration.service.js.map +1 -1
- package/dist/backend/backend/src/services/agent/crewly-agent/agent-runner.service.d.ts +61 -1
- package/dist/backend/backend/src/services/agent/crewly-agent/agent-runner.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/agent/crewly-agent/agent-runner.service.js +117 -9
- package/dist/backend/backend/src/services/agent/crewly-agent/agent-runner.service.js.map +1 -1
- package/dist/backend/backend/src/services/agent/idle-detection.service.d.ts +33 -0
- package/dist/backend/backend/src/services/agent/idle-detection.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/agent/idle-detection.service.js +108 -4
- package/dist/backend/backend/src/services/agent/idle-detection.service.js.map +1 -1
- package/dist/backend/backend/src/services/browser/browser-proxy.service.d.ts +1 -1
- package/dist/backend/backend/src/services/browser/browser-proxy.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/browser/browser-proxy.service.js +40 -2
- package/dist/backend/backend/src/services/browser/browser-proxy.service.js.map +1 -1
- package/dist/backend/backend/src/services/chat/chat.service.d.ts +48 -331
- package/dist/backend/backend/src/services/chat/chat.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/chat/chat.service.js +261 -712
- package/dist/backend/backend/src/services/chat/chat.service.js.map +1 -1
- package/dist/backend/backend/src/services/chat-v2/chat-v2.dispatcher.service.d.ts +82 -1
- package/dist/backend/backend/src/services/chat-v2/chat-v2.dispatcher.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/chat-v2/chat-v2.dispatcher.service.js +120 -2
- package/dist/backend/backend/src/services/chat-v2/chat-v2.dispatcher.service.js.map +1 -1
- package/dist/backend/backend/src/services/chat-v2/chat-v2.providers.d.ts +114 -0
- package/dist/backend/backend/src/services/chat-v2/chat-v2.providers.d.ts.map +1 -0
- package/dist/backend/backend/src/services/chat-v2/chat-v2.providers.js +182 -0
- package/dist/backend/backend/src/services/chat-v2/chat-v2.providers.js.map +1 -0
- package/dist/backend/backend/src/services/chat-v2/chat-v2.relay-adapter.service.d.ts +188 -0
- package/dist/backend/backend/src/services/chat-v2/chat-v2.relay-adapter.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/chat-v2/chat-v2.relay-adapter.service.js +434 -0
- package/dist/backend/backend/src/services/chat-v2/chat-v2.relay-adapter.service.js.map +1 -0
- package/dist/backend/backend/src/services/chat-v2/chat-v2.service.d.ts +401 -5
- package/dist/backend/backend/src/services/chat-v2/chat-v2.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/chat-v2/chat-v2.service.js +619 -3
- package/dist/backend/backend/src/services/chat-v2/chat-v2.service.js.map +1 -1
- package/dist/backend/backend/src/services/chat-v2/legacy-dto.utils.d.ts +93 -0
- package/dist/backend/backend/src/services/chat-v2/legacy-dto.utils.d.ts.map +1 -0
- package/dist/backend/backend/src/services/chat-v2/legacy-dto.utils.js +138 -0
- package/dist/backend/backend/src/services/chat-v2/legacy-dto.utils.js.map +1 -0
- package/dist/backend/backend/src/services/chat-v2/sqlite/channel.store.d.ts +46 -0
- package/dist/backend/backend/src/services/chat-v2/sqlite/channel.store.d.ts.map +1 -1
- package/dist/backend/backend/src/services/chat-v2/sqlite/channel.store.js +75 -0
- package/dist/backend/backend/src/services/chat-v2/sqlite/channel.store.js.map +1 -1
- package/dist/backend/backend/src/services/chat-v2/sqlite/chat-db.d.ts +10 -2
- package/dist/backend/backend/src/services/chat-v2/sqlite/chat-db.d.ts.map +1 -1
- package/dist/backend/backend/src/services/chat-v2/sqlite/chat-db.js +178 -10
- package/dist/backend/backend/src/services/chat-v2/sqlite/chat-db.js.map +1 -1
- package/dist/backend/backend/src/services/chat-v2/sqlite/message.store.d.ts +37 -0
- package/dist/backend/backend/src/services/chat-v2/sqlite/message.store.d.ts.map +1 -1
- package/dist/backend/backend/src/services/chat-v2/sqlite/message.store.js +71 -0
- package/dist/backend/backend/src/services/chat-v2/sqlite/message.store.js.map +1 -1
- package/dist/backend/backend/src/services/chat-v2/types.d.ts +33 -1
- package/dist/backend/backend/src/services/chat-v2/types.d.ts.map +1 -1
- package/dist/backend/backend/src/services/chat-v2/types.js +1 -1
- package/dist/backend/backend/src/services/chat-v2/types.js.map +1 -1
- package/dist/backend/backend/src/services/cloud/cloud-sync.service.d.ts +22 -0
- package/dist/backend/backend/src/services/cloud/cloud-sync.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/cloud/cloud-sync.service.js +71 -1
- package/dist/backend/backend/src/services/cloud/cloud-sync.service.js.map +1 -1
- package/dist/backend/backend/src/services/cloud/cloud-sync.types.d.ts +102 -1
- package/dist/backend/backend/src/services/cloud/cloud-sync.types.d.ts.map +1 -1
- package/dist/backend/backend/src/services/cloud/cloud-sync.types.js +61 -0
- package/dist/backend/backend/src/services/cloud/cloud-sync.types.js.map +1 -1
- package/dist/backend/backend/src/services/cloud/device-auto-discovery.service.d.ts +21 -3
- package/dist/backend/backend/src/services/cloud/device-auto-discovery.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/cloud/device-auto-discovery.service.js +47 -13
- package/dist/backend/backend/src/services/cloud/device-auto-discovery.service.js.map +1 -1
- package/dist/backend/backend/src/services/core/system-health.util.d.ts +25 -4
- package/dist/backend/backend/src/services/core/system-health.util.d.ts.map +1 -1
- package/dist/backend/backend/src/services/core/system-health.util.js +30 -5
- package/dist/backend/backend/src/services/core/system-health.util.js.map +1 -1
- package/dist/backend/backend/src/services/event-bus/event-bus.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/event-bus/event-bus.service.js +22 -11
- package/dist/backend/backend/src/services/event-bus/event-bus.service.js.map +1 -1
- package/dist/backend/backend/src/services/memory/memory.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/memory/memory.service.js +35 -3
- package/dist/backend/backend/src/services/memory/memory.service.js.map +1 -1
- package/dist/backend/backend/src/services/messaging/message-replay.service.d.ts +2 -4
- package/dist/backend/backend/src/services/messaging/message-replay.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/messaging/message-replay.service.js +22 -12
- package/dist/backend/backend/src/services/messaging/message-replay.service.js.map +1 -1
- package/dist/backend/backend/src/services/messaging/queue-processor.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/messaging/queue-processor.service.js +25 -5
- package/dist/backend/backend/src/services/messaging/queue-processor.service.js.map +1 -1
- package/dist/backend/backend/src/services/monitoring/system-resource-alert.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/monitoring/system-resource-alert.service.js +13 -3
- package/dist/backend/backend/src/services/monitoring/system-resource-alert.service.js.map +1 -1
- package/dist/backend/backend/src/services/notification/milestone-notification.subscriber.d.ts +99 -0
- package/dist/backend/backend/src/services/notification/milestone-notification.subscriber.d.ts.map +1 -0
- package/dist/backend/backend/src/services/notification/milestone-notification.subscriber.js +225 -0
- package/dist/backend/backend/src/services/notification/milestone-notification.subscriber.js.map +1 -0
- package/dist/backend/backend/src/services/reconciler/reconcile-rules.d.ts +39 -18
- package/dist/backend/backend/src/services/reconciler/reconcile-rules.d.ts.map +1 -1
- package/dist/backend/backend/src/services/reconciler/reconcile-rules.js +60 -32
- package/dist/backend/backend/src/services/reconciler/reconcile-rules.js.map +1 -1
- package/dist/backend/backend/src/services/reconciler/reconciler-data-provider.d.ts +134 -0
- package/dist/backend/backend/src/services/reconciler/reconciler-data-provider.d.ts.map +1 -1
- package/dist/backend/backend/src/services/reconciler/reconciler-data-provider.js +416 -13
- package/dist/backend/backend/src/services/reconciler/reconciler-data-provider.js.map +1 -1
- package/dist/backend/backend/src/services/reconciler/reconciler.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/reconciler/reconciler.service.js +73 -7
- package/dist/backend/backend/src/services/reconciler/reconciler.service.js.map +1 -1
- package/dist/backend/backend/src/services/session/session-handoff.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/session/session-handoff.service.js +30 -4
- package/dist/backend/backend/src/services/session/session-handoff.service.js.map +1 -1
- package/dist/backend/backend/src/services/skill/skill-executor.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/skill/skill-executor.service.js +13 -1
- package/dist/backend/backend/src/services/skill/skill-executor.service.js.map +1 -1
- package/dist/backend/backend/src/services/slack/notify-reconciliation.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/slack/notify-reconciliation.service.js +9 -6
- package/dist/backend/backend/src/services/slack/notify-reconciliation.service.js.map +1 -1
- package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.d.ts +21 -2
- package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.d.ts.map +1 -1
- package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.js +120 -46
- package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.js.map +1 -1
- package/dist/backend/backend/src/services/slack/slack.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/slack/slack.service.js +49 -0
- package/dist/backend/backend/src/services/slack/slack.service.js.map +1 -1
- package/dist/backend/backend/src/services/task-pool/task-pool.service.d.ts +33 -2
- package/dist/backend/backend/src/services/task-pool/task-pool.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/task-pool/task-pool.service.js +160 -8
- package/dist/backend/backend/src/services/task-pool/task-pool.service.js.map +1 -1
- package/dist/backend/backend/src/services/v3/cascade-request-status.d.ts.map +1 -1
- package/dist/backend/backend/src/services/v3/cascade-request-status.js +55 -2
- package/dist/backend/backend/src/services/v3/cascade-request-status.js.map +1 -1
- package/dist/backend/backend/src/services/v3/mission-executor.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/v3/mission-executor.service.js +9 -1
- package/dist/backend/backend/src/services/v3/mission-executor.service.js.map +1 -1
- package/dist/backend/backend/src/services/v3/request-decompose.subscriber.d.ts.map +1 -1
- package/dist/backend/backend/src/services/v3/request-decompose.subscriber.js +28 -3
- package/dist/backend/backend/src/services/v3/request-decompose.subscriber.js.map +1 -1
- package/dist/backend/backend/src/services/v3/request-sla.subscriber.d.ts.map +1 -1
- package/dist/backend/backend/src/services/v3/request-sla.subscriber.js +5 -2
- package/dist/backend/backend/src/services/v3/request-sla.subscriber.js.map +1 -1
- package/dist/backend/backend/src/services/v3/request-status-update.subscriber.d.ts.map +1 -1
- package/dist/backend/backend/src/services/v3/request-status-update.subscriber.js +57 -15
- package/dist/backend/backend/src/services/v3/request-status-update.subscriber.js.map +1 -1
- package/dist/backend/backend/src/services/v3/trigger-engine.service.d.ts +39 -0
- package/dist/backend/backend/src/services/v3/trigger-engine.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/v3/trigger-engine.service.js +81 -0
- package/dist/backend/backend/src/services/v3/trigger-engine.service.js.map +1 -1
- package/dist/backend/backend/src/services/v3/workitem-dispatch.subscriber.d.ts +17 -1
- package/dist/backend/backend/src/services/v3/workitem-dispatch.subscriber.d.ts.map +1 -1
- package/dist/backend/backend/src/services/v3/workitem-dispatch.subscriber.js +22 -3
- package/dist/backend/backend/src/services/v3/workitem-dispatch.subscriber.js.map +1 -1
- package/dist/backend/backend/src/services/whatsapp/whatsapp-orchestrator-bridge.d.ts +1 -1
- package/dist/backend/backend/src/services/whatsapp/whatsapp-orchestrator-bridge.d.ts.map +1 -1
- package/dist/backend/backend/src/services/whatsapp/whatsapp-orchestrator-bridge.js +26 -10
- package/dist/backend/backend/src/services/whatsapp/whatsapp-orchestrator-bridge.js.map +1 -1
- package/dist/backend/backend/src/services/workflow/cron-task.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/workflow/cron-task.service.js +68 -5
- package/dist/backend/backend/src/services/workflow/cron-task.service.js.map +1 -1
- package/dist/backend/backend/src/services/workflow/team-identifier-resolver.d.ts +44 -0
- package/dist/backend/backend/src/services/workflow/team-identifier-resolver.d.ts.map +1 -0
- package/dist/backend/backend/src/services/workflow/team-identifier-resolver.js +57 -0
- package/dist/backend/backend/src/services/workflow/team-identifier-resolver.js.map +1 -0
- package/dist/backend/backend/src/types/credential.types.d.ts +17 -1
- package/dist/backend/backend/src/types/credential.types.d.ts.map +1 -1
- package/dist/backend/backend/src/types/credential.types.js +15 -5
- package/dist/backend/backend/src/types/credential.types.js.map +1 -1
- package/dist/backend/backend/src/types/cron-task.types.d.ts +17 -0
- package/dist/backend/backend/src/types/cron-task.types.d.ts.map +1 -1
- package/dist/backend/backend/src/types/event-bus.types.d.ts +1 -1
- package/dist/backend/backend/src/types/event-bus.types.d.ts.map +1 -1
- package/dist/backend/backend/src/types/event-bus.types.js +12 -0
- package/dist/backend/backend/src/types/event-bus.types.js.map +1 -1
- package/dist/backend/backend/src/types/intent-task.types.d.ts +10 -13
- package/dist/backend/backend/src/types/intent-task.types.d.ts.map +1 -1
- package/dist/backend/backend/src/types/intent-task.types.js +4 -1
- package/dist/backend/backend/src/types/intent-task.types.js.map +1 -1
- package/dist/backend/backend/src/types/v2/work-item.types.d.ts +23 -0
- package/dist/backend/backend/src/types/v2/work-item.types.d.ts.map +1 -1
- package/dist/backend/backend/src/types/v2/work-item.types.js.map +1 -1
- package/dist/backend/backend/src/utils/team.utils.d.ts +3 -1
- package/dist/backend/backend/src/utils/team.utils.d.ts.map +1 -1
- package/dist/backend/backend/src/utils/team.utils.js +26 -5
- package/dist/backend/backend/src/utils/team.utils.js.map +1 -1
- package/dist/backend/backend/src/websocket/chat-v2.gateway.d.ts +23 -0
- package/dist/backend/backend/src/websocket/chat-v2.gateway.d.ts.map +1 -1
- package/dist/backend/backend/src/websocket/chat-v2.gateway.js +56 -7
- package/dist/backend/backend/src/websocket/chat-v2.gateway.js.map +1 -1
- package/dist/backend/backend/src/websocket/chat.gateway.d.ts +19 -4
- package/dist/backend/backend/src/websocket/chat.gateway.d.ts.map +1 -1
- package/dist/backend/backend/src/websocket/chat.gateway.js +78 -63
- package/dist/backend/backend/src/websocket/chat.gateway.js.map +1 -1
- package/dist/backend/backend/src/websocket/terminal.gateway.d.ts.map +1 -1
- package/dist/backend/backend/src/websocket/terminal.gateway.js +10 -2
- package/dist/backend/backend/src/websocket/terminal.gateway.js.map +1 -1
- package/dist/cli/backend/src/constants.d.ts +12 -0
- package/dist/cli/backend/src/constants.d.ts.map +1 -1
- package/dist/cli/backend/src/constants.js +12 -0
- package/dist/cli/backend/src/constants.js.map +1 -1
- package/dist/cli/backend/src/services/event-bus/event-bus.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/event-bus/event-bus.service.js +22 -11
- package/dist/cli/backend/src/services/event-bus/event-bus.service.js.map +1 -1
- package/dist/cli/backend/src/services/memory/memory.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/memory/memory.service.js +35 -3
- package/dist/cli/backend/src/services/memory/memory.service.js.map +1 -1
- package/dist/cli/backend/src/services/skill/skill-executor.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/skill/skill-executor.service.js +13 -1
- package/dist/cli/backend/src/services/skill/skill-executor.service.js.map +1 -1
- package/dist/cli/backend/src/services/task-pool/task-pool.service.d.ts +33 -2
- package/dist/cli/backend/src/services/task-pool/task-pool.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/task-pool/task-pool.service.js +160 -8
- package/dist/cli/backend/src/services/task-pool/task-pool.service.js.map +1 -1
- package/dist/cli/backend/src/types/credential.types.d.ts +17 -1
- package/dist/cli/backend/src/types/credential.types.d.ts.map +1 -1
- package/dist/cli/backend/src/types/credential.types.js +15 -5
- package/dist/cli/backend/src/types/credential.types.js.map +1 -1
- package/dist/cli/backend/src/types/event-bus.types.d.ts +1 -1
- package/dist/cli/backend/src/types/event-bus.types.d.ts.map +1 -1
- package/dist/cli/backend/src/types/event-bus.types.js +12 -0
- package/dist/cli/backend/src/types/event-bus.types.js.map +1 -1
- package/dist/cli/backend/src/types/v2/work-item.types.d.ts +23 -0
- package/dist/cli/backend/src/types/v2/work-item.types.d.ts.map +1 -1
- package/dist/cli/backend/src/types/v2/work-item.types.js.map +1 -1
- package/dist/cli/cli/src/commands/start.js +73 -12
- package/dist/cli/cli/src/commands/start.js.map +1 -1
- package/frontend/dist/assets/index-b279da34.js +4926 -0
- package/frontend/dist/assets/{index-b7e59b2b.css → index-c07e04c0.css} +2 -2
- package/frontend/dist/index.html +2 -2
- package/package.json +1 -1
- package/frontend/dist/assets/index-698305f3.js +0 -5228
|
@@ -43,6 +43,30 @@ Continuously monitor all active agents, detect problems early, and produce struc
|
|
|
43
43
|
- Use `heartbeat` to check overall system status
|
|
44
44
|
- Detect: API failures, queue backlogs, scheduling issues
|
|
45
45
|
|
|
46
|
+
### 6. Silent Shipping Detection (#437)
|
|
47
|
+
|
|
48
|
+
The auditor's blind spot before issue #432: an agent ships a real
|
|
49
|
+
artifact (PR merged, spec finalized) but never emits a `[MILESTONE]`
|
|
50
|
+
surface upstream. The owner finds out via `git log` 12 hours later —
|
|
51
|
+
exact shape of the 2026-05-04 incident that opened EPIC #426.
|
|
52
|
+
|
|
53
|
+
**How to monitor:**
|
|
54
|
+
|
|
55
|
+
- Cross-reference `git log` (commits in the last 24h) and merged PRs
|
|
56
|
+
against `report-status` / `[MILESTONE]` events from the same window.
|
|
57
|
+
- Flag any agent who authored a merged PR but did NOT emit a
|
|
58
|
+
`[MILESTONE]` surface within 30 minutes of merge.
|
|
59
|
+
- Evidence to capture: PR URL, author session, merge timestamp, the
|
|
60
|
+
most recent `report-status` event for that author (so the gap is
|
|
61
|
+
visible — "last surface at 14:02; PR merged at 17:18; 3h16m gap").
|
|
62
|
+
|
|
63
|
+
**Severity:** **medium** by default — silent shipping indicates a
|
|
64
|
+
system gap, not an agent bug. If the same author silently ships 3+
|
|
65
|
+
times across an audit cycle, escalate to **high**: the structural
|
|
66
|
+
fix (the Mid-Flight Milestone Surface SOP at
|
|
67
|
+
`config/sops/common/mid-flight-milestone-surface.md`) is not landing
|
|
68
|
+
in practice and a prompt/role adjustment is overdue.
|
|
69
|
+
|
|
46
70
|
## How You Report
|
|
47
71
|
|
|
48
72
|
When you find a problem, use the `write_audit_report` tool to append it to the audit log.
|
|
@@ -160,7 +160,8 @@ When I send you a task:
|
|
|
160
160
|
2. **Codebase audit** — Before implementing any feature, search the codebase for existing implementations that overlap with the task. Use `grep`, `find`, and read relevant service files. If the feature (or parts of it) already exists, report back what's already there and propose incremental improvements instead of building from scratch.
|
|
161
161
|
3. Write clean, tested code following project conventions
|
|
162
162
|
4. Report blockers and issues promptly
|
|
163
|
-
5.
|
|
163
|
+
5. **Surface milestones the moment they ship** — see "Mid-Flight Milestone Surface" SOP at `config/sops/common/mid-flight-milestone-surface.md`. When a PR merges / a spec is finalized / a build passes on a non-trivial change, immediately call `report-status` with `status: "milestone"` and a plain-language summary. Do NOT wait for the next outcome-check tick. Issue #427 / EPIC #426 — the system was structurally silent on this path until now.
|
|
164
|
+
6. Let me know when fully done.
|
|
164
165
|
|
|
165
166
|
**CRITICAL**: Never assume a feature doesn't exist. Always verify by reading the codebase first. Building duplicate code wastes time and creates maintenance burden.
|
|
166
167
|
|
|
@@ -55,6 +55,18 @@ The owner hired you to deliver results, not to narrate progress or ask permissio
|
|
|
55
55
|
|
|
56
56
|
**Onboarding exception:** For the first 1-2 interactions with a brand-new owner who hasn't seen how you work, a single onboarding message explaining "I'll run silently unless a deliverable is ready or I'm blocked" is fine. After that, don't repeat.
|
|
57
57
|
|
|
58
|
+
**Wall-clock time is NOT a sleep signal (anti-pattern, observed 2026-04 dogfood — #337):**
|
|
59
|
+
|
|
60
|
+
Silent Mode is about *not bothering the owner unnecessarily*. It does NOT mean "go quiet because it's late." The model runs 24/7; agents run 24/7; the owner sets the cadence, not the wall clock. Specifically:
|
|
61
|
+
|
|
62
|
+
- DO NOT add to any dispatch / status message: "wind down for the night", "stand back down", "GN", "sleep mode", "go to bed", "until tomorrow AM", "until 08:00 ET wake", "今晚该睡了", or any variant — unless the owner has *explicitly* said something equivalent to "I'm done for today / pick this up tomorrow / 我去睡了".
|
|
63
|
+
- DO NOT defer real work items to AM by default just because it's 22:00 / 01:00 / etc. Surface the work and execute. If the owner wants to defer, they will say so.
|
|
64
|
+
- DO NOT close out, suspend, or "park" agent sessions based on the local time of day. Agents idle out via their own runtime mechanisms; that's not your call.
|
|
65
|
+
- DO match the owner's observed energy. If owner Slack messages are arriving at 01:00, the team is also working at 01:00. If you're unsure whether they want to defer, **ask** — "Want me to push this now or queue for tomorrow?" — rather than assume.
|
|
66
|
+
- The only valid sleep-mode signal is the owner saying it directly, OR genuine Slack silence ≥ 60 min AFTER an explicit goodnight. Not a clock check.
|
|
67
|
+
|
|
68
|
+
This anti-pattern came from over-stretching the legitimate "respect owner time" guidance into "the owner is probably asleep, so the team should also wind down." Wall-clock projection is not "respect"; it's an unverified assumption that costs the owner an override turn every time. Stop it at source.
|
|
69
|
+
|
|
58
70
|
## Periodic Progress Check-In (User Requests)
|
|
59
71
|
|
|
60
72
|
Silent Mode is the default **outside** a user request. **Inside** a user request that is going to take more than ~10 minutes to deliver, you MUST keep the owner in the loop with periodic check-ins. Silence during an open request reads as "stuck" or "forgot" — the opposite of the intended behaviour.
|
|
@@ -95,11 +107,14 @@ Silent Mode is the default **outside** a user request. **Inside** a user request
|
|
|
95
107
|
|
|
96
108
|
Every owner-visible message — Slack DMs, Chat UI replies, morning reports, completion summaries, escalations, decision requests — MUST follow this standard. It does NOT apply to internal agent-to-agent messages on team channels.
|
|
97
109
|
|
|
110
|
+
**Mental model — non-negotiable:** Assume the owner is a smart **non-technical user** unless they prove otherwise in *this* conversation. They have not read the Crewly source code. They do not know what a "session" / "WorkItem" / "claim" / "idle_exit" / "queued" is. They DO understand the work in human terms — "the report Atlas wrote," "the bug Leo's debugging." This rule holds even when the owner is technical: a senior engineer on Slack wants a status update, not your scheduler vocabulary. Use system terms only when *they* introduce one first, and only for the subset they mentioned.
|
|
111
|
+
|
|
98
112
|
**Three principles (non-negotiable):**
|
|
99
113
|
|
|
100
|
-
1. **Plain language.** Strip internal vocabulary. No raw IDs, session names, skill / tool names, runtime types (`claude-code`, `gemini-cli`), credential handles, API paths, version tags, state-machine vocabulary (`queued / running / blocked`), or UTC-Z timestamps without local context. Use first names for teammates ("Ella", not `crewly-marketing-ella-member-1`). If an internal term is truly unavoidable, gloss it once on first use; switch to plain phrasing on reuse.
|
|
101
|
-
2. **Sufficient context.** Every update answers, in this order: **what** changed or got decided, **why** in one sentence, **what it means for the owner** (FYI vs needs-attention). If the owner has to ask "and so?" after reading, you under-packaged.
|
|
114
|
+
1. **Plain language.** Strip internal vocabulary. No raw IDs, session names, skill / tool names, runtime types (`claude-code`, `gemini-cli`), credential handles, API paths, version tags, state-machine vocabulary (`queued / running / blocked / cancelled / done_by_worker`), runtime-internal events (`idle_exit / agent suspended / claim revoked / lease expired / heartbeat`), or UTC-Z timestamps without local context. Use first names for teammates ("Ella", not `crewly-marketing-ella-member-1`). When a system event surfaces (an agent exited, a task got cancelled, a claim expired), describe what the **owner would have noticed**, not what the system did internally — e.g. "Owen had been idle for a long stretch, so the system put him to sleep to save resources; I've now restarted him," NOT "Owen idle_exit'd." If an internal term is truly unavoidable, gloss it once on first use; switch to plain phrasing on reuse. See the SOP's jargon-translation table for the full Crewly-specific list.
|
|
115
|
+
2. **Sufficient context.** Every update answers, in this order: **what** changed or got decided, **why** in one sentence, **what it means for the owner** (FYI vs needs-attention). If the owner has to ask "and so?" after reading, you under-packaged. When the owner asks about something surprising or unfamiliar ("怎么会这样?"), assume they need the *backstory* not just the answer — what state was the system in, why did this happen, what's the impact, what (if anything) needs them.
|
|
102
116
|
3. **Decide-first defaults.** When asking the owner to decide, never punt with "you decide" / "你定". Always recommend, then list options. The owner hired you to pre-decide; they can still override.
|
|
117
|
+
4. **Do NOT preview-then-formal.** When you are delegating the substantive answer to a teammate (Atlas writes the memo, Kai pulls the data, Sam audits the PR), do not ALSO draft your own "preview" answer in parallel. From the owner's chat view, "orc's preview" + "teammate's formal version" looks like the same question answered twice — your preview is noise on top of the canonical reply. Pick one shape: a pure acknowledgement ("good question — Atlas is on it, ~20 min") OR you own the full answer yourself. Never both. Steve 2026-05-15 dogfood: orc sent a "预扫的 6 个破局触发条件 (Atlas 待精修)" reply, then Atlas's formal §8 addendum landed minutes later — Steve read it as duplicate content. Late-but-canonical beats early-and-redundant.
|
|
103
118
|
|
|
104
119
|
**Decision-request shape (mandatory when asking the owner to decide):**
|
|
105
120
|
|
|
@@ -504,6 +519,55 @@ This is a hard pre-yield check. Do not yield if any Slack message is unanswered.
|
|
|
504
519
|
- Escalate cross-team blockers
|
|
505
520
|
- Your role boundaries are defined in the Role Boundary section. When unsure whether to do something yourself vs delegate, consult those boundaries.
|
|
506
521
|
|
|
522
|
+
### Browser Access — Prefer Crewly in Chrome
|
|
523
|
+
|
|
524
|
+
When a task needs browser access (web browsing, scraping, controlling a live web app, reading a logged-in page), **prefer the user's own Chrome via the `Crewly in Chrome` extension** over headless Playwright or a remote browser.
|
|
525
|
+
|
|
526
|
+
- **Why prefer:** reuses the user's existing logged-in sessions (Gmail, Slack, Notion, GitHub, etc.), runs locally so the user can watch / take over at any time, and avoids the OAuth-consent dead-ends headless Chromium hits.
|
|
527
|
+
- **If the user does NOT have it installed:** point them to the Chrome Web Store listing — `https://chromewebstore.google.com/detail/crewly-in-chrome/mekcefkcdgefjhadkcbkdpmilcaekhnc` — and tell them that after installing they need to **log into Crewly Cloud from the OSS Settings page** so the extension can pair with their local OSS. Once paired, browser tabs become available to worker agents automatically.
|
|
528
|
+
- **Fallback** when the user cannot or does not want to install: delegate to a worker that has the `remote-browser` skill or Playwright access, and surface the trade-off (no logged-in sessions, OAuth friction) in your reply.
|
|
529
|
+
|
|
530
|
+
#### Debugging "extension shows Connected but agents can't reach it"
|
|
531
|
+
|
|
532
|
+
**Authoritative status comes from OSS, not the Extension popup.** The Extension's own "Connected · NNms" indicator only means the Extension is talking to the Cloud Relay — it says NOTHING about whether your OSS backend is paired with that relay session. They are two independent legs of the same triangle:
|
|
533
|
+
|
|
534
|
+
```
|
|
535
|
+
Extension ──(leg A)── Cloud Relay ──(leg B)── OSS backend
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
Both legs must be up for agents to drive tabs. The Extension popup proves leg A only. When in doubt, **always ask the OSS backend first** instead of telling the user to reload the Extension.
|
|
539
|
+
|
|
540
|
+
**Step 1 — read the canonical state from OSS:**
|
|
541
|
+
|
|
542
|
+
```bash
|
|
543
|
+
curl -s http://localhost:8787/api/browser/status
|
|
544
|
+
# expected when healthy:
|
|
545
|
+
# {"connected":true,"clientCount":...,"relayAvailable":true,"relayDeviceId":"...","proxy":{"state":"connected",...}}
|
|
546
|
+
```
|
|
547
|
+
|
|
548
|
+
Decision tree on the response:
|
|
549
|
+
- `connected:true, relayAvailable:true` → it IS working. If a worker still can't drive tabs, the bug is in the worker's skill wiring, not the bridge — escalate to the agent that's failing.
|
|
550
|
+
- `relayAvailable:false` AND `clientCount:0` → leg B is down. **Do NOT** tell the user to reload the Extension. Go to step 2.
|
|
551
|
+
- `relayAvailable:false` AND `clientCount:>0` → Extension is on a direct WS to this machine (no Cloud relay needed); proceed normally.
|
|
552
|
+
|
|
553
|
+
**Step 2 — confirm leg B is down because of OSS-side Cloud auth (the common case):**
|
|
554
|
+
|
|
555
|
+
```bash
|
|
556
|
+
grep -iE "CloudSyncService|locally-signed|authentication permanently failed" \
|
|
557
|
+
~/.crewly/logs/crewly-$(date -u +%Y-%m-%d).log | tail -10
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
If you see lines like `CloudSyncService token refresh failed ... status: 403`, `Cloud token refresh failed — locally-signed token may not work with Cloud relay`, or (the smoking gun) `Cloud Sync authentication permanently failed — user must re-login | attempts: 5`, then OSS's Cloud refresh token was minted locally (dev/test path) and the Cloud Relay is rejecting it with 403. Leg B is dead until the user re-logs in.
|
|
561
|
+
|
|
562
|
+
**Step 3 — fix:**
|
|
563
|
+
|
|
564
|
+
Tell the user to run **`crewly cloud login`** in their OSS terminal (NOT in the Extension; the Extension is fine). It exchanges the locally-signed token for a Cloud-signed refresh token. After it completes:
|
|
565
|
+
- `CloudSyncService` flips from `error` → `syncing`
|
|
566
|
+
- `BrowserRelayAdapter.isAvailable()` becomes true
|
|
567
|
+
- A re-`curl` of `/api/browser/status` returns `connected:true`
|
|
568
|
+
|
|
569
|
+
**Never tell the user to reload the Extension, click Reconnect, or re-install Crewly in Chrome unless `/api/browser/status` shows `clientCount:0` AND CloudSync is healthy** — those are leg-A fixes for a leg-B symptom.
|
|
570
|
+
|
|
507
571
|
### Credential Requests — Route by Channel (MANDATORY)
|
|
508
572
|
|
|
509
573
|
#### Trigger Phrases — Auto-Route to Credential-Manager (do NOT require user to say "credential manager")
|
|
@@ -663,8 +727,16 @@ Not every event deserves a user notification. Use this priority system to decide
|
|
|
663
727
|
|----------|---------------|----------|
|
|
664
728
|
| 🔴 **Critical** — Notify IMMEDIATELY | Agent crash, task failure, blocked, error | Runtime exited, build failed, agent stuck >15min |
|
|
665
729
|
| 🟡 **Important** — Notify within 1 min | Task completed, needs user decision, milestone reached | Agent finished feature, needs review approval |
|
|
730
|
+
| 🟡 **`[MILESTONE]` envelope** (#436) — **ALWAYS notify, never downgrade to ⚪ Info** | Agent emitted an explicit `[MILESTONE]` via `report-status` `status: "milestone"` — see `config/sops/common/mid-flight-milestone-surface.md` | "PR #420 merged — agent state file is now corruption-resistant", "Spec finalized + handed off to Atlas" |
|
|
666
731
|
| ⚪ **Info** — Log only, include in next summary | Agent started working, routine status change, heartbeat | idle→in_progress, scheduled check with no changes |
|
|
667
732
|
|
|
733
|
+
**Rule at ALL trust levels (never skip)**: when an agent surfaces a
|
|
734
|
+
`[MILESTONE]` envelope, you forward it to the owner. The agent
|
|
735
|
+
already self-filtered using the Mid-Flight Milestone Surface SOP; do
|
|
736
|
+
not re-litigate that decision by downgrading to ⚪ Info even if the
|
|
737
|
+
outer outcome / OKR isn't fully met yet. Issue #427 / EPIC #426
|
|
738
|
+
documented the system gap that this rule closes.
|
|
739
|
+
|
|
668
740
|
### Decision Rules for Events
|
|
669
741
|
|
|
670
742
|
When you receive an `[EVENT:...]` notification:
|
|
@@ -203,6 +203,12 @@ Your verification approach adjusts based on the team's `templateId`:
|
|
|
203
203
|
- All reports to the Orchestrator **must** include the `[TL_REPORT]` tag
|
|
204
204
|
- All tasks delegated to workers **must** include explicit `acceptanceCriteria`
|
|
205
205
|
- Status updates use `report-status` skill with clear summaries
|
|
206
|
+
- **Mid-flight milestones must surface immediately** — when a worker
|
|
207
|
+
ships a PR / finalizes a spec / passes a non-trivial build, you
|
|
208
|
+
forward (or your worker emits directly) a `[MILESTONE]` envelope
|
|
209
|
+
via `report-status` with `status: "milestone"`. See
|
|
210
|
+
`config/sops/common/mid-flight-milestone-surface.md`. Do NOT wait
|
|
211
|
+
for the next outcome-check tick. Issue #427 / EPIC #426.
|
|
206
212
|
|
|
207
213
|
---
|
|
208
214
|
|
|
@@ -91,7 +91,7 @@ Use these to classify the complexity of the request:
|
|
|
91
91
|
| `--intent-level` | Yes | -- | L0, L1, L2, or L3 |
|
|
92
92
|
| `--intent-category` | Yes | -- | communication, query, code_change, planning, debugging, deployment, team_management, other |
|
|
93
93
|
| `--priority` | No | normal | low, normal, high, urgent |
|
|
94
|
-
| `--source-message-id` | No |
|
|
94
|
+
| `--source-message-id` | No | `agent-self-{session}-{epoch}` | Original message ID (for dedup). When omitted, the skill synthesizes `agent-self-{CREWLY_SESSION_NAME}-{epoch}` so agent-context callers (e.g. TL self-filing a child Request) don't 400 on the backend's required-field check. Issue #458. |
|
|
95
95
|
| `--needs-mission` | No | false | Whether this L3 request should trigger Mission creation |
|
|
96
96
|
|
|
97
97
|
## Examples -- CLI Flags (preferred)
|
|
@@ -155,9 +155,36 @@ if [ -n "$PRIORITY" ]; then
|
|
|
155
155
|
BODY=$(printf '%s' "$BODY" | jq --arg p "$PRIORITY" '. + {priority: $p}')
|
|
156
156
|
fi
|
|
157
157
|
|
|
158
|
-
|
|
159
|
-
|
|
158
|
+
# Issue #458: the backend validator at v2/request.types.ts:283 requires
|
|
159
|
+
# `sourceConversationItemId` to be a non-empty string. The orc passes the
|
|
160
|
+
# inbound Slack/chat message id when filing a Request, but agent-context
|
|
161
|
+
# callers (e.g. a TL filing a child Request from a delegated task) have
|
|
162
|
+
# no inbound message of their own. Auto-synthesize a stable
|
|
163
|
+
# `agent-self-{session}-{epoch}-{nonce}` id so the request lands instead
|
|
164
|
+
# of 400-ing.
|
|
165
|
+
#
|
|
166
|
+
# Notes:
|
|
167
|
+
# - The synthetic shape is intentionally distinct from the `slack-{ch}-
|
|
168
|
+
# {ts}` / `chatv2-{ch}__{msg}` prefixes so downstream consumers
|
|
169
|
+
# (extractSlackThreadTs, extractChatV2ChannelId, SLA tracker, .md
|
|
170
|
+
# store) inert-skip it.
|
|
171
|
+
# - The trailing `${RANDOM}` nonce prevents per-second collisions when
|
|
172
|
+
# two rapid create-request calls from the same session land in the
|
|
173
|
+
# same wall-clock second. `findBySourceConversationItemId` (the dedup
|
|
174
|
+
# path) keys on this string verbatim.
|
|
175
|
+
# - When `CREWLY_SESSION_NAME` is unset (likely a misconfiguration, but
|
|
176
|
+
# not load-bearing), we warn to stderr and continue with
|
|
177
|
+
# `unknown-session` so the request still lands instead of failing
|
|
178
|
+
# silently. The warn surfaces the missing env in observability.
|
|
179
|
+
if [ -z "$SOURCE_MESSAGE_ID" ]; then
|
|
180
|
+
AGENT_SESSION="${CREWLY_SESSION_NAME:-}"
|
|
181
|
+
if [ -z "$AGENT_SESSION" ]; then
|
|
182
|
+
echo "warn: CREWLY_SESSION_NAME unset; falling back to 'unknown-session' for synthesized sourceConversationItemId" >&2
|
|
183
|
+
AGENT_SESSION="unknown-session"
|
|
184
|
+
fi
|
|
185
|
+
SOURCE_MESSAGE_ID="agent-self-${AGENT_SESSION}-$(date +%s)-${RANDOM}"
|
|
160
186
|
fi
|
|
187
|
+
BODY=$(printf '%s' "$BODY" | jq --arg sid "$SOURCE_MESSAGE_ID" '. + {sourceConversationItemId: $sid}')
|
|
161
188
|
|
|
162
189
|
# Call the API to create the Request
|
|
163
190
|
RESULT=$(api_call POST "/requests" "$BODY")
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Co-located bash test for create-request skill.
|
|
3
|
+
# Spins up a tiny python HTTP stub to capture the outgoing body, then
|
|
4
|
+
# asserts that sourceConversationItemId is always present (synthesized
|
|
5
|
+
# when missing) — Issue #458 regression gate.
|
|
6
|
+
#
|
|
7
|
+
# Usage: bash execute.test.sh (exit 0 on pass)
|
|
8
|
+
set -euo pipefail
|
|
9
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
10
|
+
SKILL="${SCRIPT_DIR}/execute.sh"
|
|
11
|
+
|
|
12
|
+
if [ ! -x "$SKILL" ]; then
|
|
13
|
+
echo "FAIL: skill not executable at $SKILL" >&2
|
|
14
|
+
exit 1
|
|
15
|
+
fi
|
|
16
|
+
|
|
17
|
+
PASS=0
|
|
18
|
+
FAIL=0
|
|
19
|
+
PORT=18789
|
|
20
|
+
|
|
21
|
+
# --- Start stub backend on a fixed test port ---
|
|
22
|
+
STUB_LOG="$(mktemp)"
|
|
23
|
+
export STUB_LOG
|
|
24
|
+
export STUB_PORT="${PORT}"
|
|
25
|
+
|
|
26
|
+
python3 -u <<'PY' >/tmp/create-request-stub.log 2>&1 &
|
|
27
|
+
from http.server import BaseHTTPRequestHandler, HTTPServer
|
|
28
|
+
import json, os, sys
|
|
29
|
+
|
|
30
|
+
LOG_PATH = os.environ['STUB_LOG']
|
|
31
|
+
PORT = int(os.environ['STUB_PORT'])
|
|
32
|
+
|
|
33
|
+
class H(BaseHTTPRequestHandler):
|
|
34
|
+
def do_POST(self):
|
|
35
|
+
length = int(self.headers.get('content-length', '0'))
|
|
36
|
+
body = self.rfile.read(length).decode('utf-8') if length else ''
|
|
37
|
+
log = {
|
|
38
|
+
'path': self.path,
|
|
39
|
+
'body': body,
|
|
40
|
+
'agentSession': self.headers.get('x-agent-session', ''),
|
|
41
|
+
}
|
|
42
|
+
with open(LOG_PATH, 'w') as f:
|
|
43
|
+
f.write(json.dumps(log))
|
|
44
|
+
self.send_response(201)
|
|
45
|
+
self.send_header('Content-Type', 'application/json')
|
|
46
|
+
self.end_headers()
|
|
47
|
+
self.wfile.write(json.dumps({'success': True, 'data': {'id': 'req-stub-123'}}).encode('utf-8'))
|
|
48
|
+
def log_message(self, *a, **k):
|
|
49
|
+
pass
|
|
50
|
+
|
|
51
|
+
srv = HTTPServer(('127.0.0.1', PORT), H)
|
|
52
|
+
srv.serve_forever()
|
|
53
|
+
PY
|
|
54
|
+
STUB_PID=$!
|
|
55
|
+
|
|
56
|
+
cleanup() { kill "$STUB_PID" >/dev/null 2>&1 || true; rm -f "$STUB_LOG" || true; }
|
|
57
|
+
trap cleanup EXIT
|
|
58
|
+
|
|
59
|
+
# Wait until the port actually accepts a connection
|
|
60
|
+
for i in $(seq 1 30); do
|
|
61
|
+
if curl -s -o /dev/null -m 0.2 "http://127.0.0.1:${PORT}/__probe__" 2>/dev/null; then
|
|
62
|
+
break
|
|
63
|
+
fi
|
|
64
|
+
sleep 0.1
|
|
65
|
+
done
|
|
66
|
+
|
|
67
|
+
assert_contains() {
|
|
68
|
+
local name="$1" haystack="$2" needle="$3"
|
|
69
|
+
if echo "$haystack" | grep -q -- "$needle"; then
|
|
70
|
+
PASS=$((PASS + 1))
|
|
71
|
+
echo " ✓ $name"
|
|
72
|
+
else
|
|
73
|
+
FAIL=$((FAIL + 1))
|
|
74
|
+
echo " ✗ $name (wanted: $needle)"
|
|
75
|
+
echo " in: $haystack"
|
|
76
|
+
fi
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
run_skill() {
|
|
80
|
+
CREWLY_API_URL="http://127.0.0.1:${PORT}" \
|
|
81
|
+
CREWLY_SESSION_NAME="crewly-product-sam-TEST" \
|
|
82
|
+
bash "$SKILL" "$@"
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
# --- Test 1: Issue #458 — TL agent flow without --source-message-id ---
|
|
86
|
+
echo "test 1: agent-self synthesis when --source-message-id is omitted"
|
|
87
|
+
: > "$STUB_LOG"
|
|
88
|
+
OUT=$(run_skill \
|
|
89
|
+
--title "Fix dogfood pipeline P0" \
|
|
90
|
+
--description "Fix the pipeline regression that blocked decomposition" \
|
|
91
|
+
--intent-level L1 \
|
|
92
|
+
--intent-category debugging \
|
|
93
|
+
2>&1 || true)
|
|
94
|
+
LOG=$(cat "$STUB_LOG" 2>/dev/null || echo '{}')
|
|
95
|
+
assert_contains "success echoed" "$OUT" '"success": true'
|
|
96
|
+
assert_contains "sourceConversationItemId synthesized" "$LOG" 'sourceConversationItemId'
|
|
97
|
+
assert_contains "synthesized id uses agent-self prefix" "$LOG" 'agent-self-crewly-product-sam-TEST-'
|
|
98
|
+
assert_contains "path targets /requests" "$LOG" '/requests'
|
|
99
|
+
|
|
100
|
+
# --- Test 2: --source-message-id provided is preserved ---
|
|
101
|
+
echo "test 2: explicit --source-message-id passes through unchanged"
|
|
102
|
+
: > "$STUB_LOG"
|
|
103
|
+
OUT=$(run_skill \
|
|
104
|
+
--title "Inbound Slack request" \
|
|
105
|
+
--description "Some inbound user message routing through orc" \
|
|
106
|
+
--intent-level L2 \
|
|
107
|
+
--intent-category code_change \
|
|
108
|
+
--source-message-id "slack-D0AC7-1234567890.000001" \
|
|
109
|
+
2>&1 || true)
|
|
110
|
+
LOG=$(cat "$STUB_LOG" 2>/dev/null || echo '{}')
|
|
111
|
+
assert_contains "explicit sourceConversationItemId preserved" "$LOG" 'slack-D0AC7-1234567890.000001'
|
|
112
|
+
if echo "$LOG" | grep -q 'agent-self-'; then
|
|
113
|
+
FAIL=$((FAIL + 1))
|
|
114
|
+
echo " ✗ agent-self prefix wrongly added when explicit id present"
|
|
115
|
+
else
|
|
116
|
+
PASS=$((PASS + 1))
|
|
117
|
+
echo " ✓ explicit id is not overwritten by agent-self synthesis"
|
|
118
|
+
fi
|
|
119
|
+
|
|
120
|
+
# --- Test 3: legacy JSON form without sourceMessageId still gets synthesis ---
|
|
121
|
+
echo "test 3: legacy JSON form falls back to agent-self synthesis"
|
|
122
|
+
: > "$STUB_LOG"
|
|
123
|
+
OUT=$(run_skill '{"title":"json form","description":"some description","intentLevel":"L1","intentCategory":"debugging"}' 2>&1 || true)
|
|
124
|
+
LOG=$(cat "$STUB_LOG" 2>/dev/null || echo '{}')
|
|
125
|
+
assert_contains "json-form synthesis works too" "$LOG" 'agent-self-crewly-product-sam-TEST-'
|
|
126
|
+
|
|
127
|
+
# --- Test 4: explicit empty-string --source-message-id triggers synthesis ---
|
|
128
|
+
echo "test 4: empty-string source-message-id treated as missing"
|
|
129
|
+
: > "$STUB_LOG"
|
|
130
|
+
OUT=$(run_skill \
|
|
131
|
+
--title "Empty id test" \
|
|
132
|
+
--description "Caller passed --source-message-id with an empty string" \
|
|
133
|
+
--intent-level L1 \
|
|
134
|
+
--intent-category debugging \
|
|
135
|
+
--source-message-id "" \
|
|
136
|
+
2>&1 || true)
|
|
137
|
+
LOG=$(cat "$STUB_LOG" 2>/dev/null || echo '{}')
|
|
138
|
+
assert_contains "empty-string id triggers synthesis" "$LOG" 'agent-self-crewly-product-sam-TEST-'
|
|
139
|
+
|
|
140
|
+
# --- Test 5: two back-to-back synthesis calls produce distinct ids ---
|
|
141
|
+
# Issue #458 review follow-up: the synthesis nonce ($RANDOM) must prevent
|
|
142
|
+
# per-second collisions so `findBySourceConversationItemId` doesn't false-
|
|
143
|
+
# positive on a second TL self-file in the same wall-clock second.
|
|
144
|
+
echo "test 5: rapid synthesis produces distinct ids (no per-second collision)"
|
|
145
|
+
: > "$STUB_LOG"
|
|
146
|
+
run_skill --title "First" --description "Description one" --intent-level L1 --intent-category debugging >/dev/null 2>&1 || true
|
|
147
|
+
ID_1=$(jq -r '.body | fromjson | .sourceConversationItemId' "$STUB_LOG" 2>/dev/null || echo '')
|
|
148
|
+
: > "$STUB_LOG"
|
|
149
|
+
run_skill --title "Second" --description "Description two" --intent-level L1 --intent-category debugging >/dev/null 2>&1 || true
|
|
150
|
+
ID_2=$(jq -r '.body | fromjson | .sourceConversationItemId' "$STUB_LOG" 2>/dev/null || echo '')
|
|
151
|
+
if [ -n "$ID_1" ] && [ -n "$ID_2" ] && [ "$ID_1" != "$ID_2" ]; then
|
|
152
|
+
PASS=$((PASS + 1))
|
|
153
|
+
echo " ✓ two rapid syntheses produce distinct ids ($ID_1 vs $ID_2)"
|
|
154
|
+
else
|
|
155
|
+
FAIL=$((FAIL + 1))
|
|
156
|
+
echo " ✗ rapid syntheses collided or were empty: '$ID_1' '$ID_2'"
|
|
157
|
+
fi
|
|
158
|
+
|
|
159
|
+
echo
|
|
160
|
+
echo "========================================"
|
|
161
|
+
echo "create-request skill: PASS=$PASS FAIL=$FAIL"
|
|
162
|
+
echo "========================================"
|
|
163
|
+
if [ "$FAIL" -gt 0 ]; then
|
|
164
|
+
echo "--- stub server log ---"
|
|
165
|
+
cat /tmp/create-request-stub.log 2>/dev/null | tail -40
|
|
166
|
+
exit 1
|
|
167
|
+
fi
|
|
168
|
+
exit 0
|
|
@@ -48,7 +48,7 @@ When `status` is `done` and a `taskPath` is provided, the task file is automatic
|
|
|
48
48
|
| Flag | JSON Field | Required | Description |
|
|
49
49
|
|------|-----------|----------|-------------|
|
|
50
50
|
| `--session` / `-s` | `sessionName` | Yes | Your agent session name |
|
|
51
|
-
| `--status` / `-S` | `status` | Yes | Status: `done`, `blocked`, `failed`, `in_progress`, `active` |
|
|
51
|
+
| `--status` / `-S` | `status` | Yes | Status: `done`, `blocked`, `failed`, `in_progress`, `active`, `milestone`. `milestone` = a SHIPPED artifact inside an open goal (PR merged / spec finalized / build pass); emits a `[MILESTONE]` envelope that orc's Smart Notification Protocol always forwards to the owner. See `config/sops/common/mid-flight-milestone-surface.md` (#435). Summary must be ≥30 chars. |
|
|
52
52
|
| `--summary` / `-m` | `summary` | Yes | Brief description (or pipe via stdin) |
|
|
53
53
|
| `--summary-file` | — | No | Read summary from a file path |
|
|
54
54
|
| `--project` / `-p` | `projectPath` | No | Project path for auto-remember on completion |
|
|
@@ -69,6 +69,13 @@ bash execute.sh --session dev-1 --status blocked --summary "Waiting on API crede
|
|
|
69
69
|
# Report failure
|
|
70
70
|
bash execute.sh --session dev-1 --status failed --summary "Build fails due to missing dependency"
|
|
71
71
|
|
|
72
|
+
# Surface a milestone (#435) — a SHIPPED artifact inside an open goal.
|
|
73
|
+
# Emits the [MILESTONE] envelope orc's Smart Notification table always
|
|
74
|
+
# forwards to the owner. Summary must carry both WHAT shipped AND
|
|
75
|
+
# WHAT-IT-MEANS-FOR-OWNER (≥30 chars).
|
|
76
|
+
bash execute.sh --session dev-1 --status milestone \
|
|
77
|
+
--summary "PR #420 merged — agent state file is now corruption-resistant + auto-snapshots every 30s"
|
|
78
|
+
|
|
72
79
|
# Multi-line summary via stdin (avoids shell escaping)
|
|
73
80
|
echo "Fixed the bug — it's working now" | bash execute.sh --session dev-1 --status done --project /path
|
|
74
81
|
|
|
@@ -22,7 +22,12 @@ Usage:
|
|
|
22
22
|
|
|
23
23
|
Options:
|
|
24
24
|
--session | -s Agent session name (required)
|
|
25
|
-
--status | -S Status: done, blocked, failed, in_progress, active (required)
|
|
25
|
+
--status | -S Status: done, blocked, failed, in_progress, active, milestone (required)
|
|
26
|
+
milestone = a SHIPPED artifact inside an open goal (PR merged,
|
|
27
|
+
spec finalized, build pass on a non-trivial change). Emits a
|
|
28
|
+
[MILESTONE] envelope that orc's Smart Notification Protocol
|
|
29
|
+
always forwards to the owner. See:
|
|
30
|
+
config/sops/common/mid-flight-milestone-surface.md (issue #435)
|
|
26
31
|
--summary | -m Status summary text (required unless piped via stdin)
|
|
27
32
|
--summary-file Read summary from file path
|
|
28
33
|
--project | -p Project path for auto-remember on completion
|
|
@@ -151,6 +156,18 @@ require_param "sessionName (--session)" "$SESSION_NAME"
|
|
|
151
156
|
require_param "status (--status)" "$STATUS"
|
|
152
157
|
require_param "summary (--summary)" "$SUMMARY"
|
|
153
158
|
|
|
159
|
+
# Gate: milestone summaries must carry a real "WHAT shipped + WHAT it means
|
|
160
|
+
# for the owner" — see config/sops/common/mid-flight-milestone-surface.md
|
|
161
|
+
# (issue #435). Reject anything that looks like "task done" boilerplate
|
|
162
|
+
# (under 30 chars) so the [MILESTONE] envelope doesn't get diluted.
|
|
163
|
+
if [ "$STATUS" = "milestone" ]; then
|
|
164
|
+
SUMMARY_LEN=${#SUMMARY}
|
|
165
|
+
if [ "$SUMMARY_LEN" -lt 30 ]; then
|
|
166
|
+
echo "{\"error\":\"milestone gate failed: summary too short (${SUMMARY_LEN} chars, minimum 30). Per the Mid-Flight Milestone Surface SOP, a milestone surface must carry both WHAT shipped AND WHAT-IT-MEANS-FOR-OWNER.\"}" >&2
|
|
167
|
+
error_exit "Summary must be at least 30 characters when reporting milestone (got ${SUMMARY_LEN})"
|
|
168
|
+
fi
|
|
169
|
+
fi
|
|
170
|
+
|
|
154
171
|
# Gate: before_mark_done — reject empty or trivially short summaries
|
|
155
172
|
# This prevents agents from marking tasks done without meaningful output.
|
|
156
173
|
if [ "$STATUS" = "done" ]; then
|
|
@@ -168,6 +185,11 @@ map_status_to_state() {
|
|
|
168
185
|
blocked) echo "blocked" ;;
|
|
169
186
|
failed) echo "failed" ;;
|
|
170
187
|
in_progress|working) echo "working" ;;
|
|
188
|
+
# Issue #435: `milestone` is a SHIPPED artifact inside an open goal,
|
|
189
|
+
# not the outer goal's completion. It maps to its own state so the
|
|
190
|
+
# downstream consumer (auto-learning subscriber, milestone-notification
|
|
191
|
+
# subscriber #438, orc Smart Notification table row #436) can branch.
|
|
192
|
+
milestone) echo "milestone" ;;
|
|
171
193
|
*) echo "$1" ;;
|
|
172
194
|
esac
|
|
173
195
|
}
|
|
@@ -15,13 +15,55 @@ teams_response=$(api_call GET "/teams" 2>/dev/null) || teams_response='{"error":
|
|
|
15
15
|
projects_response=$(api_call GET "/projects" 2>/dev/null) || projects_response='{"error":"unavailable"}'
|
|
16
16
|
queue_response=$(api_call GET "/messaging/queue/status" 2>/dev/null) || queue_response='{"error":"unavailable"}'
|
|
17
17
|
|
|
18
|
-
#
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
# Parse helpers:
|
|
19
|
+
# - strict=False tolerates embedded control chars (e.g. raw \n in agent system-prompt strings)
|
|
20
|
+
# - All three endpoints wrap payload as {success, data}; drill into .data, falling back to the
|
|
21
|
+
# raw dict for older shapes.
|
|
22
|
+
# - On parse failure / missing field, emit valid JSON (null or 0) — never an unquoted '?'.
|
|
21
23
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
24
|
+
count_data() {
|
|
25
|
+
# Count elements in the .data array (or fall back to the raw response).
|
|
26
|
+
python3 -c "
|
|
27
|
+
import sys, json
|
|
28
|
+
try:
|
|
29
|
+
d = json.loads(sys.stdin.read(), strict=False)
|
|
30
|
+
except Exception:
|
|
31
|
+
print(0); sys.exit(0)
|
|
32
|
+
payload = d.get('data', d) if isinstance(d, dict) else d
|
|
33
|
+
print(len(payload) if isinstance(payload, (list, dict)) else 0)
|
|
34
|
+
" 2>/dev/null || echo 0
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
team_count=$(echo "$teams_response" | count_data)
|
|
38
|
+
project_count=$(echo "$projects_response" | count_data)
|
|
39
|
+
|
|
40
|
+
# Queue: pendingCount (int) and isProcessing (bool, mapped to 0/1).
|
|
41
|
+
queue_pending=$(echo "$queue_response" | python3 -c "
|
|
42
|
+
import sys, json
|
|
43
|
+
try:
|
|
44
|
+
d = json.loads(sys.stdin.read(), strict=False)
|
|
45
|
+
payload = d.get('data', d) if isinstance(d, dict) else d
|
|
46
|
+
v = payload.get('pendingCount', payload.get('pending', 0)) if isinstance(payload, dict) else 0
|
|
47
|
+
print(int(v) if v is not None else 0)
|
|
48
|
+
except Exception:
|
|
49
|
+
print(0)
|
|
50
|
+
" 2>/dev/null || echo 0)
|
|
51
|
+
|
|
52
|
+
queue_processing=$(echo "$queue_response" | python3 -c "
|
|
53
|
+
import sys, json
|
|
54
|
+
try:
|
|
55
|
+
d = json.loads(sys.stdin.read(), strict=False)
|
|
56
|
+
payload = d.get('data', d) if isinstance(d, dict) else d
|
|
57
|
+
if isinstance(payload, dict):
|
|
58
|
+
v = payload.get('processingCount')
|
|
59
|
+
if v is None:
|
|
60
|
+
v = 1 if payload.get('isProcessing') else 0
|
|
61
|
+
print(int(v))
|
|
62
|
+
else:
|
|
63
|
+
print(0)
|
|
64
|
+
except Exception:
|
|
65
|
+
print(0)
|
|
66
|
+
" 2>/dev/null || echo 0)
|
|
25
67
|
|
|
26
68
|
cat <<EOF
|
|
27
69
|
{
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: common-mid-flight-milestone-surface
|
|
3
|
+
version: 1
|
|
4
|
+
createdAt: 2026-05-16T00:00:00Z
|
|
5
|
+
updatedAt: 2026-05-16T00:00:00Z
|
|
6
|
+
createdBy: system
|
|
7
|
+
updatedBy: system
|
|
8
|
+
role: common
|
|
9
|
+
category: communication
|
|
10
|
+
priority: 8
|
|
11
|
+
title: Mid-Flight Milestone Surface
|
|
12
|
+
description: When you ship a milestone inside an active goal, surface it upstream immediately — do not wait for the next outcome-check tick.
|
|
13
|
+
triggers:
|
|
14
|
+
- milestone
|
|
15
|
+
- report-status
|
|
16
|
+
- PR merged
|
|
17
|
+
- spec finalized
|
|
18
|
+
- build pass
|
|
19
|
+
- deploy succeeded
|
|
20
|
+
tags:
|
|
21
|
+
- communication
|
|
22
|
+
- proactive
|
|
23
|
+
- milestone
|
|
24
|
+
- silent-shipping
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
# Mid-Flight Milestone Surface
|
|
28
|
+
|
|
29
|
+
When you ship a milestone INSIDE an active goal — a PR merged, a spec
|
|
30
|
+
finalized, a build pass on a non-trivial change, a dependency
|
|
31
|
+
unblocked, a deploy succeeded — do **NOT** wait for the next
|
|
32
|
+
outcome-check tick or for someone to ask. Surface it now.
|
|
33
|
+
|
|
34
|
+
This SOP is the **symmetric agent-side rule** to the orchestrator's
|
|
35
|
+
periodic check-in cadence (PR #418). The orc surfaces upward to the
|
|
36
|
+
owner on a 15-min cadence; agents surface upward to the orc the
|
|
37
|
+
moment they have something concrete to report. Issue #427 / EPIC
|
|
38
|
+
#426 documented that worker / TL prompts had **zero** "milestone"
|
|
39
|
+
keyword across 21 role prompts — the system was structurally silent
|
|
40
|
+
on this path.
|
|
41
|
+
|
|
42
|
+
## The rule
|
|
43
|
+
|
|
44
|
+
Immediately call the `report-status` skill with:
|
|
45
|
+
|
|
46
|
+
- `status: "milestone"` — the explicit verb introduced by issue #435 /
|
|
47
|
+
PR adding `[MILESTONE]` envelope to `report-status`.
|
|
48
|
+
- `summary` — plain-language **WHAT shipped** + **one-line
|
|
49
|
+
WHAT-IT-MEANS-FOR-OWNER**. Optimize for someone scanning Slack on
|
|
50
|
+
their phone.
|
|
51
|
+
- `artifacts` — the canonical link (PR URL, spec path, deploy id).
|
|
52
|
+
If there's no artifact, the milestone probably isn't one.
|
|
53
|
+
|
|
54
|
+
The orchestrator's Smart Notification Protocol has a dedicated
|
|
55
|
+
`[MILESTONE]` row in the priority table (issue #436) that promotes
|
|
56
|
+
the surface to 🟡 Important — always notify, never downgraded to
|
|
57
|
+
⚪ Info even if the outer outcome isn't fully met yet.
|
|
58
|
+
|
|
59
|
+
## What counts as a milestone
|
|
60
|
+
|
|
61
|
+
- PR merged
|
|
62
|
+
- Spec finalized + handed off
|
|
63
|
+
- Build pass on a non-trivial change (e.g. cross-module refactor
|
|
64
|
+
that compiles for the first time)
|
|
65
|
+
- Dependency unblocked (the thing you were waiting on now exists)
|
|
66
|
+
- Deploy succeeded
|
|
67
|
+
- Verification gate passed (e.g. an integration test you wrote went
|
|
68
|
+
green for the first time)
|
|
69
|
+
|
|
70
|
+
## What does NOT count
|
|
71
|
+
|
|
72
|
+
- "task done" without context (no `summary` body) — every dispatch
|
|
73
|
+
produces a "task done" event, those flow through the existing
|
|
74
|
+
task-pool path and don't need separate `[MILESTONE]` surfacing.
|
|
75
|
+
- Internal progress checkpoints (40% of the way through, 80% of the
|
|
76
|
+
way through). Those are status updates, not milestones.
|
|
77
|
+
- The same milestone re-surfaced. Once is enough.
|
|
78
|
+
|
|
79
|
+
## Examples
|
|
80
|
+
|
|
81
|
+
✅ **Good**:
|
|
82
|
+
|
|
83
|
+
- "PR #420 merged — agent state file is now corruption-resistant +
|
|
84
|
+
auto-snapshots every 30s. Persistence-loss risk on crash drops from
|
|
85
|
+
'all session memory' to 'last 30s only'. No action needed."
|
|
86
|
+
|
|
87
|
+
- "Onboarding cold-start state machine merged (PR #409). End-to-end
|
|
88
|
+
5-min activation path is now in code. Real-user dry-run still
|
|
89
|
+
pending."
|
|
90
|
+
|
|
91
|
+
❌ **Bad — do not emit**:
|
|
92
|
+
|
|
93
|
+
- "task done"
|
|
94
|
+
- "Progress update: 80%"
|
|
95
|
+
- "Working on it"
|
|
96
|
+
- "Will continue tomorrow" (without specifying what shipped)
|
|
97
|
+
|
|
98
|
+
## How orc handles it
|
|
99
|
+
|
|
100
|
+
When the `[MILESTONE]` envelope arrives, the orchestrator:
|
|
101
|
+
|
|
102
|
+
1. Recognizes the explicit `[MILESTONE]` marker from the priority
|
|
103
|
+
table (issue #436) — classified as 🟡 Important.
|
|
104
|
+
2. Surfaces to the owner via the trust-adaptive channel (chat-v2
|
|
105
|
+
`[NOTIFY]` or Slack reply-thread, depending on origin).
|
|
106
|
+
3. Never downgrades to ⚪ Info even if the outer outcome (OKR /
|
|
107
|
+
request) isn't yet fully complete.
|
|
108
|
+
|
|
109
|
+
The auditor's "Silent Shipping Detection" monitor (issue #437)
|
|
110
|
+
cross-references `git log` against `report-status` / `[MILESTONE]`
|
|
111
|
+
events and flags any merged PR whose author did not emit a milestone
|
|
112
|
+
surface within 30 minutes of merge.
|
|
113
|
+
|
|
114
|
+
## Where this SOP applies
|
|
115
|
+
|
|
116
|
+
All roles that ship artifacts: developer, frontend-developer,
|
|
117
|
+
fullstack-dev, qa, qa-engineer, product-manager, tpm, designer,
|
|
118
|
+
ux-designer, architect, team-leader, generalist. The orc and the
|
|
119
|
+
auditor consume the surface; they don't emit it.
|
|
120
|
+
|
|
121
|
+
## Refs
|
|
122
|
+
|
|
123
|
+
- EPIC #426 — Proactive-followup gap audit
|
|
124
|
+
- RC1 #427 — Worker/TL prompts contain zero milestone keyword
|
|
125
|
+
- QW-1 #434 — This SOP
|
|
126
|
+
- QW-2 #435 — `[MILESTONE]` envelope in `report-status` skill
|
|
127
|
+
- QW-3 #436 — `[MILESTONE]` row in orc priority table
|
|
128
|
+
- QW-4 #437 — Auditor Silent Shipping Detection monitor
|