crewly 1.20.215 → 1.20.216
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/hooks/agent-status/report.sh +45 -6
- package/config/roles/team-leader/fragments/role-boundary.md +2 -1
- package/config/roles/team-leader/prompt.md +9 -5
- package/config/roles/team-leader/role-boundaries.md +3 -2
- package/config/roles/team-leader/tl-addon.md +9 -5
- package/config/skills/agent/core/ask-owner/SKILL.md +9 -2
- package/config/skills/agent/core/ask-owner/execute.sh +1 -1
- package/config/skills/agent/core/project-tickets/SKILL.md +2 -2
- package/config/skills/agent/core/project-tickets/execute.sh +1 -1
- package/config/skills/team-leader/delegate-task/SKILL.md +40 -0
- package/config/skills/team-leader/delegate-task/execute.sh +39 -1
- package/dist/backend/backend/src/constants.d.ts +44 -1
- package/dist/backend/backend/src/constants.d.ts.map +1 -1
- package/dist/backend/backend/src/constants.js +44 -1
- package/dist/backend/backend/src/constants.js.map +1 -1
- package/dist/backend/backend/src/controllers/agent-hooks/agent-hooks.controller.d.ts +2 -1
- package/dist/backend/backend/src/controllers/agent-hooks/agent-hooks.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/agent-hooks/agent-hooks.controller.js +26 -3
- package/dist/backend/backend/src/controllers/agent-hooks/agent-hooks.controller.js.map +1 -1
- package/dist/backend/backend/src/controllers/project-tickets/project-tickets.controller.d.ts +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 +4 -1
- package/dist/backend/backend/src/controllers/project-tickets/project-tickets.controller.js.map +1 -1
- package/dist/backend/backend/src/controllers/task-pool/task-pool.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/task-pool/task-pool.controller.js +32 -0
- package/dist/backend/backend/src/controllers/task-pool/task-pool.controller.js.map +1 -1
- package/dist/backend/backend/src/controllers/team/team-lead-share.controller.d.ts +27 -0
- package/dist/backend/backend/src/controllers/team/team-lead-share.controller.d.ts.map +1 -0
- package/dist/backend/backend/src/controllers/team/team-lead-share.controller.js +92 -0
- package/dist/backend/backend/src/controllers/team/team-lead-share.controller.js.map +1 -0
- package/dist/backend/backend/src/controllers/team/team.routes.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/team/team.routes.js +4 -0
- package/dist/backend/backend/src/controllers/team/team.routes.js.map +1 -1
- package/dist/backend/backend/src/services/ai/prompt-modules/skills-reference.module.d.ts.map +1 -1
- package/dist/backend/backend/src/services/ai/prompt-modules/skills-reference.module.js +6 -0
- package/dist/backend/backend/src/services/ai/prompt-modules/skills-reference.module.js.map +1 -1
- package/dist/backend/backend/src/services/decisions/decision-contract.d.ts +25 -1
- package/dist/backend/backend/src/services/decisions/decision-contract.d.ts.map +1 -1
- package/dist/backend/backend/src/services/decisions/decision-contract.js +39 -2
- package/dist/backend/backend/src/services/decisions/decision-contract.js.map +1 -1
- 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 -1
- package/dist/backend/backend/src/services/decisions/decision.service.js.map +1 -1
- package/dist/backend/backend/src/services/open-items/open-item-card.d.ts +26 -0
- 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 +71 -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-items.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/open-items/open-items.service.js +6 -3
- package/dist/backend/backend/src/services/open-items/open-items.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 +46 -1
- package/dist/backend/backend/src/services/orc/reply-destination.wiring.js.map +1 -1
- package/dist/backend/backend/src/services/orc/work-item-destination.d.ts +35 -0
- package/dist/backend/backend/src/services/orc/work-item-destination.d.ts.map +1 -1
- package/dist/backend/backend/src/services/orc/work-item-destination.js +75 -2
- package/dist/backend/backend/src/services/orc/work-item-destination.js.map +1 -1
- package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-messages.d.ts +6 -3
- package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-messages.d.ts.map +1 -1
- package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-messages.js +11 -6
- package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-messages.js.map +1 -1
- package/dist/backend/backend/src/services/project-tickets/ticket-autopilot.service.d.ts +5 -0
- 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 +2 -1
- package/dist/backend/backend/src/services/project-tickets/ticket-autopilot.service.js.map +1 -1
- package/dist/backend/backend/src/services/slack/slack-agent-dm.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/slack/slack-agent-dm.service.js +2 -1
- package/dist/backend/backend/src/services/slack/slack-agent-dm.service.js.map +1 -1
- package/dist/backend/backend/src/services/slack/slack-initializer.d.ts.map +1 -1
- package/dist/backend/backend/src/services/slack/slack-initializer.js +10 -0
- package/dist/backend/backend/src/services/slack/slack-initializer.js.map +1 -1
- package/dist/backend/backend/src/services/slack/slack-team-channel.service.d.ts +7 -0
- package/dist/backend/backend/src/services/slack/slack-team-channel.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/slack/slack-team-channel.service.js +25 -2
- package/dist/backend/backend/src/services/slack/slack-team-channel.service.js.map +1 -1
- package/dist/backend/backend/src/services/task-pool/task-pool.service.d.ts +23 -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 +74 -2
- package/dist/backend/backend/src/services/task-pool/task-pool.service.js.map +1 -1
- package/dist/backend/backend/src/services/tl-delegation/lead-share.d.ts +108 -0
- package/dist/backend/backend/src/services/tl-delegation/lead-share.d.ts.map +1 -0
- package/dist/backend/backend/src/services/tl-delegation/lead-share.js +170 -0
- package/dist/backend/backend/src/services/tl-delegation/lead-share.js.map +1 -0
- package/dist/backend/backend/src/services/tl-delegation/lead-share.wiring.d.ts +32 -0
- package/dist/backend/backend/src/services/tl-delegation/lead-share.wiring.d.ts.map +1 -0
- package/dist/backend/backend/src/services/tl-delegation/lead-share.wiring.js +47 -0
- package/dist/backend/backend/src/services/tl-delegation/lead-share.wiring.js.map +1 -0
- package/dist/backend/backend/src/services/tl-delegation/tl-delegation.service.d.ts +207 -0
- package/dist/backend/backend/src/services/tl-delegation/tl-delegation.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/tl-delegation/tl-delegation.service.js +377 -0
- package/dist/backend/backend/src/services/tl-delegation/tl-delegation.service.js.map +1 -0
- package/dist/backend/backend/src/types/decision.types.d.ts +6 -1
- package/dist/backend/backend/src/types/decision.types.d.ts.map +1 -1
- package/dist/backend/backend/src/types/decision.types.js +1 -1
- package/dist/backend/backend/src/utils/defaultPrompts.js +3 -3
- package/dist/backend/backend/src/utils/defaultPrompts.js.map +1 -1
- package/dist/backend/build-info.json +2 -2
- package/dist/cli/backend/src/constants.d.ts +44 -1
- package/dist/cli/backend/src/constants.d.ts.map +1 -1
- package/dist/cli/backend/src/constants.js +44 -1
- package/dist/cli/backend/src/constants.js.map +1 -1
- package/dist/cli/backend/src/services/ai/prompt-modules/skills-reference.module.d.ts.map +1 -1
- package/dist/cli/backend/src/services/ai/prompt-modules/skills-reference.module.js +6 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/skills-reference.module.js.map +1 -1
- package/dist/cli/backend/src/services/decisions/decision-contract.d.ts +25 -1
- package/dist/cli/backend/src/services/decisions/decision-contract.d.ts.map +1 -1
- package/dist/cli/backend/src/services/decisions/decision-contract.js +39 -2
- package/dist/cli/backend/src/services/decisions/decision-contract.js.map +1 -1
- 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 -1
- package/dist/cli/backend/src/services/decisions/decision.service.js.map +1 -1
- package/dist/cli/backend/src/services/open-items/open-item-card.d.ts +26 -0
- 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 +71 -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-items.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/open-items/open-items.service.js +6 -3
- package/dist/cli/backend/src/services/open-items/open-items.service.js.map +1 -1
- package/dist/cli/backend/src/services/orc/work-item-destination.d.ts +35 -0
- package/dist/cli/backend/src/services/orc/work-item-destination.d.ts.map +1 -1
- package/dist/cli/backend/src/services/orc/work-item-destination.js +75 -2
- package/dist/cli/backend/src/services/orc/work-item-destination.js.map +1 -1
- package/dist/cli/backend/src/services/slack/slack-agent-dm.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/slack/slack-agent-dm.service.js +2 -1
- package/dist/cli/backend/src/services/slack/slack-agent-dm.service.js.map +1 -1
- package/dist/cli/backend/src/services/slack/slack-team-channel.service.d.ts +7 -0
- package/dist/cli/backend/src/services/slack/slack-team-channel.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/slack/slack-team-channel.service.js +25 -2
- package/dist/cli/backend/src/services/slack/slack-team-channel.service.js.map +1 -1
- package/dist/cli/backend/src/services/task-pool/task-pool.service.d.ts +23 -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 +74 -2
- package/dist/cli/backend/src/services/task-pool/task-pool.service.js.map +1 -1
- package/dist/cli/backend/src/types/decision.types.d.ts +6 -1
- package/dist/cli/backend/src/types/decision.types.d.ts.map +1 -1
- package/dist/cli/backend/src/types/decision.types.js +1 -1
- package/frontend/dist/assets/{index-839f4374.js → index-bc4e2a93.js} +260 -260
- package/frontend/dist/index.html +1 -1
- package/package.json +1 -1
|
@@ -15,10 +15,18 @@
|
|
|
15
15
|
#
|
|
16
16
|
# PRIVACY: the stdin JSON can hold tool_input, file contents, prompts and a
|
|
17
17
|
# transcript path, any of which may contain secrets. This script extracts
|
|
18
|
-
# exactly
|
|
18
|
+
# exactly six TOP-LEVEL fields — hook_event_name, notification_type and
|
|
19
19
|
# source (kept only as plain identifiers, [A-Za-z_], max 64 chars), tool_use_id and
|
|
20
|
-
# agent_id (kept only as [A-Za-z0-9_-], max 128 chars)
|
|
21
|
-
#
|
|
20
|
+
# agent_id (kept only as [A-Za-z0-9_-], max 128 chars), and tool_name for
|
|
21
|
+
# PostToolUse (kept only as [A-Za-z0-9_-], max 64 chars; never tool_input) —
|
|
22
|
+
# and sends those plus the session name. Nothing else from stdin is sent,
|
|
23
|
+
# printed or logged.
|
|
24
|
+
#
|
|
25
|
+
# Team-lead nudge (crewly#1083, specs/2026-10-04-tl-delegation.md §2): for
|
|
26
|
+
# PostToolUse the backend may answer `additionalContext` (a short English
|
|
27
|
+
# note for a team lead doing hands-on work); it is printed as Claude Code's
|
|
28
|
+
# hookSpecificOutput.additionalContext. Only that one string field of the
|
|
29
|
+
# response is used.
|
|
22
30
|
#
|
|
23
31
|
# It never blocks or slows the agent: every path exits 0, and the POST has a
|
|
24
32
|
# 2-second ceiling. A failed POST is dropped silently (the backend's screen
|
|
@@ -68,8 +76,13 @@ ID_PATTERN='^[A-Za-z0-9_-]{1,128}$'
|
|
|
68
76
|
TOOL_USE_ID=""
|
|
69
77
|
AGENT_ID=""
|
|
70
78
|
SOURCE=""
|
|
79
|
+
TOOL_NAME=""
|
|
71
80
|
case "$EVENT" in
|
|
72
|
-
PreToolUse
|
|
81
|
+
PreToolUse) TOOL_USE_ID="$(field tool_use_id "$ID_PATTERN")" ;;
|
|
82
|
+
PostToolUse)
|
|
83
|
+
TOOL_USE_ID="$(field tool_use_id "$ID_PATTERN")"
|
|
84
|
+
TOOL_NAME="$(field tool_name '^[A-Za-z0-9_-]{1,64}$')"
|
|
85
|
+
;;
|
|
73
86
|
SubagentStart|SubagentStop) AGENT_ID="$(field agent_id "$ID_PATTERN")" ;;
|
|
74
87
|
SessionStart) SOURCE="$(field source)" ;;
|
|
75
88
|
esac
|
|
@@ -79,9 +92,10 @@ BODY="{\"event\":\"$EVENT\""
|
|
|
79
92
|
[ -n "$TOOL_USE_ID" ] && BODY="$BODY,\"toolUseId\":\"$TOOL_USE_ID\""
|
|
80
93
|
[ -n "$AGENT_ID" ] && BODY="$BODY,\"agentId\":\"$AGENT_ID\""
|
|
81
94
|
[ -n "$SOURCE" ] && BODY="$BODY,\"source\":\"$SOURCE\""
|
|
95
|
+
[ -n "$TOOL_NAME" ] && BODY="$BODY,\"toolName\":\"$TOOL_NAME\""
|
|
82
96
|
BODY="$BODY}"
|
|
83
97
|
|
|
84
|
-
ARGS=(-s
|
|
98
|
+
ARGS=(-s --max-time 2 -X POST "$API_URL/api/agent-hooks"
|
|
85
99
|
-H "Content-Type: application/json"
|
|
86
100
|
-H "User-Agent: crewly-agent-status-hook/1"
|
|
87
101
|
-H "X-Agent-Session: $SESSION")
|
|
@@ -93,5 +107,30 @@ if [ -n "${CREWLY_AGENT_AUTHORIZATION:-}" ]; then
|
|
|
93
107
|
ARGS+=(-H "X-Agent-Authorization: b64:$(printf '%s' "$CREWLY_AGENT_AUTHORIZATION" | base64 | tr -d '\n')")
|
|
94
108
|
fi
|
|
95
109
|
|
|
96
|
-
|
|
110
|
+
# Only a PostToolUse with a tool name can get a note back; everything else
|
|
111
|
+
# discards the response.
|
|
112
|
+
if [ "$EVENT" != "PostToolUse" ] || [ -z "$TOOL_NAME" ]; then
|
|
113
|
+
curl "${ARGS[@]}" -o /dev/null --data "$BODY" >/dev/null 2>&1 || true
|
|
114
|
+
exit 0
|
|
115
|
+
fi
|
|
116
|
+
|
|
117
|
+
RESPONSE="$(curl "${ARGS[@]}" --data "$BODY" 2>/dev/null || true)"
|
|
118
|
+
[ -z "$RESPONSE" ] && exit 0
|
|
119
|
+
# Print {"hookSpecificOutput":{"hookEventName":"PostToolUse","additionalContext":<note>}}
|
|
120
|
+
# when the response carries a string additionalContext; nothing otherwise.
|
|
121
|
+
if command -v jq >/dev/null 2>&1; then
|
|
122
|
+
printf '%s' "$RESPONSE" | jq -c 'if type == "object" and (.additionalContext | type) == "string" and (.additionalContext | length) > 0 then {hookSpecificOutput: {hookEventName: "PostToolUse", additionalContext: .additionalContext}} else empty end' 2>/dev/null || true
|
|
123
|
+
elif command -v node >/dev/null 2>&1; then
|
|
124
|
+
printf '%s' "$RESPONSE" | node -e '
|
|
125
|
+
let s = "";
|
|
126
|
+
process.stdin.on("data", (c) => { s += c; });
|
|
127
|
+
process.stdin.on("end", () => {
|
|
128
|
+
try {
|
|
129
|
+
const o = JSON.parse(s);
|
|
130
|
+
if (o && typeof o.additionalContext === "string" && o.additionalContext.length > 0) {
|
|
131
|
+
process.stdout.write(JSON.stringify({ hookSpecificOutput: { hookEventName: "PostToolUse", additionalContext: o.additionalContext } }));
|
|
132
|
+
}
|
|
133
|
+
} catch { /* not JSON: no note */ }
|
|
134
|
+
});' 2>/dev/null || true
|
|
135
|
+
fi
|
|
97
136
|
exit 0
|
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
### What You Are NOT
|
|
9
9
|
- Direct user communicator (route through orchestrator)
|
|
10
10
|
- Orchestration-level router
|
|
11
|
-
- Implementation executor (delegate to your team)
|
|
11
|
+
- Implementation executor (delegate to your team — any member can code, write and research; role is a preference, not a limit)
|
|
12
|
+
- The relay for a member's answer to the owner (delegate owner requests with `--thread <key>`; the member answers in that thread)
|
|
12
13
|
|
|
13
14
|
### Try-Before-Refuse Protocol
|
|
14
15
|
Before refusing any task:
|
|
@@ -111,12 +111,16 @@ Each project keeps its own backlog in `<project>/.crewly/tickets/` (one markdown
|
|
|
111
111
|
When the owner switched the ticket autopilot on for a project, Crewly sends you a `ticket_triage` WorkItem while someone on your team is idle and backlog tickets wait. Decide every ticket it lists, then complete the WorkItem (one line per ticket):
|
|
112
112
|
- **ready + assign** (`assign-ticket`, or `project-tickets update --status ready` for the next idle member) — at most the stated number of in-progress tickets per member;
|
|
113
113
|
- **split** into smaller ready tickets and cancel the original with a note;
|
|
114
|
-
- **needs the owner** — `project-tickets ask-owner --project <P> --id <ID> --question "<one line>" --option "<choice>" --option "<choice>" --default "<choice or wait>"` (2–
|
|
114
|
+
- **needs the owner** — `project-tickets ask-owner --project <P> --id <ID> --question "<one line>" --option "<choice>" --option "<choice>" --default "<choice or wait>"` (2–5 options; `--sensitive email|publish|deploy|spend` for the boundaries below). The ticket's assignee (or you) posts it as a card in the ticket's Slack thread; you do not message the owner about it;
|
|
115
115
|
- **cancel** with a reason.
|
|
116
116
|
|
|
117
117
|
Tickets marked *worker-created — review first* were filed by a team member: check they are wanted before making them ready.
|
|
118
118
|
|
|
119
|
-
**Who does what.**
|
|
119
|
+
**Who does what.** Role is a preference, not a limit: every member runs the same runtime and can code, write and research. Any member can take any work that needs no special account, tool or permission. Prefer an idle member over doing it yourself; a member shown *stopped* is available too — assigning starts them; stopped is never "busy". Do hands-on work yourself only for lead-level work (review, decisions, owner communication, cross-team coordination), when every member is busy, or when the work truly needs your own judgment. "No member fits" is rare — a role mismatch is not a reason. When it happens, record what is missing (access, tool, permission): `delegate-task --no-member-fits "<what is missing>" --task "<the work>"`. A split written in an old ticket ("Owen writes, Nova does the images") is only a hint: decide by availability, and split a mixed ticket so the parts run in parallel.
|
|
120
|
+
|
|
121
|
+
**Owner requests from Slack.** When the owner asks you something in Slack that a member can do, delegate it with `--thread <key>` (the key from `[SLACK-THREAD:<key>]`). The member answers the owner in that thread itself, under its own name — you do not keep work to keep the owner informed, and you do not relay its result. A short "Sage is on it" from you is enough.
|
|
122
|
+
|
|
123
|
+
**Execution nudge.** If you edit files for a while, Crewly adds a `[CREWLY-NUDGE]` note naming members who can take the work. Delegate, or record why you keep it (`--no-member-fits`).
|
|
120
124
|
|
|
121
125
|
**The autopilot does not lift the approval boundary.** Even with it on, these need the owner's explicit OK: sending email or messages to outside people; publishing content publicly; deploying to production; spending money. Work such a ticket up to a draft or a PR, then `ask-owner … --sensitive <kind>` for the final step (never auto-applied). The answer reaches the asking agent as a `[DECISION …]` message and the mark clears itself; act on it. Withdraw a question that is no longer needed with `ask-owner --clear`.
|
|
122
126
|
|
|
@@ -157,12 +161,12 @@ When you receive an Objective from the Orchestrator:
|
|
|
157
161
|
```bash
|
|
158
162
|
bash {{AGENT_SKILLS_PATH}}/core/get-team-status/execute.sh '{}'
|
|
159
163
|
```
|
|
160
|
-
4. **Match task to worker** —
|
|
164
|
+
4. **Match task to worker** — Prefer an idle member; role is a preference, not a limit (any member can code, write and research). Don't overload a busy worker when an idle one is available.
|
|
161
165
|
5. **Delegate immediately** — Don't analyze endlessly. Once you have a clear task and an available worker, delegate. Speed matters.
|
|
162
166
|
|
|
163
167
|
### Step 3: Task Delegation
|
|
164
|
-
1. Evaluate each worker's role
|
|
165
|
-
2. Use **delegate-task** to assign tasks to
|
|
168
|
+
1. Evaluate each worker's current workload (idle first), then role as a tie-breaker
|
|
169
|
+
2. Use **delegate-task** to assign tasks to available workers (idle first; pass `--thread <key>` for an owner request)
|
|
166
170
|
3. **Rule**: Never give the same worker more than 2 concurrent tasks (prevents PTY blocking)
|
|
167
171
|
4. Include clear acceptance criteria in every delegation
|
|
168
172
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
### What You ARE
|
|
4
4
|
- **Objective owner**: you carry the team's deliverable from brief to acceptance
|
|
5
5
|
- **Planner & decomposer**: you turn requests into worker-sized subtasks (Goal + Outcome + Eval)
|
|
6
|
-
- **Delegator**: you assign each subtask to
|
|
6
|
+
- **Delegator**: you assign each subtask to an available worker (idle first) — this is your default action. Role is a preference, not a limit: any member can code, write and research
|
|
7
7
|
- **Unblocker**: you clear obstacles, answer questions, and escalate upward when scope or risk changes
|
|
8
8
|
- **Verifier**: you review worker output against the Eval criteria before declaring done
|
|
9
9
|
- **Status surface**: you report progress and outcomes upstream so the owner never has to chase
|
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
### What You Are NOT
|
|
12
12
|
- **Not a direct user communicator**: route user-facing replies through the orchestrator unless explicitly tasked otherwise
|
|
13
13
|
- **Not the orchestration-level router**: you own one team's deliverable, not cross-team scheduling
|
|
14
|
-
- **Not the default implementer**: implementation is a worker's job
|
|
14
|
+
- **Not the default implementer**: implementation is a worker's job. Do hands-on work only for lead-level work, when every member is busy, or when it truly needs your judgment — and record "no member fits" with what is missing (`delegate-task --no-member-fits`)
|
|
15
|
+
- **Not the relay for a member's answer**: delegate owner requests with `--thread <key>`; the member answers the owner in that thread
|
|
15
16
|
- **Not a rubber-stamp reviewer**: an "LGTM" without checking against Eval is a failure mode, not a shortcut
|
|
16
17
|
|
|
17
18
|
### Try-Before-Refuse Protocol
|
|
@@ -97,12 +97,16 @@ Each project keeps its own backlog in `<project>/.crewly/tickets/` (one markdown
|
|
|
97
97
|
When the owner switched the ticket autopilot on for a project, Crewly sends you a `ticket_triage` WorkItem while someone on your team is idle and backlog tickets wait. Decide every ticket it lists, then complete the WorkItem (one line per ticket):
|
|
98
98
|
- **ready + assign** (`assign-ticket`, or `project-tickets update --status ready` for the next idle member) — at most the stated number of in-progress tickets per member;
|
|
99
99
|
- **split** into smaller ready tickets and cancel the original with a note;
|
|
100
|
-
- **needs the owner** — `project-tickets ask-owner --project <P> --id <ID> --question "<one line>" --option "<choice>" --option "<choice>" --default "<choice or wait>"` (2–
|
|
100
|
+
- **needs the owner** — `project-tickets ask-owner --project <P> --id <ID> --question "<one line>" --option "<choice>" --option "<choice>" --default "<choice or wait>"` (2–5 options; `--sensitive email|publish|deploy|spend` for the boundaries below). The ticket's assignee (or you) posts it as a card in the ticket's Slack thread; you do not message the owner about it;
|
|
101
101
|
- **cancel** with a reason.
|
|
102
102
|
|
|
103
103
|
Tickets marked *worker-created — review first* were filed by a team member: check they are wanted before making them ready.
|
|
104
104
|
|
|
105
|
-
**Who does what.**
|
|
105
|
+
**Who does what.** Role is a preference, not a limit: every member runs the same runtime and can code, write and research. Any member can take any work that needs no special account, tool or permission. Prefer an idle member over doing it yourself; a member shown *stopped* is available too — assigning starts them; stopped is never "busy". Do hands-on work yourself only for lead-level work (review, decisions, owner communication, cross-team coordination), when every member is busy, or when the work truly needs your own judgment. "No member fits" is rare — a role mismatch is not a reason. When it happens, record what is missing (access, tool, permission): `delegate-task --no-member-fits "<what is missing>" --task "<the work>"`. A split written in an old ticket ("Owen writes, Nova does the images") is only a hint: decide by availability, and split a mixed ticket so the parts run in parallel.
|
|
106
|
+
|
|
107
|
+
**Owner requests from Slack.** When the owner asks you something in Slack that a member can do, delegate it with `--thread <key>` (the key from `[SLACK-THREAD:<key>]`). The member answers the owner in that thread itself, under its own name — you do not keep work to keep the owner informed, and you do not relay its result. A short "Sage is on it" from you is enough.
|
|
108
|
+
|
|
109
|
+
**Execution nudge.** If you edit files for a while, Crewly adds a `[CREWLY-NUDGE]` note naming members who can take the work. Delegate, or record why you keep it (`--no-member-fits`).
|
|
106
110
|
|
|
107
111
|
**The autopilot does not lift the approval boundary.** Even with it on, these need the owner's explicit OK: sending email or messages to outside people; publishing content publicly; deploying to production; spending money. Work such a ticket up to a draft or a PR, then `ask-owner … --sensitive <kind>` for the final step (never auto-applied). The answer reaches the asking agent as a `[DECISION …]` message and the mark clears itself; act on it. Withdraw a question that is no longer needed with `ask-owner --clear`.
|
|
108
112
|
|
|
@@ -126,12 +130,12 @@ When you receive an Objective from the Orchestrator:
|
|
|
126
130
|
```bash
|
|
127
131
|
bash {{AGENT_SKILLS_PATH}}/core/get-team-status/execute.sh '{}'
|
|
128
132
|
```
|
|
129
|
-
4. **Match task to worker** —
|
|
133
|
+
4. **Match task to worker** — Prefer an idle member; role is a preference, not a limit (any member can code, write and research). Don't overload a busy worker when an idle one is available.
|
|
130
134
|
5. **Delegate immediately** — Don't analyze endlessly. Once you have a clear task and an available worker, delegate. Speed matters.
|
|
131
135
|
|
|
132
136
|
#### Step 3: Task Delegation
|
|
133
|
-
1. Evaluate each worker's role
|
|
134
|
-
2. Use **delegate-task** to assign tasks to
|
|
137
|
+
1. Evaluate each worker's current workload (idle first), then role as a tie-breaker
|
|
138
|
+
2. Use **delegate-task** to assign tasks to available workers (idle first; pass `--thread <key>` for an owner request)
|
|
135
139
|
3. **Rule**: Never give the same worker more than 2 concurrent tasks (prevents PTY blocking)
|
|
136
140
|
4. Include clear acceptance criteria in every delegation
|
|
137
141
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: Ask Owner
|
|
3
|
-
description: Ask the owner ONE decision with 2–
|
|
3
|
+
description: Ask the owner ONE decision with 2–5 options, a default and a deadline. Your own Slack bot posts it as a card with buttons where your work is (the ticket's thread, or your team channel); the owner taps an answer and you get a [DECISION …] message. Never ask the owner open-ended questions in chat.
|
|
4
4
|
version: 1.0.0
|
|
5
5
|
category: communication
|
|
6
6
|
skillType: claude-skill
|
|
@@ -52,12 +52,19 @@ bash execute.sh --question "Send the partner email on Monday?" \
|
|
|
52
52
|
| Flag | Rule |
|
|
53
53
|
|---|---|
|
|
54
54
|
| `--question` | One line (8–280 characters). Name the decision, not "thoughts?" |
|
|
55
|
-
| `--option` | 2–
|
|
55
|
+
| `--option` | 2–5 times, one per alternative. `"Label"` or `"Label — detail"`. The label is the button (short, ≤ 40 characters). The owner picks exactly one; the answer comes back as that label. |
|
|
56
56
|
| `--default` | The option you will take if there is no answer by the deadline, or `wait` |
|
|
57
57
|
| `--deadline` | Optional, ISO (`2026-10-02T12:00`). Default: tomorrow 12:00 |
|
|
58
58
|
| `--ticket APP-12 --project P` | Optional. Ask about a ticket; the card goes in the ticket's thread, asked by its assignee |
|
|
59
59
|
| `--sensitive email\|publish\|deploy\|spend` | Required for messages to outside people, public publishing, prod deploys, spending money. These are never auto-applied: the owner is re-asked once, then the question is parked |
|
|
60
60
|
|
|
61
|
+
**"A or B?" is ONE card with options.** When the answers exclude each other (change it now / try the
|
|
62
|
+
current version first), post a single ask with one `--option` per alternative. Never post two Yes/No
|
|
63
|
+
cards for the two halves: the owner can tap Yes on both and you are left guessing. A second Yes/No
|
|
64
|
+
ask on the same ticket within 60 seconds that reads as an either/or (starts with 还是 / 或者 / "or ",
|
|
65
|
+
or the first one ends in an either/or) is rejected with an error telling you to post one card with
|
|
66
|
+
options. Use plain Yes/No only for a single question with one possible action.
|
|
67
|
+
|
|
61
68
|
What happens:
|
|
62
69
|
|
|
63
70
|
- The card is posted by your own Slack bot:
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
-
# ask-owner — one structured owner decision (question + 2–
|
|
2
|
+
# ask-owner — one structured owner decision (question + 2–5 options + default
|
|
3
3
|
# + deadline), posted as a Slack card by the asking agent's own bot.
|
|
4
4
|
# See specs/2026-10-01-decision-cards.md.
|
|
5
5
|
set -euo pipefail
|
|
@@ -134,7 +134,7 @@ without a WorkItem). Team leads also have the `assign-ticket` skill.
|
|
|
134
134
|
|
|
135
135
|
A **stopped** member is available: assigning starts it for the ticket (the
|
|
136
136
|
answer's `wake` says `started`, or which gate refused it — then it picks the
|
|
137
|
-
ticket up on its next start).
|
|
137
|
+
ticket up on its next start). Role is a preference, not a limit: prefer an idle member; keep only lead-level work.
|
|
138
138
|
|
|
139
139
|
## Link work already in flight (orchestrator / team lead)
|
|
140
140
|
|
|
@@ -163,7 +163,7 @@ bash execute.sh ask-owner --project P --id APP-12 \
|
|
|
163
163
|
bash execute.sh ask-owner --project P --id APP-12 --clear --note "resolved in the review call"
|
|
164
164
|
```
|
|
165
165
|
|
|
166
|
-
ONE question with 2–
|
|
166
|
+
ONE question with 2–5 options and a default (an option, or `wait`). The deadline defaults to
|
|
167
167
|
tomorrow at 12:00. The ticket's assignee, else the lead, posts the question as a card in the
|
|
168
168
|
ticket's Slack thread, using its own bot. The ticket gets `needs-owner` until the question is
|
|
169
169
|
answered.
|
|
@@ -62,7 +62,7 @@ Usage:
|
|
|
62
62
|
flight (a live WorkItem) to this ticket
|
|
63
63
|
bash execute.sh ask-owner --project P --id APP-12 --question "…" --option "A" --option "B — detail"
|
|
64
64
|
--default "B"|wait [--deadline ISO] [--sensitive email|publish|deploy|spend]
|
|
65
|
-
Ask the owner ONE question with 2–
|
|
65
|
+
Ask the owner ONE question with 2–5 options. The
|
|
66
66
|
ticket's assignee (else its lead) posts it as a card
|
|
67
67
|
in the ticket's Slack thread; the answer comes back
|
|
68
68
|
to that agent as a [DECISION …] message
|
|
@@ -49,6 +49,46 @@ Assigns a task to a worker within the Team Leader's subordinate scope. Validates
|
|
|
49
49
|
| `--from` | `fromSession` | No | Delegating TL's session name (for monitoring) |
|
|
50
50
|
| `--request-id` / `-R` | `requestId` | No | Ticket this work is for: the id (or `TKT-123`) from the `[TICKET:TKT-123 <id>]` line of the message you are acting on. Omit it and the task is still linked when your current turn has exactly one ticket |
|
|
51
51
|
| `--ticket` | `ticket` | No | Project ticket this work is for (e.g. `APP-12`). Without it a ticket is created for you when the target works on a project — see Project tickets |
|
|
52
|
+
| `--thread` | `thread` | No | Owner request from Slack: the key from the `[SLACK-THREAD:<key>]` of the owner's message. The member answers the owner in that thread itself — see Owner requests |
|
|
53
|
+
| `--no-member-fits` | `noMemberFits` | No | You keep the work: record what is missing (access, tool, permission, everyone busy). Needs `--task`; nothing is delegated — see No member fits |
|
|
54
|
+
| `--work-item` | `workItemId` | No | With `--no-member-fits`: the WorkItem you keep (gets a `[NO-MEMBER-FITS]` note) |
|
|
55
|
+
|
|
56
|
+
## Who to delegate to
|
|
57
|
+
|
|
58
|
+
Role is a preference, not a limit: every member runs the same runtime and can
|
|
59
|
+
code, write and research. Any member can take any work that needs no special
|
|
60
|
+
account, tool or permission. Prefer an idle (or stopped) member over doing it
|
|
61
|
+
yourself. Do hands-on work yourself only for lead-level work (review,
|
|
62
|
+
decisions, owner communication, cross-team coordination), when every member
|
|
63
|
+
is busy, or when the work truly needs your own judgment.
|
|
64
|
+
|
|
65
|
+
## Owner requests (`--thread`)
|
|
66
|
+
|
|
67
|
+
When the owner asked in Slack, pass the thread key from the owner message's
|
|
68
|
+
`[SLACK-THREAD:<key>]`:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
bash execute.sh --to sage-session --task "Goal: … Outcome: … Eval: …" --thread C0THINK:1790000000.000100
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
The WorkItem carries the thread and the member is told to post its progress
|
|
75
|
+
and result there itself (`reply --work-item <id>`), under its own name. You do
|
|
76
|
+
not relay its answer; the room's reply gate never holds the member's post.
|
|
77
|
+
Without `--thread` the thread of your last owner message is used. (A thread
|
|
78
|
+
in your DM with the owner: the member's bot cannot post there, so it answers
|
|
79
|
+
in its own DM with the owner, opened with `Re: <task>`.)
|
|
80
|
+
|
|
81
|
+
## No member fits (`--no-member-fits`)
|
|
82
|
+
|
|
83
|
+
Rare. When you keep work yourself, record why — what is missing, not a role
|
|
84
|
+
mismatch — so the owner sees missing roles in the daily report:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
bash execute.sh --no-member-fits "needs the owner's Stripe login" --task "Update billing settings" [--work-item <id>] [--ticket CE-7]
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Nothing is delivered. With `--work-item` the WorkItem gets a
|
|
91
|
+
`[NO-MEMBER-FITS] <reason>` note.
|
|
52
92
|
|
|
53
93
|
## Project tickets
|
|
54
94
|
|
|
@@ -18,6 +18,9 @@ TL_MEMBER_ID=""
|
|
|
18
18
|
FROM_SESSION=""
|
|
19
19
|
REQUEST_ID=""
|
|
20
20
|
PROJECT_TICKET=""
|
|
21
|
+
OWNER_THREAD=""
|
|
22
|
+
NO_MEMBER_FITS=""
|
|
23
|
+
KEPT_WORK_ITEM=""
|
|
21
24
|
|
|
22
25
|
# Detect legacy JSON argument
|
|
23
26
|
if [[ $# -gt 0 && ${1:0:1} == '{' ]]; then
|
|
@@ -38,10 +41,15 @@ while [[ $# -gt 0 ]]; do
|
|
|
38
41
|
--from) FROM_SESSION="$2"; shift 2 ;;
|
|
39
42
|
--request-id|-R) REQUEST_ID="$2"; shift 2 ;;
|
|
40
43
|
--ticket) PROJECT_TICKET="$2"; shift 2 ;;
|
|
44
|
+
--thread) OWNER_THREAD="$2"; shift 2 ;;
|
|
45
|
+
--no-member-fits) NO_MEMBER_FITS="$2"; shift 2 ;;
|
|
46
|
+
--work-item) KEPT_WORK_ITEM="$2"; shift 2 ;;
|
|
41
47
|
--json|-j) INPUT_JSON="$2"; shift 2 ;;
|
|
42
48
|
--help|-h)
|
|
43
49
|
echo "Usage: execute.sh --to worker-session --task 'implement feature' --priority high --project /path [--team teamId] [--tl-member memberId] [--request-id <ticket id from the [TICKET:TKT-123 <id>] line>] [--ticket <project ticket id, e.g. APP-12>]"
|
|
44
50
|
echo "Work for a teammate on a project always runs through a project ticket: --ticket uses that backlog/ready ticket, otherwise one is created for you."
|
|
51
|
+
echo "Owner request from Slack: add --thread <key> (from [SLACK-THREAD:<key>]) — the member answers the owner in that thread itself."
|
|
52
|
+
echo "Keeping work yourself because no member fits: execute.sh --no-member-fits '<what is missing: access / tool / permission / everyone busy>' --task '<the work>' [--work-item <id>] [--ticket <ID>]"
|
|
45
53
|
exit 0
|
|
46
54
|
;;
|
|
47
55
|
--) shift; break ;;
|
|
@@ -79,6 +87,28 @@ if [ -n "$INPUT_JSON" ]; then
|
|
|
79
87
|
[ -z "$FROM_SESSION" ] && FROM_SESSION=$(printf '%s' "$INPUT" | jq -r '.fromSession // empty')
|
|
80
88
|
[ -z "$REQUEST_ID" ] && REQUEST_ID=$(printf '%s' "$INPUT" | jq -r '.requestId // empty')
|
|
81
89
|
[ -z "$PROJECT_TICKET" ] && PROJECT_TICKET=$(printf '%s' "$INPUT" | jq -r '.ticket // .projectTicketId // empty')
|
|
90
|
+
[ -z "$OWNER_THREAD" ] && OWNER_THREAD=$(printf '%s' "$INPUT" | jq -r '.thread // .ownerThread // empty')
|
|
91
|
+
[ -z "$NO_MEMBER_FITS" ] && NO_MEMBER_FITS=$(printf '%s' "$INPUT" | jq -r '.noMemberFits // empty')
|
|
92
|
+
[ -z "$KEPT_WORK_ITEM" ] && KEPT_WORK_ITEM=$(printf '%s' "$INPUT" | jq -r '.workItemId // empty')
|
|
93
|
+
fi
|
|
94
|
+
|
|
95
|
+
# "No member fits" (crewly#1083, specs/2026-10-04-tl-delegation.md §4): the
|
|
96
|
+
# lead keeps this work. Record why, so the owner sees missing roles; nothing
|
|
97
|
+
# is delivered to anyone.
|
|
98
|
+
if [ -n "$NO_MEMBER_FITS" ]; then
|
|
99
|
+
require_param "task (--task)" "$TASK"
|
|
100
|
+
KEEP_BODY=$(jq -n --arg reason "$NO_MEMBER_FITS" --arg work "$(echo "$TASK" | head -c 300)" --arg workItemId "$KEPT_WORK_ITEM" --arg ticket "$PROJECT_TICKET" \
|
|
101
|
+
'{reason: $reason, work: $work} + (if $workItemId != "" then {workItemId: $workItemId} else {} end) + (if $ticket != "" then {ticket: $ticket} else {} end)')
|
|
102
|
+
KEEP_RESULT=$(api_call POST "/teams/lead-self-work" "$KEEP_BODY" 2>/dev/null || echo '{"success":false}')
|
|
103
|
+
if [ "$(echo "$KEEP_RESULT" | jq -r '.success // false' 2>/dev/null)" != "true" ]; then
|
|
104
|
+
error_exit "Could not record the kept work: $(echo "$KEEP_RESULT" | jq -r '.error // "unknown error"' 2>/dev/null)"
|
|
105
|
+
fi
|
|
106
|
+
if [ -n "$KEPT_WORK_ITEM" ]; then
|
|
107
|
+
NOTE_BODY=$(jq -n --arg author "${CREWLY_SESSION_NAME:-${FROM_SESSION:-team-leader}}" --arg note "[NO-MEMBER-FITS] ${NO_MEMBER_FITS}" '{author: $author, note: $note}')
|
|
108
|
+
api_call POST "/task-pool/items/${KEPT_WORK_ITEM}/notes" "$NOTE_BODY" >/dev/null 2>&1 || true
|
|
109
|
+
fi
|
|
110
|
+
echo "$KEEP_RESULT" | jq -c '{success: true, recorded: .data, info: "Recorded: you keep this work because no member fits. Nothing was delegated."}'
|
|
111
|
+
exit 0
|
|
82
112
|
fi
|
|
83
113
|
|
|
84
114
|
require_param "to (--to)" "$TO"
|
|
@@ -218,7 +248,8 @@ POOL_BODY=$(jq -n \
|
|
|
218
248
|
--arg requestId "${REQUEST_ID:-}" \
|
|
219
249
|
--arg projectTicketId "${PROJECT_TICKET:-}" \
|
|
220
250
|
--arg delegatedBy "${CREWLY_SESSION_NAME:-${FROM_SESSION:-}}" \
|
|
221
|
-
|
|
251
|
+
--arg ownerThread "${OWNER_THREAD:-}" \
|
|
252
|
+
'{type: $type, owner: $owner, target: $target, title: $title, description: $description, briefMarkdown: $briefMarkdown, metadata: ({priority: $priority, directDelivery: true} + (if $projectPath != "" then {projectPath: $projectPath} else {} end) + (if $delegatedBy != "" then {delegatedBy: $delegatedBy} else {} end) + (if $ownerThread != "" then {ownerThread: $ownerThread} else {} end))} + (if $requestId != "" then {requestId: $requestId} else {} end) + (if $projectTicketId != "" then {projectTicketId: $projectTicketId} else {} end)')
|
|
222
253
|
# metadata.directDelivery: this script delivers the brief itself (below), so
|
|
223
254
|
# the backend's workitem:queued push holds off and only fires if that
|
|
224
255
|
# delivery never lands — the task reaches the worker once.
|
|
@@ -258,6 +289,13 @@ if [ -n "$TASK_ID" ]; then
|
|
|
258
289
|
DELIVER_MESSAGE="WorkItem ${TASK_ID} — ${TASK_MESSAGE//<your WorkItem id>/$TASK_ID}"
|
|
259
290
|
fi
|
|
260
291
|
|
|
292
|
+
# Owner request from a Slack thread (crewly#1083): the backend names the
|
|
293
|
+
# thread and tells the member to answer the owner there itself.
|
|
294
|
+
OWNER_THREAD_NOTE=$(echo "$POOL_RESULT" | jq -r '.data.ownerThread.note // empty' 2>/dev/null || true)
|
|
295
|
+
if [ -n "$OWNER_THREAD_NOTE" ]; then
|
|
296
|
+
DELIVER_MESSAGE="${DELIVER_MESSAGE}\n\n${OWNER_THREAD_NOTE}"
|
|
297
|
+
fi
|
|
298
|
+
|
|
261
299
|
if [ "$POOL_OK" != "true" ]; then
|
|
262
300
|
# Unchanged from before the reorder: warn and still attempt delivery.
|
|
263
301
|
# Aborting here instead would be a behavioural change beyond this WI's
|
|
@@ -6050,7 +6050,9 @@ export declare const DECISION_CONSTANTS: {
|
|
|
6050
6050
|
readonly QUESTION_MIN_CHARS: 8;
|
|
6051
6051
|
/** Allowed option count */
|
|
6052
6052
|
readonly MIN_OPTIONS: 2;
|
|
6053
|
-
readonly MAX_OPTIONS:
|
|
6053
|
+
readonly MAX_OPTIONS: 5;
|
|
6054
|
+
/** Two yes/no asks on one ticket this close together that read as either/or are rejected (ms) */
|
|
6055
|
+
readonly EITHER_OR_WINDOW_MS: number;
|
|
6054
6056
|
/** Max characters of an option label (Slack button text max is 75) */
|
|
6055
6057
|
readonly OPTION_LABEL_MAX_CHARS: 40;
|
|
6056
6058
|
/** Max characters of an option's optional detail line */
|
|
@@ -6203,6 +6205,47 @@ export declare const WORK_ITEM_DESTINATION_CONSTANTS: {
|
|
|
6203
6205
|
/** The one line every prompt carries about where to answer */
|
|
6204
6206
|
readonly PROMPT_LINE: "Answer where you were asked; a new topic goes in a new thread.";
|
|
6205
6207
|
};
|
|
6208
|
+
/**
|
|
6209
|
+
* Team leads delegate instead of doing the work (crewly#1083,
|
|
6210
|
+
* specs/2026-10-04-tl-delegation.md): owner-thread hand-over, the execution
|
|
6211
|
+
* nudge, lead share of team tokens, and "no member fits" records.
|
|
6212
|
+
*/
|
|
6213
|
+
export declare const TL_DELEGATION_CONSTANTS: {
|
|
6214
|
+
/** WorkItem.metadata / add-body field: the owner's Slack thread key a delegation answers */
|
|
6215
|
+
readonly OWNER_THREAD_FIELD: "ownerThread";
|
|
6216
|
+
/** A done item still lets its agent post in the owner's thread this long (ms) */
|
|
6217
|
+
readonly DONE_THREAD_GRACE_MS: number;
|
|
6218
|
+
/** Claude Code tools that count as hands-on file edits */
|
|
6219
|
+
readonly EDIT_TOOLS: readonly string[];
|
|
6220
|
+
/** Edits in the window that trigger a nudge */
|
|
6221
|
+
readonly EDIT_THRESHOLD: 6;
|
|
6222
|
+
/** Sliding window for counting edits (ms) */
|
|
6223
|
+
readonly WINDOW_MS: number;
|
|
6224
|
+
/** At most one nudge per lead per this long (ms) */
|
|
6225
|
+
readonly NUDGE_COOLDOWN_MS: number;
|
|
6226
|
+
/** A delegation this soon after a nudge counts as following it (ms) */
|
|
6227
|
+
readonly FOLLOW_WINDOW_MS: number;
|
|
6228
|
+
/** Members named in a nudge */
|
|
6229
|
+
readonly NUDGE_MAX_MEMBERS: 3;
|
|
6230
|
+
/** Tag that opens the nudge text */
|
|
6231
|
+
readonly NUDGE_TAG: "[CREWLY-NUDGE]";
|
|
6232
|
+
/** Note tag on a WorkItem the lead kept because no member fits */
|
|
6233
|
+
readonly NO_MEMBER_FITS_TAG: "[NO-MEMBER-FITS]";
|
|
6234
|
+
/** A lead share above this is flagged */
|
|
6235
|
+
readonly FLAG_SHARE: 0.5;
|
|
6236
|
+
/** Below this many team tokens in the period a share is not flagged (too little to judge) */
|
|
6237
|
+
readonly MIN_TEAM_TOKENS: 1000000;
|
|
6238
|
+
/** "This week" = the last N days */
|
|
6239
|
+
readonly WEEK_DAYS: 7;
|
|
6240
|
+
/** State file in CREWLY_HOME (nudge counts, kept-work records) */
|
|
6241
|
+
readonly STATE_FILENAME: "tl-delegation.json";
|
|
6242
|
+
/** Kept-work records retained */
|
|
6243
|
+
readonly MAX_RECORDS: 500;
|
|
6244
|
+
/** Max characters of a recorded reason / work line */
|
|
6245
|
+
readonly MAX_TEXT_CHARS: 300;
|
|
6246
|
+
/** Accepted tool_name from the hook */
|
|
6247
|
+
readonly TOOL_NAME_PATTERN: RegExp;
|
|
6248
|
+
};
|
|
6206
6249
|
/**
|
|
6207
6250
|
* Harness-owned reply routing (specs/2026-10-02-harness-owned-routing.md).
|
|
6208
6251
|
*/
|