crewly 1.20.319 → 1.20.321
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/constants.ts +1 -1
- package/config/hooks/destructive-git-guard/guard.mjs +210 -0
- package/config/hooks/install-hooks.sh +6 -0
- package/config/hooks/pre-commit +29 -0
- package/config/roles/orchestrator/prompt.md +2 -1
- package/config/roles/team-leader/prompt.md +2 -1
- package/config/skills/_conventions/skill-lint.allowlist.json +127 -0
- package/config/skills/_conventions/skill-lint.ts +66 -0
- package/config/skills/agent/core/publish-app/SKILL.md +30 -2
- package/config/skills/agent/core/publish-app/execute.sh +21 -3
- package/config/skills/agent/core/publish-app/execute.test.sh +9 -0
- package/config/skills/agent/core/report-status/SKILL.md +2 -0
- package/config/skills/team-leader/decompose-goal/SKILL.md +20 -6
- package/config/skills/team-leader/decompose-goal/execute.sh +66 -5
- package/config/skills/team-leader/decompose-goal/execute.test.sh +92 -0
- package/config/sops/common/dev-process-tiers.md +1 -1
- package/config/sops/common/writing-for-agents.md +45 -0
- package/config/sops/developer/debugging.md +53 -0
- package/config/sops/developer/git-workflow.md +4 -0
- package/config/sops/developer/testing-requirements.md +14 -0
- package/dist/backend/backend/src/constants.d.ts +85 -1
- package/dist/backend/backend/src/constants.d.ts.map +1 -1
- package/dist/backend/backend/src/constants.js +86 -0
- package/dist/backend/backend/src/constants.js.map +1 -1
- package/dist/backend/backend/src/controllers/apps/apps.controller.d.ts +4 -0
- package/dist/backend/backend/src/controllers/apps/apps.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/apps/apps.controller.js +9 -0
- package/dist/backend/backend/src/controllers/apps/apps.controller.js.map +1 -1
- package/dist/backend/backend/src/controllers/apps/apps.routes.d.ts +2 -0
- package/dist/backend/backend/src/controllers/apps/apps.routes.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/apps/apps.routes.js +5 -1
- package/dist/backend/backend/src/controllers/apps/apps.routes.js.map +1 -1
- package/dist/backend/backend/src/controllers/chat/agent-reply.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/chat/agent-reply.controller.js +5 -0
- package/dist/backend/backend/src/controllers/chat/agent-reply.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 +10 -3
- package/dist/backend/backend/src/controllers/chat/chat.controller.js.map +1 -1
- package/dist/backend/backend/src/controllers/project-tickets/project-tickets.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/project-tickets/project-tickets.controller.js +9 -1
- package/dist/backend/backend/src/controllers/project-tickets/project-tickets.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 +11 -0
- package/dist/backend/backend/src/controllers/slack/slack.controller.js.map +1 -1
- package/dist/backend/backend/src/index.js +1 -1
- package/dist/backend/backend/src/index.js.map +1 -1
- package/dist/backend/backend/src/services/agent/agent-registration.service.d.ts +9 -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 +41 -1
- package/dist/backend/backend/src/services/agent/agent-registration.service.js.map +1 -1
- package/dist/backend/backend/src/services/agent/control-plane-guard.service.d.ts +4 -1
- package/dist/backend/backend/src/services/agent/control-plane-guard.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/agent/control-plane-guard.service.js +11 -2
- package/dist/backend/backend/src/services/agent/control-plane-guard.service.js.map +1 -1
- package/dist/backend/backend/src/services/agent/destructive-git-guard.service.d.ts +21 -0
- package/dist/backend/backend/src/services/agent/destructive-git-guard.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/agent/destructive-git-guard.service.js +40 -0
- package/dist/backend/backend/src/services/agent/destructive-git-guard.service.js.map +1 -0
- package/dist/backend/backend/src/services/agent/follow-through.wiring.d.ts.map +1 -1
- package/dist/backend/backend/src/services/agent/follow-through.wiring.js +42 -14
- package/dist/backend/backend/src/services/agent/follow-through.wiring.js.map +1 -1
- package/dist/backend/backend/src/services/agent/fresh-task-conversation.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/agent/fresh-task-conversation.service.js +5 -1
- package/dist/backend/backend/src/services/agent/fresh-task-conversation.service.js.map +1 -1
- package/dist/backend/backend/src/services/agent/handover-pointers.d.ts +70 -0
- package/dist/backend/backend/src/services/agent/handover-pointers.d.ts.map +1 -0
- package/dist/backend/backend/src/services/agent/handover-pointers.js +122 -0
- package/dist/backend/backend/src/services/agent/handover-pointers.js.map +1 -0
- package/dist/backend/backend/src/services/agent/runtime-session-recovery.d.ts.map +1 -1
- package/dist/backend/backend/src/services/agent/runtime-session-recovery.js +6 -3
- package/dist/backend/backend/src/services/agent/runtime-session-recovery.js.map +1 -1
- package/dist/backend/backend/src/services/ai/prompt-modules/decision-rights.module.d.ts.map +1 -1
- package/dist/backend/backend/src/services/ai/prompt-modules/decision-rights.module.js +2 -1
- package/dist/backend/backend/src/services/ai/prompt-modules/decision-rights.module.js.map +1 -1
- package/dist/backend/backend/src/services/apps/apps.service.d.ts +40 -0
- package/dist/backend/backend/src/services/apps/apps.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/apps/apps.service.js +54 -0
- package/dist/backend/backend/src/services/apps/apps.service.js.map +1 -1
- package/dist/backend/backend/src/services/cache/redis-cache.service.d.ts +30 -2
- package/dist/backend/backend/src/services/cache/redis-cache.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/cache/redis-cache.service.js +43 -3
- package/dist/backend/backend/src/services/cache/redis-cache.service.js.map +1 -1
- package/dist/backend/backend/src/services/cloud/cloud-disconnect-notice.utils.d.ts.map +1 -1
- package/dist/backend/backend/src/services/cloud/cloud-disconnect-notice.utils.js +2 -0
- package/dist/backend/backend/src/services/cloud/cloud-disconnect-notice.utils.js.map +1 -1
- package/dist/backend/backend/src/services/cloud/cloud-sync.service.d.ts +9 -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 +50 -8
- package/dist/backend/backend/src/services/cloud/cloud-sync.service.js.map +1 -1
- package/dist/backend/backend/src/services/decisions/decision-routing.d.ts +4 -1
- package/dist/backend/backend/src/services/decisions/decision-routing.d.ts.map +1 -1
- package/dist/backend/backend/src/services/decisions/decision-routing.js +11 -7
- package/dist/backend/backend/src/services/decisions/decision-routing.js.map +1 -1
- package/dist/backend/backend/src/services/decisions/decision.service.d.ts +6 -0
- package/dist/backend/backend/src/services/decisions/decision.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/decisions/decision.service.js +17 -2
- package/dist/backend/backend/src/services/decisions/decision.service.js.map +1 -1
- package/dist/backend/backend/src/services/decisions/decision.wiring.d.ts.map +1 -1
- package/dist/backend/backend/src/services/decisions/decision.wiring.js +20 -1
- package/dist/backend/backend/src/services/decisions/decision.wiring.js.map +1 -1
- package/dist/backend/backend/src/services/decisions/recurring-change-handoff.d.ts +81 -0
- package/dist/backend/backend/src/services/decisions/recurring-change-handoff.d.ts.map +1 -0
- package/dist/backend/backend/src/services/decisions/recurring-change-handoff.js +132 -0
- package/dist/backend/backend/src/services/decisions/recurring-change-handoff.js.map +1 -0
- package/dist/backend/backend/src/services/event-bus/event-to-workitem-bridge.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/event-bus/event-to-workitem-bridge.service.js +6 -0
- package/dist/backend/backend/src/services/event-bus/event-to-workitem-bridge.service.js.map +1 -1
- package/dist/backend/backend/src/services/messaging/owner-thread-sentinel.service.d.ts +14 -0
- package/dist/backend/backend/src/services/messaging/owner-thread-sentinel.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/messaging/owner-thread-sentinel.service.js +67 -13
- package/dist/backend/backend/src/services/messaging/owner-thread-sentinel.service.js.map +1 -1
- package/dist/backend/backend/src/services/open-items/open-item-card.d.ts +25 -1
- package/dist/backend/backend/src/services/open-items/open-item-card.d.ts.map +1 -1
- package/dist/backend/backend/src/services/open-items/open-item-card.js +108 -1
- package/dist/backend/backend/src/services/open-items/open-item-card.js.map +1 -1
- package/dist/backend/backend/src/services/open-items/open-item-extractor.d.ts +2 -0
- package/dist/backend/backend/src/services/open-items/open-item-extractor.d.ts.map +1 -1
- package/dist/backend/backend/src/services/open-items/open-item-extractor.js +21 -1
- package/dist/backend/backend/src/services/open-items/open-item-extractor.js.map +1 -1
- package/dist/backend/backend/src/services/open-items/open-items.service.d.ts +12 -1
- package/dist/backend/backend/src/services/open-items/open-items.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/open-items/open-items.service.js +54 -4
- package/dist/backend/backend/src/services/open-items/open-items.service.js.map +1 -1
- package/dist/backend/backend/src/services/orc/owner-completion-report.service.d.ts +11 -0
- package/dist/backend/backend/src/services/orc/owner-completion-report.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/orc/owner-completion-report.service.js +23 -0
- package/dist/backend/backend/src/services/orc/owner-completion-report.service.js.map +1 -1
- package/dist/backend/backend/src/services/orc/reply-destination.wiring.d.ts.map +1 -1
- package/dist/backend/backend/src/services/orc/reply-destination.wiring.js +69 -6
- package/dist/backend/backend/src/services/orc/reply-destination.wiring.js.map +1 -1
- package/dist/backend/backend/src/services/project-tickets/project-ticket-workflow.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/project-tickets/project-ticket-workflow.service.js +7 -2
- package/dist/backend/backend/src/services/project-tickets/project-ticket-workflow.service.js.map +1 -1
- package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-decision.d.ts +44 -1
- package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-decision.d.ts.map +1 -1
- package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-decision.js +32 -3
- package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-decision.js.map +1 -1
- package/dist/backend/backend/src/services/project-tickets/ticket-autopilot.service.d.ts +5 -4
- package/dist/backend/backend/src/services/project-tickets/ticket-autopilot.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/project-tickets/ticket-autopilot.service.js +41 -10
- package/dist/backend/backend/src/services/project-tickets/ticket-autopilot.service.js.map +1 -1
- 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 +26 -4
- package/dist/backend/backend/src/services/reconciler/reconciler-data-provider.js.map +1 -1
- package/dist/backend/backend/src/services/runtime-fallback/runtime-fallback.wiring.d.ts.map +1 -1
- package/dist/backend/backend/src/services/runtime-fallback/runtime-fallback.wiring.js +3 -2
- package/dist/backend/backend/src/services/runtime-fallback/runtime-fallback.wiring.js.map +1 -1
- package/dist/backend/backend/src/services/session/tui-input-guard.js +1 -1
- package/dist/backend/backend/src/services/session/tui-input-guard.js.map +1 -1
- package/dist/backend/backend/src/services/task-pool/task-pool.service.d.ts +15 -0
- 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 +33 -0
- package/dist/backend/backend/src/services/task-pool/task-pool.service.js.map +1 -1
- package/dist/backend/backend/src/services/trace/session-projects.d.ts +17 -0
- package/dist/backend/backend/src/services/trace/session-projects.d.ts.map +1 -0
- package/dist/backend/backend/src/services/trace/session-projects.js +57 -0
- package/dist/backend/backend/src/services/trace/session-projects.js.map +1 -0
- package/dist/backend/backend/src/services/trace/trace-context.service.d.ts +27 -1
- package/dist/backend/backend/src/services/trace/trace-context.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/trace/trace-context.service.js +43 -5
- package/dist/backend/backend/src/services/trace/trace-context.service.js.map +1 -1
- package/dist/backend/backend/src/services/trace/trace-http.middleware.d.ts +9 -1
- package/dist/backend/backend/src/services/trace/trace-http.middleware.d.ts.map +1 -1
- package/dist/backend/backend/src/services/trace/trace-http.middleware.js +10 -2
- package/dist/backend/backend/src/services/trace/trace-http.middleware.js.map +1 -1
- package/dist/backend/backend/src/services/trace/trace-recorder.d.ts +7 -3
- package/dist/backend/backend/src/services/trace/trace-recorder.d.ts.map +1 -1
- package/dist/backend/backend/src/services/trace/trace-recorder.js +12 -5
- package/dist/backend/backend/src/services/trace/trace-recorder.js.map +1 -1
- package/dist/backend/backend/src/services/v3/workitem-dispatch.subscriber.d.ts +59 -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 +101 -31
- package/dist/backend/backend/src/services/v3/workitem-dispatch.subscriber.js.map +1 -1
- package/dist/backend/build-info.json +2 -2
- package/dist/backend/config/constants.d.ts +1 -1
- package/dist/backend/config/constants.d.ts.map +1 -1
- package/dist/backend/config/constants.js +1 -1
- package/dist/backend/config/constants.js.map +1 -1
- package/dist/cli/backend/src/constants.d.ts +85 -1
- package/dist/cli/backend/src/constants.d.ts.map +1 -1
- package/dist/cli/backend/src/constants.js +86 -0
- package/dist/cli/backend/src/constants.js.map +1 -1
- package/dist/cli/backend/src/services/agent/agent-registration.service.d.ts +9 -0
- package/dist/cli/backend/src/services/agent/agent-registration.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/agent/agent-registration.service.js +41 -1
- package/dist/cli/backend/src/services/agent/agent-registration.service.js.map +1 -1
- package/dist/cli/backend/src/services/agent/control-plane-guard.service.d.ts +4 -1
- package/dist/cli/backend/src/services/agent/control-plane-guard.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/agent/control-plane-guard.service.js +11 -2
- package/dist/cli/backend/src/services/agent/control-plane-guard.service.js.map +1 -1
- package/dist/cli/backend/src/services/agent/destructive-git-guard.service.d.ts +21 -0
- package/dist/cli/backend/src/services/agent/destructive-git-guard.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/agent/destructive-git-guard.service.js +40 -0
- package/dist/cli/backend/src/services/agent/destructive-git-guard.service.js.map +1 -0
- package/dist/cli/backend/src/services/agent/fresh-task-conversation.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/agent/fresh-task-conversation.service.js +5 -1
- package/dist/cli/backend/src/services/agent/fresh-task-conversation.service.js.map +1 -1
- package/dist/cli/backend/src/services/agent/handover-pointers.d.ts +70 -0
- package/dist/cli/backend/src/services/agent/handover-pointers.d.ts.map +1 -0
- package/dist/cli/backend/src/services/agent/handover-pointers.js +122 -0
- package/dist/cli/backend/src/services/agent/handover-pointers.js.map +1 -0
- package/dist/cli/backend/src/services/agent/runtime-session-recovery.d.ts.map +1 -1
- package/dist/cli/backend/src/services/agent/runtime-session-recovery.js +6 -3
- package/dist/cli/backend/src/services/agent/runtime-session-recovery.js.map +1 -1
- package/dist/cli/backend/src/services/ai/prompt-modules/decision-rights.module.d.ts.map +1 -1
- package/dist/cli/backend/src/services/ai/prompt-modules/decision-rights.module.js +2 -1
- package/dist/cli/backend/src/services/ai/prompt-modules/decision-rights.module.js.map +1 -1
- package/dist/cli/backend/src/services/cloud/cloud-sync.service.d.ts +9 -0
- package/dist/cli/backend/src/services/cloud/cloud-sync.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/cloud/cloud-sync.service.js +50 -8
- package/dist/cli/backend/src/services/cloud/cloud-sync.service.js.map +1 -1
- package/dist/cli/backend/src/services/decisions/decision.service.d.ts +6 -0
- package/dist/cli/backend/src/services/decisions/decision.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/decisions/decision.service.js +17 -2
- package/dist/cli/backend/src/services/decisions/decision.service.js.map +1 -1
- package/dist/cli/backend/src/services/messaging/owner-thread-sentinel.service.d.ts +14 -0
- package/dist/cli/backend/src/services/messaging/owner-thread-sentinel.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/messaging/owner-thread-sentinel.service.js +67 -13
- package/dist/cli/backend/src/services/messaging/owner-thread-sentinel.service.js.map +1 -1
- package/dist/cli/backend/src/services/open-items/open-item-card.d.ts +25 -1
- package/dist/cli/backend/src/services/open-items/open-item-card.d.ts.map +1 -1
- package/dist/cli/backend/src/services/open-items/open-item-card.js +108 -1
- package/dist/cli/backend/src/services/open-items/open-item-card.js.map +1 -1
- package/dist/cli/backend/src/services/open-items/open-item-extractor.d.ts +2 -0
- package/dist/cli/backend/src/services/open-items/open-item-extractor.d.ts.map +1 -1
- package/dist/cli/backend/src/services/open-items/open-item-extractor.js +21 -1
- package/dist/cli/backend/src/services/open-items/open-item-extractor.js.map +1 -1
- package/dist/cli/backend/src/services/open-items/open-items.service.d.ts +12 -1
- package/dist/cli/backend/src/services/open-items/open-items.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/open-items/open-items.service.js +54 -4
- package/dist/cli/backend/src/services/open-items/open-items.service.js.map +1 -1
- package/dist/cli/backend/src/services/session/tui-input-guard.js +1 -1
- package/dist/cli/backend/src/services/session/tui-input-guard.js.map +1 -1
- package/dist/cli/backend/src/services/task-pool/task-pool.service.d.ts +15 -0
- 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 +33 -0
- package/dist/cli/backend/src/services/task-pool/task-pool.service.js.map +1 -1
- package/dist/cli/backend/src/services/trace/session-projects.d.ts +17 -0
- package/dist/cli/backend/src/services/trace/session-projects.d.ts.map +1 -0
- package/dist/cli/backend/src/services/trace/session-projects.js +57 -0
- package/dist/cli/backend/src/services/trace/session-projects.js.map +1 -0
- package/dist/cli/backend/src/services/trace/trace-context.service.d.ts +27 -1
- package/dist/cli/backend/src/services/trace/trace-context.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/trace/trace-context.service.js +43 -5
- package/dist/cli/backend/src/services/trace/trace-context.service.js.map +1 -1
- package/dist/cli/backend/src/services/trace/trace-recorder.d.ts +7 -3
- package/dist/cli/backend/src/services/trace/trace-recorder.d.ts.map +1 -1
- package/dist/cli/backend/src/services/trace/trace-recorder.js +12 -5
- package/dist/cli/backend/src/services/trace/trace-recorder.js.map +1 -1
- package/dist/cli/config/constants.d.ts +1 -1
- package/dist/cli/config/constants.d.ts.map +1 -1
- package/dist/cli/config/constants.js +1 -1
- package/dist/cli/config/constants.js.map +1 -1
- package/package.json +1 -1
|
@@ -60,7 +60,15 @@ Where `/tmp/decompose.json` contains:
|
|
|
60
60
|
"description": "Create a React login form with email/password fields and validation",
|
|
61
61
|
"requiredRole": "frontend-developer",
|
|
62
62
|
"acceptanceCriteria": "- Form renders correctly\n- Client-side validation works\n- Accessible (ARIA labels)",
|
|
63
|
-
"priority": "high"
|
|
63
|
+
"priority": "high",
|
|
64
|
+
"blockedBy": ["Implement JWT token service"]
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"title": "Wire login form to the token service",
|
|
68
|
+
"description": "Call the token service from the form and store the session; cover the full path with an end-to-end test",
|
|
69
|
+
"requiredRole": "frontend-developer",
|
|
70
|
+
"acceptanceCriteria": "- A user can log in end to end\n- E2E test passes",
|
|
71
|
+
"blockedBy": [0, 1]
|
|
64
72
|
}
|
|
65
73
|
]
|
|
66
74
|
}
|
|
@@ -80,6 +88,8 @@ Where `/tmp/decompose.json` contains:
|
|
|
80
88
|
| `tasks[].requiredRole` | No | Role best suited for this task (default: `developer`) |
|
|
81
89
|
| `tasks[].acceptanceCriteria` | No | Criteria for task verification |
|
|
82
90
|
| `tasks[].priority` | No | Task priority: `low`, `normal`, `high` (default: `normal`) |
|
|
91
|
+
| `tasks[].blockedBy` | No | Tasks of this same call that must finish first: 0-based indexes or exact titles. Becomes the WorkItem `dependsOn`; the task stays `blocked` and is not dispatched until every blocker is done. Unknown/ambiguous references and cycles are rejected and nothing is created |
|
|
92
|
+
| `--dry-run` | No | Print the breakdown (title / blockedBy / delivers) in creation order and create nothing. Use it to review a large decomposition first |
|
|
83
93
|
|
|
84
94
|
## Output
|
|
85
95
|
|
|
@@ -98,11 +108,15 @@ Where `/tmp/decompose.json` contains:
|
|
|
98
108
|
|
|
99
109
|
## Best Practices
|
|
100
110
|
|
|
101
|
-
1. **
|
|
102
|
-
2. **
|
|
103
|
-
3. **
|
|
104
|
-
4. **
|
|
105
|
-
5. **
|
|
111
|
+
1. **Cut vertical slices**: each task is a narrow but COMPLETE path through every layer (schema, API, UI, tests), not a horizontal slice of one layer.
|
|
112
|
+
2. **Demoable alone**: a finished slice can be demoed or verified on its own.
|
|
113
|
+
3. **Fresh-context size**: size each slice to fit a single fresh context window (the test for "completable by one worker in one session").
|
|
114
|
+
4. **Prefactor first**: any prefactoring is its own first task that the slices depend on.
|
|
115
|
+
5. **Wide refactors**: expand (add the new path), migrate in batches, contract (remove the old path). Each batch is its own task with `blockedBy` the expand task.
|
|
116
|
+
6. **Name the blockers**: put every real prerequisite in `blockedBy`. A task with no blockers can start now; work the frontier (tasks whose blockers are all done).
|
|
117
|
+
7. **Include acceptance criteria**: Workers need clear success metrics.
|
|
118
|
+
8. **Match roles**: Set `requiredRole` to help with delegation.
|
|
119
|
+
9. **Don't over-split**: 2-5 sub-tasks per objective is typical.
|
|
106
120
|
|
|
107
121
|
## Related Skills
|
|
108
122
|
|
|
@@ -13,17 +13,19 @@ source "${SCRIPT_DIR}/../_common/lib.sh"
|
|
|
13
13
|
# (the [TICKET:TKT-123 <id>] line of the message being worked on). Also
|
|
14
14
|
# accepted as `"requestId"` in the JSON.
|
|
15
15
|
REQUEST_ID=""
|
|
16
|
+
DRY_RUN=0
|
|
16
17
|
POSITIONAL=()
|
|
17
18
|
while [[ $# -gt 0 ]]; do
|
|
18
19
|
case "$1" in
|
|
19
20
|
--request-id|-R) REQUEST_ID="${2:-}"; shift 2 ;;
|
|
21
|
+
--dry-run) DRY_RUN=1; shift ;;
|
|
20
22
|
*) POSITIONAL+=("$1"); shift ;;
|
|
21
23
|
esac
|
|
22
24
|
done
|
|
23
25
|
set -- "${POSITIONAL[@]+"${POSITIONAL[@]}"}"
|
|
24
26
|
|
|
25
27
|
INPUT=$(read_json_input "${1:-}")
|
|
26
|
-
[ -z "$INPUT" ] && error_exit "Usage: execute.sh '{\"objective\":\"Build auth module\",\"projectPath\":\"/path/to/project\",\"requestId\":\"<ticket id, optional>\",\"tasks\":[{\"title\":\"...\",\"description\":\"...\",\"requiredRole\":\"developer\",\"acceptanceCriteria\":\"...\",\"priority\":\"high\"}]}' [--request-id <id>]"
|
|
28
|
+
[ -z "$INPUT" ] && error_exit "Usage: execute.sh '{\"objective\":\"Build auth module\",\"projectPath\":\"/path/to/project\",\"requestId\":\"<ticket id, optional>\",\"tasks\":[{\"title\":\"...\",\"description\":\"...\",\"requiredRole\":\"developer\",\"acceptanceCriteria\":\"...\",\"priority\":\"high\"}]}' [--request-id <id>] [--dry-run] (optional per task: \"blockedBy\":[<index or title>, ...])"
|
|
27
29
|
|
|
28
30
|
OBJECTIVE=$(printf '%s' "$INPUT" | jq -r '.objective // empty')
|
|
29
31
|
PROJECT_PATH=$(printf '%s' "$INPUT" | jq -r '.projectPath // empty')
|
|
@@ -39,11 +41,52 @@ if [ "$TASK_COUNT" = "0" ]; then
|
|
|
39
41
|
error_exit "tasks array must contain at least one task"
|
|
40
42
|
fi
|
|
41
43
|
|
|
44
|
+
# Blocking edges: tasks[].blockedBy lists tasks of this same call (0-based
|
|
45
|
+
# index or exact title). Resolve to indexes, reject unknown/ambiguous
|
|
46
|
+
# references and cycles (a self-reference is a cycle) BEFORE creating anything, and derive a creation order
|
|
47
|
+
# in which every blocker comes first.
|
|
48
|
+
PLAN=$(printf '%s' "$TASKS" | jq -c '
|
|
49
|
+
. as $t
|
|
50
|
+
| [ to_entries[] | (.value.blockedBy // []) | map(
|
|
51
|
+
if type == "number" then (if . >= 0 and . < ($t | length) and . == floor then . else null end)
|
|
52
|
+
elif type == "string" then (. as $s | [$t | to_entries[] | select(.value.title == $s) | .key] | if length == 1 then .[0] else null end)
|
|
53
|
+
else null end) ] as $deps
|
|
54
|
+
| ([ $deps | to_entries[] | select(.value | any(. == null)) | .key ]) as $bad
|
|
55
|
+
| { deps: $deps, bad: $bad }
|
|
56
|
+
| . as $p
|
|
57
|
+
| ($p.deps | length) as $n
|
|
58
|
+
| ({ done: [], rem: [range(0; $n)], stuck: false }
|
|
59
|
+
| until((.rem | length) == 0 or .stuck;
|
|
60
|
+
. as $s
|
|
61
|
+
| [ $s.rem[] | . as $i | select($p.deps[$i] | all(. as $d | any($s.done[]; . == $d))) ] as $ready
|
|
62
|
+
| if ($ready | length) == 0 then .stuck = true else (.done += $ready | .rem -= $ready) end)) as $o
|
|
63
|
+
| { deps: $p.deps, bad: $p.bad, order: $o.done, cyclic: $o.rem }')
|
|
64
|
+
BAD=$(printf '%s' "$PLAN" | jq -c '.bad')
|
|
65
|
+
CYCLIC=$(printf '%s' "$PLAN" | jq -c '.cyclic')
|
|
66
|
+
if [ "$BAD" != "[]" ]; then
|
|
67
|
+
error_exit "blockedBy has an unknown, ambiguous or invalid reference in task index(es) ${BAD}; nothing created"
|
|
68
|
+
fi
|
|
69
|
+
if [ "$CYCLIC" != "[]" ]; then
|
|
70
|
+
error_exit "blockedBy forms a cycle among task index(es) ${CYCLIC}; nothing created"
|
|
71
|
+
fi
|
|
72
|
+
|
|
73
|
+
if [ "$DRY_RUN" = "1" ]; then
|
|
74
|
+
printf '%s' "$TASKS" | jq -c --argjson plan "$PLAN" --arg objective "$OBJECTIVE" '
|
|
75
|
+
. as $t
|
|
76
|
+
| { dryRun: true, objective: $objective, tasks: [ $plan.order[] | . as $i | {
|
|
77
|
+
index: $i, title: $t[$i].title,
|
|
78
|
+
blockedBy: [ $plan.deps[$i][] | $t[.].title ],
|
|
79
|
+
delivers: ($t[$i].description // "") } ] }'
|
|
80
|
+
exit 0
|
|
81
|
+
fi
|
|
82
|
+
|
|
42
83
|
CREATED_TASKS="[]"
|
|
43
84
|
ERRORS="[]"
|
|
85
|
+
# index -> WorkItem id, for resolving blockedBy into dependsOn
|
|
86
|
+
declare -a CREATED_ID_BY_INDEX=()
|
|
44
87
|
|
|
45
|
-
# Create each sub-task via the task-
|
|
46
|
-
for i in $(
|
|
88
|
+
# Create each sub-task via the task-pool API, blockers first
|
|
89
|
+
for i in $(printf '%s' "$PLAN" | jq -r '.order[]'); do
|
|
47
90
|
TASK_TITLE=$(echo "$TASKS" | jq -r ".[$i].title // empty")
|
|
48
91
|
TASK_DESC=$(echo "$TASKS" | jq -r ".[$i].description // empty")
|
|
49
92
|
TASK_ROLE=$(echo "$TASKS" | jq -r ".[$i].requiredRole // \"developer\"")
|
|
@@ -55,6 +98,21 @@ for i in $(seq 0 $((TASK_COUNT - 1))); do
|
|
|
55
98
|
continue
|
|
56
99
|
fi
|
|
57
100
|
|
|
101
|
+
# Resolve blockers to WorkItem ids; a blocker that failed to create blocks this task too.
|
|
102
|
+
DEPENDS_ON="[]"
|
|
103
|
+
DEP_FAILED=0
|
|
104
|
+
for d in $(printf '%s' "$PLAN" | jq -r ".deps[$i][]"); do
|
|
105
|
+
if [ -n "${CREATED_ID_BY_INDEX[$d]:-}" ]; then
|
|
106
|
+
DEPENDS_ON=$(echo "$DEPENDS_ON" | jq --arg id "${CREATED_ID_BY_INDEX[$d]}" '. + [$id]')
|
|
107
|
+
else
|
|
108
|
+
DEP_FAILED=1
|
|
109
|
+
fi
|
|
110
|
+
done
|
|
111
|
+
if [ "$DEP_FAILED" = "1" ]; then
|
|
112
|
+
ERRORS=$(echo "$ERRORS" | jq --arg msg "Skipped (a blocker was not created): ${TASK_TITLE}" '. + [$msg]')
|
|
113
|
+
continue
|
|
114
|
+
fi
|
|
115
|
+
|
|
58
116
|
# Compose the brief markdown for the WorkItem.
|
|
59
117
|
BRIEF="${TASK_DESC}"
|
|
60
118
|
[ -n "$TASK_CRITERIA" ] && BRIEF="${BRIEF}
|
|
@@ -83,13 +141,14 @@ Required role: ${TASK_ROLE}"
|
|
|
83
141
|
--arg role "$TASK_ROLE" \
|
|
84
142
|
--arg priority "$TASK_PRIORITY" \
|
|
85
143
|
--arg requestId "$REQUEST_ID" \
|
|
144
|
+
--argjson dependsOn "$DEPENDS_ON" \
|
|
86
145
|
'{
|
|
87
146
|
title: $title,
|
|
88
147
|
type: "delegate",
|
|
89
148
|
owner: "system",
|
|
90
149
|
briefMarkdown: $brief,
|
|
91
150
|
metadata: { projectPath: $projectPath, milestone: $milestone, requiredRole: $role, priority: $priority }
|
|
92
|
-
} + (if $requestId != "" then {requestId: $requestId} else {} end)')
|
|
151
|
+
} + (if $requestId != "" then {requestId: $requestId} else {} end) + (if ($dependsOn | length) > 0 then {dependsOn: $dependsOn} else {} end)')
|
|
93
152
|
|
|
94
153
|
# No envelope: addItem reads req.body directly as the CreateWorkItemInput.
|
|
95
154
|
CREATE_BODY="$WORK_ITEM"
|
|
@@ -97,12 +156,14 @@ Required role: ${TASK_ROLE}"
|
|
|
97
156
|
CREATED_ID=$(echo "$CREATE_RESULT" | jq -r '.data.id // .workItem.id // empty' 2>/dev/null || true)
|
|
98
157
|
|
|
99
158
|
if [ -n "$CREATED_ID" ]; then
|
|
159
|
+
CREATED_ID_BY_INDEX[$i]="$CREATED_ID"
|
|
100
160
|
CREATED_TASKS=$(echo "$CREATED_TASKS" | jq \
|
|
101
161
|
--arg title "$TASK_TITLE" \
|
|
102
162
|
--arg role "$TASK_ROLE" \
|
|
103
163
|
--arg id "$CREATED_ID" \
|
|
104
164
|
--arg priority "$TASK_PRIORITY" \
|
|
105
|
-
|
|
165
|
+
--argjson dependsOn "$DEPENDS_ON" \
|
|
166
|
+
'. + [{title: $title, requiredRole: $role, workItemId: $id, priority: $priority} + (if ($dependsOn | length) > 0 then {dependsOn: $dependsOn} else {} end)]')
|
|
106
167
|
else
|
|
107
168
|
ERRORS=$(echo "$ERRORS" | jq --arg msg "Failed to create WorkItem: ${TASK_TITLE}" '. + [$msg]')
|
|
108
169
|
fi
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Tests for decompose-goal (TL) — run with: bash execute.test.sh
|
|
3
|
+
# A python HTTP stub plays the backend; asserts the POST /task-pool/add bodies.
|
|
4
|
+
set -u
|
|
5
|
+
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
6
|
+
EXEC="$HERE/execute.sh"
|
|
7
|
+
PASS=0; FAIL=0
|
|
8
|
+
check() {
|
|
9
|
+
local name="$1"; local got="$2"; local want="$3"
|
|
10
|
+
if [ "$got" = "$want" ]; then PASS=$((PASS+1)); else FAIL=$((FAIL+1)); echo "FAIL: $name"; echo " want: $want"; echo " got: $got"; fi
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
PORT=18843
|
|
14
|
+
STUB_LOG="$(mktemp)"; export STUB_LOG; export STUB_PORT="$PORT"
|
|
15
|
+
python3 -u <<'PY' >/dev/null 2>&1 &
|
|
16
|
+
from http.server import BaseHTTPRequestHandler, HTTPServer
|
|
17
|
+
import json, os
|
|
18
|
+
LOG = os.environ['STUB_LOG']; PORT = int(os.environ['STUB_PORT'])
|
|
19
|
+
N = [0]
|
|
20
|
+
class H(BaseHTTPRequestHandler):
|
|
21
|
+
def reply(self, code, obj):
|
|
22
|
+
self.send_response(code); self.send_header('Content-Type', 'application/json'); self.end_headers()
|
|
23
|
+
self.wfile.write(json.dumps(obj).encode())
|
|
24
|
+
def do_GET(self):
|
|
25
|
+
return self.reply(200, {'ok': True})
|
|
26
|
+
def do_POST(self):
|
|
27
|
+
n = int(self.headers.get('content-length', '0')); body = json.loads(self.rfile.read(n).decode() or '{}')
|
|
28
|
+
if not self.path.endswith('/task-pool/add'):
|
|
29
|
+
return self.reply(200, {'success': True})
|
|
30
|
+
with open(LOG, 'a') as f: f.write(json.dumps(body) + '\n')
|
|
31
|
+
if body.get('title') == 'Fails':
|
|
32
|
+
return self.reply(500, {'success': False})
|
|
33
|
+
return self.reply(200, {'success': True, 'data': {'id': 'wi-' + body['title']}})
|
|
34
|
+
def log_message(self, *a, **k): pass
|
|
35
|
+
HTTPServer(('127.0.0.1', PORT), H).serve_forever()
|
|
36
|
+
PY
|
|
37
|
+
STUB_PID=$!
|
|
38
|
+
disown "$STUB_PID" 2>/dev/null || true
|
|
39
|
+
trap 'kill "$STUB_PID" >/dev/null 2>&1; rm -f "$STUB_LOG"' EXIT
|
|
40
|
+
for i in $(seq 1 30); do curl -s -o /dev/null -m 0.2 "http://127.0.0.1:${PORT}/" 2>/dev/null && break; sleep 0.1; done
|
|
41
|
+
|
|
42
|
+
run() { CREWLY_SESSION_NAME=tl-sam CREWLY_API_URL="http://127.0.0.1:${PORT}" bash "$EXEC" "$@" 2>&1; }
|
|
43
|
+
posts() { wc -l < "$STUB_LOG" | tr -d ' '; }
|
|
44
|
+
|
|
45
|
+
# 1. No blockedBy: unchanged behaviour, no dependsOn on the wire.
|
|
46
|
+
: > "$STUB_LOG"
|
|
47
|
+
OUT=$(run '{"objective":"O","tasks":[{"title":"A","description":"a"},{"title":"B","description":"b"}]}')
|
|
48
|
+
check "plain: two created" "$(printf '%s' "$OUT" | jq -r .tasksCreated)" "2"
|
|
49
|
+
check "plain: no dependsOn" "$(jq -c 'has("dependsOn")' "$STUB_LOG" | sort -u)" "false"
|
|
50
|
+
|
|
51
|
+
# 2. Dependency chain (titles and indexes mixed), listed out of order: blockers are created first.
|
|
52
|
+
: > "$STUB_LOG"
|
|
53
|
+
OUT=$(run '{"objective":"Auth","tasks":[
|
|
54
|
+
{"title":"UI","description":"u","blockedBy":["API"]},
|
|
55
|
+
{"title":"Schema","description":"s"},
|
|
56
|
+
{"title":"API","description":"a","blockedBy":[1]},
|
|
57
|
+
{"title":"E2E","description":"e","blockedBy":["UI","API"]}]}')
|
|
58
|
+
check "chain: all created" "$(printf '%s' "$OUT" | jq -r .tasksCreated)" "4"
|
|
59
|
+
check "chain: creation order" "$(jq -r .title "$STUB_LOG" | paste -sd, -)" "Schema,API,UI,E2E"
|
|
60
|
+
check "chain: API depends on Schema" "$(jq -c 'select(.title=="API") | .dependsOn' "$STUB_LOG")" '["wi-Schema"]'
|
|
61
|
+
check "chain: UI depends on API" "$(jq -c 'select(.title=="UI") | .dependsOn' "$STUB_LOG")" '["wi-API"]'
|
|
62
|
+
check "chain: E2E depends on API and UI" "$(jq -c 'select(.title=="E2E") | .dependsOn | sort' "$STUB_LOG")" '["wi-API","wi-UI"]'
|
|
63
|
+
check "chain: Schema has no dependsOn" "$(jq -c 'select(.title=="Schema") | has("dependsOn")' "$STUB_LOG")" "false"
|
|
64
|
+
|
|
65
|
+
# 3. Cycle / unknown / self reference: error, nothing created.
|
|
66
|
+
: > "$STUB_LOG"
|
|
67
|
+
OUT=$(run '{"objective":"O","tasks":[{"title":"A","description":"a","blockedBy":["B"]},{"title":"B","description":"b","blockedBy":["A"]}]}')
|
|
68
|
+
check "cycle: error" "$(printf '%s' "$OUT" | grep -c 'cycle')" "1"
|
|
69
|
+
check "cycle: nothing created" "$(posts)" "0"
|
|
70
|
+
OUT=$(run '{"objective":"O","tasks":[{"title":"A","description":"a","blockedBy":["Nope"]}]}')
|
|
71
|
+
check "unknown title: error" "$(printf '%s' "$OUT" | grep -c 'unknown')" "1"
|
|
72
|
+
OUT=$(run '{"objective":"O","tasks":[{"title":"A","description":"a","blockedBy":[5]}]}')
|
|
73
|
+
check "index out of range: error" "$(printf '%s' "$OUT" | grep -c 'unknown')" "1"
|
|
74
|
+
OUT=$(run '{"objective":"O","tasks":[{"title":"A","description":"a","blockedBy":[0]}]}')
|
|
75
|
+
check "self reference: error" "$(printf '%s' "$OUT" | grep -c 'cycle')" "1"
|
|
76
|
+
check "errors: nothing created" "$(posts)" "0"
|
|
77
|
+
|
|
78
|
+
# 4. A blocker that fails to create also blocks its dependents from being created.
|
|
79
|
+
: > "$STUB_LOG"
|
|
80
|
+
OUT=$(run '{"objective":"O","tasks":[{"title":"Fails","description":"f"},{"title":"After","description":"x","blockedBy":["Fails"]}]}')
|
|
81
|
+
check "failed blocker: success false" "$(printf '%s' "$OUT" | jq -r .success)" "false"
|
|
82
|
+
check "failed blocker: dependent skipped" "$(grep -c '"After"' "$STUB_LOG")" "0"
|
|
83
|
+
|
|
84
|
+
# 5. --dry-run prints the breakdown, creates nothing.
|
|
85
|
+
: > "$STUB_LOG"
|
|
86
|
+
OUT=$(run '{"objective":"O","tasks":[{"title":"B","description":"b","blockedBy":["A"]},{"title":"A","description":"a"}]}' --dry-run)
|
|
87
|
+
check "dry-run: order" "$(printf '%s' "$OUT" | jq -c '[.tasks[] | .title]')" '["A","B"]'
|
|
88
|
+
check "dry-run: blockedBy titles" "$(printf '%s' "$OUT" | jq -c '.tasks[1].blockedBy')" '["A"]'
|
|
89
|
+
check "dry-run: nothing created" "$(posts)" "0"
|
|
90
|
+
|
|
91
|
+
echo "decompose-goal: ${PASS} passed, ${FAIL} failed"
|
|
92
|
+
[ "$FAIL" -eq 0 ]
|
|
@@ -99,7 +99,7 @@ If two rules disagree, the higher tier wins.
|
|
|
99
99
|
4. One TL or senior reviewer + one peer reviewer.
|
|
100
100
|
5. CHANGELOG entry if user-visible.
|
|
101
101
|
6. Manual smoke test of the changed flow before merge.
|
|
102
|
-
7.
|
|
102
|
+
7. Merge Danger filled in on the PR (Door + Blast radius). A one-way door needs the rollback rehearsal from Tier 3. One-way = data migration, deletion, published artifact (npm, docker, deploy), outbound message.
|
|
103
103
|
|
|
104
104
|
**Skipped vs Release Path:**
|
|
105
105
|
- No staged rollout (merge-and-deploy together).
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: common-writing-for-agents
|
|
3
|
+
version: 1
|
|
4
|
+
createdAt: 2026-10-13T00:00:00Z
|
|
5
|
+
updatedAt: 2026-10-13T00:00:00Z
|
|
6
|
+
createdBy: system
|
|
7
|
+
type: norm
|
|
8
|
+
role: all
|
|
9
|
+
category: quality
|
|
10
|
+
priority: 8
|
|
11
|
+
title: Writing for Agents
|
|
12
|
+
description: How to word skills, prompts, roles and SOPs so agents act on them at low context cost
|
|
13
|
+
triggers:
|
|
14
|
+
- write skill
|
|
15
|
+
- write prompt
|
|
16
|
+
- skill description
|
|
17
|
+
- author sop
|
|
18
|
+
- role prompt
|
|
19
|
+
- steering text
|
|
20
|
+
tags:
|
|
21
|
+
- authoring
|
|
22
|
+
- prompts
|
|
23
|
+
- skills
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
# Writing for Agents
|
|
27
|
+
|
|
28
|
+
Long form: mattpocock/skills `writing-for-agents`. Skill descriptions and always-loaded steering text cost context on every turn. Check new text against these six points. `skill-lint` (config/skills/_conventions) enforces the mechanical ones.
|
|
29
|
+
|
|
30
|
+
1. **Pointer wording decides when material is read.** Front-load the leading word of a description. One trigger per branch; synonyms of one trigger are one branch written twice. Cut identity the body already carries.
|
|
31
|
+
2. **End every step on a completion criterion.** The agent must be able to tell done from not-done. The best criteria are checkable and exhaustive ("the command exits 0", not "make sure it works").
|
|
32
|
+
3. **Say what to do, not what to avoid.** A prohibition puts the forbidden behavior into context and makes it more available. Prefer "Run the check, then report its exit code" over "NEVER report without checking".
|
|
33
|
+
4. **Do not restate the environment.** A doc that copies `package.json` scripts or config is a cache that goes stale. Cache only what cannot be found by looking; point to the file otherwise.
|
|
34
|
+
5. **Hunt no-ops.** If the model already does it by default, the sentence costs load and changes nothing. Keep a sentence only if removing it would change behavior (the same test for #877 rule cleanup).
|
|
35
|
+
6. **Keep emphasis rare.** Capitals and HARD RULE lose force when everything has them.
|
|
36
|
+
|
|
37
|
+
## Example (real skill: `agent/core/publish-app`)
|
|
38
|
+
|
|
39
|
+
Before (612 chars, why-it-works history and edge cases in the pointer):
|
|
40
|
+
|
|
41
|
+
> Publish a small web app (HTML/JS) you wrote to https://apps.crewlyai.com/<appId> so the owner can open it on their phone. Creates the app the first time, uploads a new version each time after (same app for the same directory), supports rollback, and can post an "Open app" card to the owner (...). Can ask the owner to make an app public, or to publish it as a template on the Crewly Marketplace (...). The owner's edits, comments on elements, and anonymous visitors' submissions on a public app, come back to you as an [APP CHANGES] message.
|
|
42
|
+
|
|
43
|
+
After (under 200 chars, one trigger, detail moves to the SKILL.md body):
|
|
44
|
+
|
|
45
|
+
> Publish an HTML/JS app you wrote to apps.crewlyai.com so the owner can open it on their phone. Also: new version, rollback, make public.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: dev-debugging
|
|
3
|
+
version: 1
|
|
4
|
+
createdAt: 2026-10-13T00:00:00Z
|
|
5
|
+
updatedAt: 2026-10-13T00:00:00Z
|
|
6
|
+
createdBy: system
|
|
7
|
+
type: sop
|
|
8
|
+
role: developer
|
|
9
|
+
category: debugging
|
|
10
|
+
priority: 9
|
|
11
|
+
title: Debugging
|
|
12
|
+
description: How to debug a failure — a red-capable loop first, ranked falsifiable hypotheses, tagged debug logs, cleanup
|
|
13
|
+
triggers:
|
|
14
|
+
- debug
|
|
15
|
+
- failing test
|
|
16
|
+
- bug
|
|
17
|
+
- regression
|
|
18
|
+
- flaky
|
|
19
|
+
- broken
|
|
20
|
+
- repro
|
|
21
|
+
tags:
|
|
22
|
+
- debugging
|
|
23
|
+
- testing
|
|
24
|
+
- quality
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
# Debugging
|
|
28
|
+
|
|
29
|
+
Long form: mattpocock/skills `diagnosing-bugs`. Redact secrets in every command and artifact you show.
|
|
30
|
+
|
|
31
|
+
## 1. Loop first
|
|
32
|
+
|
|
33
|
+
Name **one command** you have already run at least once that shows the user's exact symptom (red-capable), is deterministic, fast and runnable by you. If you are reading code to build a theory before this command exists, stop and build the command.
|
|
34
|
+
|
|
35
|
+
## 2. Reproduce and minimise
|
|
36
|
+
|
|
37
|
+
Cut inputs one at a time. Done when every remaining element is load-bearing.
|
|
38
|
+
|
|
39
|
+
## 3. Hypotheses
|
|
40
|
+
|
|
41
|
+
Write 3-5 ranked hypotheses before testing any. Each is falsifiable: "If X is the cause, then changing Y makes the bug disappear." If you cannot state the prediction, it is a guess. Test one at a time.
|
|
42
|
+
|
|
43
|
+
## 4. Instrument
|
|
44
|
+
|
|
45
|
+
Tag every debug log with a unique prefix, e.g. `[DEBUG-a4f2]`, so cleanup is one grep. Untagged logs survive; tagged logs die. If no correct seam exists to test the bug, that is itself the finding.
|
|
46
|
+
|
|
47
|
+
## 5. Fix with a regression test
|
|
48
|
+
|
|
49
|
+
Turn the loop into a test. See it fail first, then fix (see `testing-requirements`: red before green).
|
|
50
|
+
|
|
51
|
+
## 6. Clean up
|
|
52
|
+
|
|
53
|
+
`grep -rn "\[DEBUG-" .` returns nothing. The pre-commit hook rejects a staged `[DEBUG-` line.
|
|
@@ -85,6 +85,10 @@ git worktree remove /tmp/crewly-{ticket-name}-{owner}
|
|
|
85
85
|
|
|
86
86
|
**Solo work exception:** if you are the only developer active in the team and you're not parking work for parallel review, working directly in the main repo is acceptable. The worktree pattern is required only when there is a real risk of branch-state collision.
|
|
87
87
|
|
|
88
|
+
## Destructive Git Is Blocked
|
|
89
|
+
|
|
90
|
+
A hook blocks these in your Bash tool: force-push (`--force`, `-f`, `--force-with-lease`, `+ref`), any push to `main`/`master`, `reset --hard`, `clean -f`, `branch -D`, `checkout .`, `restore .`. Plain `git push origin feat/x` is fine. Instead: `git stash -u` before discarding, `branch -d` for merged branches, push a normal commit and open a PR. If you truly need one, ask your team lead. Limit: interpreter one-liners and variable-built commands are not seen.
|
|
91
|
+
|
|
88
92
|
## Before Pushing
|
|
89
93
|
|
|
90
94
|
1. Run `npm run typecheck`
|
|
@@ -56,6 +56,20 @@ describe('ComponentName', () => {
|
|
|
56
56
|
- No test interdependencies
|
|
57
57
|
- Clean up after tests
|
|
58
58
|
|
|
59
|
+
When a test fails and the cause is unclear, follow the `debugging` SOP (red-capable loop first).
|
|
60
|
+
|
|
61
|
+
## Anti-patterns
|
|
62
|
+
|
|
63
|
+
Coverage percentage rewards all three. Avoid them.
|
|
64
|
+
|
|
65
|
+
- **Tautological**: the assertion recomputes the expected value the way the code does (`expect(add(a, b)).toBe(a + b)`). Tell: it can never disagree with the code. Take expected values from a literal, a worked example or the spec. Prove a test is not tautological with the mutation check: break the code on purpose and see the test fail.
|
|
66
|
+
- **Implementation-coupled**: the test checks how (private calls, call order), not what. Tell: it breaks on a refactor that changes no behavior.
|
|
67
|
+
- **Horizontal slicing**: all tests first, then all code. Tell: many tests, none ever seen red. Work one test, one implementation, repeat. **Red before green**: see the new test fail before the fix.
|
|
68
|
+
|
|
69
|
+
## Seams
|
|
70
|
+
|
|
71
|
+
Test at agreed seams (public function, API route, CLI entry), not at every internal. For each seam write one line: what it catches and what it misses.
|
|
72
|
+
|
|
59
73
|
## Coverage Goals
|
|
60
74
|
|
|
61
75
|
- Minimum 80% for new code
|
|
@@ -1040,7 +1040,7 @@ export declare const CONTROL_PLANE_GUARD_CONSTANTS: {
|
|
|
1040
1040
|
/** Files under CREWLY_HOME that agents must not read with the built-in tools either. */
|
|
1041
1041
|
readonly CREWLY_HOME_READ_DENIED_FILES: readonly ["api-token"];
|
|
1042
1042
|
/** Write-protected directories under the install root (whole subtree). */
|
|
1043
|
-
readonly INSTALL_DIRS: readonly ["config/skills/orchestrator/stop-agent", "config/skills/orchestrator/start-agent", "config/skills/orchestrator/terminate-agent", "config/skills/orchestrator/stop-team", "config/skills/orchestrator/start-team", "config/skills/orchestrator/restart-crewly", "config/hooks/control-plane-guard", "config/hooks/agent-status", "config/hooks/subagent-guard", "config/hooks/credential-guard", "config/hooks/live-checkout-guard", "dist"];
|
|
1043
|
+
readonly INSTALL_DIRS: readonly ["config/skills/orchestrator/stop-agent", "config/skills/orchestrator/start-agent", "config/skills/orchestrator/terminate-agent", "config/skills/orchestrator/stop-team", "config/skills/orchestrator/start-team", "config/skills/orchestrator/restart-crewly", "config/hooks/control-plane-guard", "config/hooks/agent-status", "config/hooks/subagent-guard", "config/hooks/credential-guard", "config/hooks/live-checkout-guard", "config/hooks/destructive-git-guard", "dist"];
|
|
1044
1044
|
/** Write-protected files under the install root. */
|
|
1045
1045
|
readonly INSTALL_FILES: readonly ["config/skills/_common/lib.sh"];
|
|
1046
1046
|
/** Write-protected directories under the agent's project path (whole subtree). */
|
|
@@ -1063,6 +1063,21 @@ export declare const LIVE_CHECKOUT_GUARD_CONSTANTS: {
|
|
|
1063
1063
|
/** Codex tools the hook is attached to (shell, plus its patch tool). */
|
|
1064
1064
|
readonly CODEX_MATCHER: "Bash|apply_patch|Edit|Write";
|
|
1065
1065
|
};
|
|
1066
|
+
/**
|
|
1067
|
+
* Destructive-git guard: blocks force-push, push to main/master, reset --hard,
|
|
1068
|
+
* clean -f, branch -D, checkout . and restore . in an agent's Bash tool, so an
|
|
1069
|
+
* agent cannot destroy its own unmerged work.
|
|
1070
|
+
*/
|
|
1071
|
+
export declare const DESTRUCTIVE_GIT_GUARD_CONSTANTS: {
|
|
1072
|
+
/** Backend env var; `0` turns the guard off for sessions launched by this backend (the owner/TL override). */
|
|
1073
|
+
readonly KILL_SWITCH_ENV: "CREWLY_DESTRUCTIVE_GIT_GUARD";
|
|
1074
|
+
/** Value of KILL_SWITCH_ENV that disables the guard. */
|
|
1075
|
+
readonly KILL_SWITCH_OFF_VALUE: "0";
|
|
1076
|
+
/** The hook script (node), relative to the install root. */
|
|
1077
|
+
readonly HOOK_SCRIPT: "config/hooks/destructive-git-guard/guard.mjs";
|
|
1078
|
+
/** Claude Code tool the hook is attached to. */
|
|
1079
|
+
readonly CLAUDE_MATCHER: "Bash";
|
|
1080
|
+
};
|
|
1066
1081
|
/**
|
|
1067
1082
|
* Credential guard (specs/2026-10-04-agent-credential-isolation.md, layer 2):
|
|
1068
1083
|
* a pre-tool hook, on every runtime that has one, that refuses an agent's
|
|
@@ -1482,6 +1497,14 @@ export declare const TUI_INPUT_GUARD: {
|
|
|
1482
1497
|
readonly RULE_STALE_PREFIX_MIN_CHARS: 40;
|
|
1483
1498
|
/** Most status/footer lines allowed below an input box */
|
|
1484
1499
|
readonly FOOTER_MAX_LINES: 6;
|
|
1500
|
+
/**
|
|
1501
|
+
* Most rows below the bottom rule when searching for a Claude Code /
|
|
1502
|
+
* Antigravity box. Claude Code lists background agents and shells under
|
|
1503
|
+
* the box, so a busy session can have well over FOOTER_MAX_LINES rows
|
|
1504
|
+
* there (crewly#1113). Only the box search uses this; turn-signal reading
|
|
1505
|
+
* keeps FOOTER_MAX_LINES.
|
|
1506
|
+
*/
|
|
1507
|
+
readonly BOX_FOOTER_MAX_LINES: 14;
|
|
1485
1508
|
/** Most lines an input box may span */
|
|
1486
1509
|
readonly MAX_BOX_LINES: 60;
|
|
1487
1510
|
/**
|
|
@@ -3300,6 +3323,20 @@ export declare const ORC_CONVERSATION_CONSTANTS: {
|
|
|
3300
3323
|
/** Most of an earlier handover carried into a new one when the source conversation is unreadable */
|
|
3301
3324
|
readonly CHAINED_HANDOVER_MAX_CHARS: 16000;
|
|
3302
3325
|
};
|
|
3326
|
+
/** Pointer header (open work, suggested skills) written at the top of a handover file. */
|
|
3327
|
+
export declare const HANDOVER_POINTERS_CONSTANTS: {
|
|
3328
|
+
/** Task pool file under CREWLY_HOME (dir, file) */
|
|
3329
|
+
readonly POOL_DIR: "task-pool";
|
|
3330
|
+
readonly POOL_FILE: "pool.json";
|
|
3331
|
+
/** WorkItem statuses that count as still open for the agent */
|
|
3332
|
+
readonly OPEN_STATUSES: readonly string[];
|
|
3333
|
+
readonly MAX_OPEN_WORK: 15;
|
|
3334
|
+
readonly MAX_LINKS: 10;
|
|
3335
|
+
readonly TITLE_CHARS: 80;
|
|
3336
|
+
/** Markers around the header, so a chained handover can strip it */
|
|
3337
|
+
readonly START_MARK: "<!-- handover-pointers -->";
|
|
3338
|
+
readonly END_MARK: "<!-- /handover-pointers -->";
|
|
3339
|
+
};
|
|
3303
3340
|
/**
|
|
3304
3341
|
* Fresh conversation per new task for Claude Code team members (never the
|
|
3305
3342
|
* orchestrator): before a work item with a different root than the last one
|
|
@@ -5332,6 +5369,12 @@ export declare const CLOUD_SYNC_CONSTANTS: {
|
|
|
5332
5369
|
* backs off so a relay that is refusing (quota, outage) is not hammered.
|
|
5333
5370
|
*/
|
|
5334
5371
|
readonly REGISTER_RETRY_BACKOFF_MS: readonly number[];
|
|
5372
|
+
/**
|
|
5373
|
+
* Upper bound (ms) on honouring a relay 429 `Retry-After`. A daily-limit
|
|
5374
|
+
* 429 can ask for hours; we re-check at most this often so a limit that was
|
|
5375
|
+
* raised or reset is noticed without a restart.
|
|
5376
|
+
*/
|
|
5377
|
+
readonly RETRY_AFTER_MAX_MS: number;
|
|
5335
5378
|
/** Device considered offline after this threshold (ms) */
|
|
5336
5379
|
readonly OFFLINE_THRESHOLD_MS: 60000;
|
|
5337
5380
|
/** HTTP request timeout for sync API calls (ms) */
|
|
@@ -6799,6 +6842,12 @@ export declare const DECISION_CONSTANTS: {
|
|
|
6799
6842
|
* (specs/2026-10-02-decision-card-thread-answers.md §2) (ms)
|
|
6800
6843
|
*/
|
|
6801
6844
|
readonly WAIT_REMINDER_DELAY_MS: number;
|
|
6845
|
+
/**
|
|
6846
|
+
* `wait` default: a card still unanswered this long after its one reminder
|
|
6847
|
+
* (a lifted reply question: after the deadline notice, it never gets a
|
|
6848
|
+
* reminder) expires instead of staying open for days (#1243) (ms)
|
|
6849
|
+
*/
|
|
6850
|
+
readonly WAIT_EXPIRE_AFTER_REMINDER_MS: number;
|
|
6802
6851
|
/** Max characters of a withdrawn card's reason ("Closed — <reason>") */
|
|
6803
6852
|
readonly CLOSED_REASON_MAX_CHARS: 120;
|
|
6804
6853
|
/** Withdraw reasons the harness itself uses (cancelWhere notes) */
|
|
@@ -6882,6 +6931,18 @@ export declare const OPEN_ITEMS_CONSTANTS: {
|
|
|
6882
6931
|
readonly YES_LABEL: "Yes";
|
|
6883
6932
|
readonly NO_LABEL: "No";
|
|
6884
6933
|
readonly REPLY_LABEL: "Reply in thread";
|
|
6934
|
+
/** A lifted question shorter than this (characters) gets its subject prepended (#1242) */
|
|
6935
|
+
readonly QUESTION_MIN_CHARS: 20;
|
|
6936
|
+
/** Still shorter than this after the subject was added (characters): too vague for a card, tracked without one */
|
|
6937
|
+
readonly QUESTION_VAGUE_CHARS: 12;
|
|
6938
|
+
/** Suffix of the option the agent recommended */
|
|
6939
|
+
readonly RECOMMENDED_SUFFIX: " (recommended)";
|
|
6940
|
+
/** A sentence that recommends something ("我推荐 A", "I recommend B") */
|
|
6941
|
+
readonly RECOMMENDATION_PATTERN: RegExp;
|
|
6942
|
+
/** A recommendation that argues against doing it ("不建议", "don't recommend") */
|
|
6943
|
+
readonly RECOMMENDATION_NEGATIVE_PATTERN: RegExp;
|
|
6944
|
+
/** A project ticket id named in a promise ("CE-16"); the work done under it fulfils the promise wherever it ran (crewly#1037) */
|
|
6945
|
+
readonly PROJECT_TICKET_REF_PATTERN: RegExp;
|
|
6885
6946
|
/** `WorkItem.metadata` key of a follow-up WorkItem */
|
|
6886
6947
|
readonly FOLLOW_UP_METADATA_KEY: "openItemFollowUp";
|
|
6887
6948
|
/**
|
|
@@ -7679,6 +7740,11 @@ export declare const OWNER_THREAD_SENTINEL_CONSTANTS: {
|
|
|
7679
7740
|
readonly DEADLINE_FACTOR: 1.5;
|
|
7680
7741
|
/** Floor of a promise deadline (ms) */
|
|
7681
7742
|
readonly MIN_DEADLINE_MS: number;
|
|
7743
|
+
/**
|
|
7744
|
+
* A promise with no stated time ("做好发到 #x", "I'll post the result") is
|
|
7745
|
+
* overdue after this much silence from the agent in the thread (#1240) (ms)
|
|
7746
|
+
*/
|
|
7747
|
+
readonly NO_ETA_SILENCE_MS: number;
|
|
7682
7748
|
/** Longest promise the sentinel times (longer ones are not timed) */
|
|
7683
7749
|
readonly MAX_PROMISE_MINUTES: number;
|
|
7684
7750
|
/** Slot freeing / idle stop spare an agent whose owner message or promise is this recent */
|
|
@@ -8283,4 +8349,22 @@ export declare const CANVAS_CONSTANTS: {
|
|
|
8283
8349
|
/** A Chrome session counts as the agent's browser while it was active this recently (ms) */
|
|
8284
8350
|
readonly BROWSER_ACTIVE_MS: number;
|
|
8285
8351
|
};
|
|
8352
|
+
/**
|
|
8353
|
+
* Hand-off of an owner-approved change to a recurring job to the agent that
|
|
8354
|
+
* runs it (crewly#1137).
|
|
8355
|
+
*/
|
|
8356
|
+
export declare const RECURRING_CHANGE_HANDOFF_CONSTANTS: {
|
|
8357
|
+
/** Share of the schedule's name/title terms the decision text must contain */
|
|
8358
|
+
readonly MIN_COVERAGE: 0.6;
|
|
8359
|
+
/** Distinct terms the decision and the schedule must share */
|
|
8360
|
+
readonly MIN_SHARED_TERMS: 2;
|
|
8361
|
+
/** ...or this many shared terms whatever the coverage (labels mixing two languages) */
|
|
8362
|
+
readonly SURE_SHARED_TERMS: 3;
|
|
8363
|
+
/** Terms shorter than this (Latin words) are noise */
|
|
8364
|
+
readonly MIN_WORD_CHARS: 3;
|
|
8365
|
+
/** `WorkItem.metadata` marker on the hand-off WorkItem */
|
|
8366
|
+
readonly METADATA_KEY: "recurringChangeHandoff";
|
|
8367
|
+
/** Option labels / answers that decline a change (nothing to hand off) */
|
|
8368
|
+
readonly DECLINE_PATTERN: RegExp;
|
|
8369
|
+
};
|
|
8286
8370
|
//# sourceMappingURL=constants.d.ts.map
|