role-os 1.2.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +58 -0
- package/README.md +9 -2
- package/bin/roleos.mjs +23 -1
- package/package.json +1 -1
- package/src/calibration.mjs +292 -0
- package/src/composite.mjs +454 -0
- package/src/decompose.mjs +311 -0
- package/src/packs.mjs +33 -5
- package/src/replan.mjs +404 -0
- package/src/session.mjs +337 -0
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mixed-Task Decomposition — Phase N
|
|
3
|
+
*
|
|
4
|
+
* Detects composite work early, splits it into the right packets,
|
|
5
|
+
* assigns packs/roles to each slice, and preserves dependencies.
|
|
6
|
+
*
|
|
7
|
+
* Failure mode this solves: treating one messy task like one job
|
|
8
|
+
* when it is actually several (feature + docs, bugfix + release,
|
|
9
|
+
* research + product framing, security + treatment).
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { suggestPack } from "./packs.mjs";
|
|
13
|
+
|
|
14
|
+
// ── Subtask categories ────────────────────────────────────────────────────────
|
|
15
|
+
|
|
16
|
+
const SUBTASK_CATEGORIES = {
|
|
17
|
+
build: {
|
|
18
|
+
signals: ["implement", "add feature", "create command", "build", "code", "develop", "ship feature"],
|
|
19
|
+
pack: "feature",
|
|
20
|
+
phase: 1,
|
|
21
|
+
},
|
|
22
|
+
bugfix: {
|
|
23
|
+
signals: ["fix", "bug", "crash", "broken", "repair", "regression", "patch"],
|
|
24
|
+
pack: "bugfix",
|
|
25
|
+
phase: 1,
|
|
26
|
+
},
|
|
27
|
+
security: {
|
|
28
|
+
signals: ["security review", "vulnerability", "injection", "threat model", "audit security", "CVE"],
|
|
29
|
+
pack: "security",
|
|
30
|
+
phase: 2,
|
|
31
|
+
},
|
|
32
|
+
docs: {
|
|
33
|
+
signals: ["documentation", "handbook", "readme", "update docs", "write docs", "add page"],
|
|
34
|
+
pack: "docs",
|
|
35
|
+
phase: 3,
|
|
36
|
+
},
|
|
37
|
+
research: {
|
|
38
|
+
signals: ["research", "should we", "evaluate", "assess whether", "competitive analysis", "investigate"],
|
|
39
|
+
pack: "research",
|
|
40
|
+
phase: 0, // research goes first when present
|
|
41
|
+
},
|
|
42
|
+
launch: {
|
|
43
|
+
signals: ["launch", "announce", "release notes", "social post", "messaging", "publish announcement"],
|
|
44
|
+
pack: "launch",
|
|
45
|
+
phase: 4,
|
|
46
|
+
},
|
|
47
|
+
treatment: {
|
|
48
|
+
signals: ["treatment", "repo polish", "pre-release", "ship-ready", "housekeeping", "cleanup"],
|
|
49
|
+
pack: "treatment",
|
|
50
|
+
phase: 3,
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
// ── Composite signals (structural patterns that suggest multiple tasks) ──────
|
|
55
|
+
|
|
56
|
+
const COMPOSITE_CONNECTORS = [
|
|
57
|
+
/\band\s+(?:then|also|additionally)\b/i,
|
|
58
|
+
/\bafter\s+that\b/i,
|
|
59
|
+
/\bplus\b/i,
|
|
60
|
+
/\bas\s+well\s+as\b/i,
|
|
61
|
+
/\bthen\s+(?:write|update|create|run|do)\b/i,
|
|
62
|
+
/\balso\s+(?:need|want|should|must)\b/i,
|
|
63
|
+
];
|
|
64
|
+
|
|
65
|
+
// ── Detection ─────────────────────────────────────────────────────────────────
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Detect whether a packet contains composite work.
|
|
69
|
+
*
|
|
70
|
+
* @param {string} content - Packet markdown content
|
|
71
|
+
* @returns {{
|
|
72
|
+
* isComposite: boolean,
|
|
73
|
+
* confidence: "high" | "medium" | "low",
|
|
74
|
+
* detectedCategories: { category: string, signals: string[], pack: string }[],
|
|
75
|
+
* hasConnectors: boolean,
|
|
76
|
+
* reason: string
|
|
77
|
+
* }}
|
|
78
|
+
*/
|
|
79
|
+
export function detectComposite(content) {
|
|
80
|
+
const lower = content.toLowerCase();
|
|
81
|
+
|
|
82
|
+
// Find matching categories
|
|
83
|
+
const detectedCategories = [];
|
|
84
|
+
for (const [category, def] of Object.entries(SUBTASK_CATEGORIES)) {
|
|
85
|
+
const matchedSignals = def.signals.filter(s => lower.includes(s.toLowerCase()));
|
|
86
|
+
if (matchedSignals.length > 0) {
|
|
87
|
+
detectedCategories.push({
|
|
88
|
+
category,
|
|
89
|
+
signals: matchedSignals,
|
|
90
|
+
pack: def.pack,
|
|
91
|
+
phase: def.phase,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Check for structural connectors
|
|
97
|
+
const hasConnectors = COMPOSITE_CONNECTORS.some(re => re.test(content));
|
|
98
|
+
|
|
99
|
+
// Determine if composite
|
|
100
|
+
const categoryCount = detectedCategories.length;
|
|
101
|
+
|
|
102
|
+
if (categoryCount <= 1) {
|
|
103
|
+
return {
|
|
104
|
+
isComposite: false,
|
|
105
|
+
confidence: "high",
|
|
106
|
+
detectedCategories,
|
|
107
|
+
hasConnectors,
|
|
108
|
+
reason: categoryCount === 0
|
|
109
|
+
? "No recognized subtask categories detected."
|
|
110
|
+
: `Single category detected: ${detectedCategories[0].category}.`,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Multiple categories detected
|
|
115
|
+
let confidence;
|
|
116
|
+
let reason;
|
|
117
|
+
|
|
118
|
+
if (categoryCount >= 3 || (categoryCount >= 2 && hasConnectors)) {
|
|
119
|
+
confidence = "high";
|
|
120
|
+
reason = `${categoryCount} distinct subtask categories detected${hasConnectors ? " with structural connectors" : ""}. Recommend splitting.`;
|
|
121
|
+
} else if (categoryCount === 2 && !hasConnectors) {
|
|
122
|
+
confidence = "medium";
|
|
123
|
+
reason = `${categoryCount} categories detected without explicit connectors. May be composite or just a broad single task.`;
|
|
124
|
+
} else {
|
|
125
|
+
confidence = "low";
|
|
126
|
+
reason = "Ambiguous — multiple weak signals.";
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return {
|
|
130
|
+
isComposite: categoryCount >= 2,
|
|
131
|
+
confidence,
|
|
132
|
+
detectedCategories,
|
|
133
|
+
hasConnectors,
|
|
134
|
+
reason,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// ── Decomposition ─────────────────────────────────────────────────────────────
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Decompose a composite task into linked child packets.
|
|
142
|
+
*
|
|
143
|
+
* @param {string} content - Original packet content
|
|
144
|
+
* @param {{ category: string, signals: string[], pack: string, phase: number }[]} categories
|
|
145
|
+
* @returns {{
|
|
146
|
+
* parentId: string,
|
|
147
|
+
* children: { id: string, category: string, pack: string, dependsOn: string[], phase: number }[],
|
|
148
|
+
* dependencyOrder: string[],
|
|
149
|
+
* handoffChain: string,
|
|
150
|
+
* }}
|
|
151
|
+
*/
|
|
152
|
+
export function decompose(content, categories) {
|
|
153
|
+
const parentId = `parent-${Date.now()}`;
|
|
154
|
+
|
|
155
|
+
// Sort by phase (research first, launch last)
|
|
156
|
+
const sorted = [...categories].sort((a, b) => a.phase - b.phase);
|
|
157
|
+
|
|
158
|
+
const children = sorted.map((cat, i) => {
|
|
159
|
+
const id = `${parentId}-${cat.category}`;
|
|
160
|
+
const dependsOn = i > 0 ? [sorted[i - 1].category] : [];
|
|
161
|
+
|
|
162
|
+
return {
|
|
163
|
+
id,
|
|
164
|
+
category: cat.category,
|
|
165
|
+
pack: cat.pack,
|
|
166
|
+
dependsOn,
|
|
167
|
+
phase: cat.phase,
|
|
168
|
+
};
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
const dependencyOrder = children.map(c => c.category);
|
|
172
|
+
|
|
173
|
+
const handoffChain = children
|
|
174
|
+
.map(c => `${c.category} (${c.pack} pack)`)
|
|
175
|
+
.join(" → ");
|
|
176
|
+
|
|
177
|
+
return {
|
|
178
|
+
parentId,
|
|
179
|
+
children,
|
|
180
|
+
dependencyOrder,
|
|
181
|
+
handoffChain,
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// ── Parent run plan ───────────────────────────────────────────────────────────
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Create a parent run plan with completion tracking.
|
|
189
|
+
*
|
|
190
|
+
* @param {object} decomposition - Output from decompose()
|
|
191
|
+
* @returns {object}
|
|
192
|
+
*/
|
|
193
|
+
export function createRunPlan(decomposition) {
|
|
194
|
+
return {
|
|
195
|
+
parentId: decomposition.parentId,
|
|
196
|
+
status: "pending",
|
|
197
|
+
children: decomposition.children.map(child => ({
|
|
198
|
+
...child,
|
|
199
|
+
status: "pending",
|
|
200
|
+
escalations: 0,
|
|
201
|
+
completedAt: null,
|
|
202
|
+
})),
|
|
203
|
+
dependencyOrder: decomposition.dependencyOrder,
|
|
204
|
+
handoffChain: decomposition.handoffChain,
|
|
205
|
+
completionCondition: "All child packets must be completed in dependency order.",
|
|
206
|
+
escalationRollup: "Any child blocked or failed blocks the parent.",
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Update a child's status in the run plan.
|
|
212
|
+
*
|
|
213
|
+
* @param {object} plan
|
|
214
|
+
* @param {string} category
|
|
215
|
+
* @param {"pending"|"running"|"completed"|"blocked"|"failed"} status
|
|
216
|
+
* @returns {object} Updated plan
|
|
217
|
+
*/
|
|
218
|
+
export function updateChildStatus(plan, category, status) {
|
|
219
|
+
const child = plan.children.find(c => c.category === category);
|
|
220
|
+
if (!child) return plan;
|
|
221
|
+
|
|
222
|
+
child.status = status;
|
|
223
|
+
if (status === "completed") {
|
|
224
|
+
child.completedAt = new Date().toISOString();
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Update parent status
|
|
228
|
+
const allCompleted = plan.children.every(c => c.status === "completed");
|
|
229
|
+
const anyBlocked = plan.children.some(c => c.status === "blocked" || c.status === "failed");
|
|
230
|
+
|
|
231
|
+
if (allCompleted) plan.status = "completed";
|
|
232
|
+
else if (anyBlocked) plan.status = "blocked";
|
|
233
|
+
else if (plan.children.some(c => c.status === "running")) plan.status = "running";
|
|
234
|
+
|
|
235
|
+
return plan;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Get the next actionable child in the plan.
|
|
240
|
+
*
|
|
241
|
+
* @param {object} plan
|
|
242
|
+
* @returns {{ child: object, reason: string } | null}
|
|
243
|
+
*/
|
|
244
|
+
export function getNextChild(plan) {
|
|
245
|
+
for (const child of plan.children) {
|
|
246
|
+
if (child.status !== "pending") continue;
|
|
247
|
+
|
|
248
|
+
// Check dependencies
|
|
249
|
+
const depsComplete = child.dependsOn.every(dep => {
|
|
250
|
+
const depChild = plan.children.find(c => c.category === dep);
|
|
251
|
+
return depChild && depChild.status === "completed";
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
if (depsComplete) {
|
|
255
|
+
return {
|
|
256
|
+
child,
|
|
257
|
+
reason: child.dependsOn.length === 0
|
|
258
|
+
? `${child.category} has no dependencies — ready to start.`
|
|
259
|
+
: `Dependencies complete: ${child.dependsOn.join(", ")}.`,
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
return null;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// ── Format ────────────────────────────────────────────────────────────────────
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Format decomposition result for display.
|
|
270
|
+
*
|
|
271
|
+
* @param {object} detection - Output from detectComposite()
|
|
272
|
+
* @param {object} [decomposition] - Output from decompose() (if composite)
|
|
273
|
+
* @returns {string}
|
|
274
|
+
*/
|
|
275
|
+
export function formatDecomposition(detection, decomposition) {
|
|
276
|
+
const lines = [];
|
|
277
|
+
|
|
278
|
+
if (!detection.isComposite) {
|
|
279
|
+
lines.push(`Task type: single`);
|
|
280
|
+
lines.push(` ${detection.reason}`);
|
|
281
|
+
return lines.join("\n");
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
lines.push(`Task type: composite (${detection.confidence} confidence)`);
|
|
285
|
+
lines.push(` ${detection.reason}`);
|
|
286
|
+
lines.push(``);
|
|
287
|
+
lines.push(`Detected subtasks:`);
|
|
288
|
+
for (const cat of detection.detectedCategories) {
|
|
289
|
+
lines.push(` ${cat.category} → ${cat.pack} pack (signals: ${cat.signals.join(", ")})`);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
if (decomposition) {
|
|
293
|
+
lines.push(``);
|
|
294
|
+
lines.push(`Recommended split:`);
|
|
295
|
+
lines.push(` ${decomposition.handoffChain}`);
|
|
296
|
+
lines.push(``);
|
|
297
|
+
lines.push(`Dependency order:`);
|
|
298
|
+
for (const child of decomposition.children) {
|
|
299
|
+
const deps = child.dependsOn.length > 0 ? ` (after: ${child.dependsOn.join(", ")})` : " (no dependencies)";
|
|
300
|
+
lines.push(` ${child.category}${deps}`);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
if (detection.confidence === "medium" || detection.confidence === "low") {
|
|
305
|
+
lines.push(``);
|
|
306
|
+
lines.push(`⚠ Confidence is ${detection.confidence}. Consider whether this is truly composite`);
|
|
307
|
+
lines.push(` or just a broad single task. Use --no-split to force single-packet routing.`);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
return lines.join("\n");
|
|
311
|
+
}
|
package/src/packs.mjs
CHANGED
|
@@ -55,6 +55,10 @@ export const TEAM_PACKS = {
|
|
|
55
55
|
mismatchGuards: [
|
|
56
56
|
{ notForSignals: ["security review", "threat model", "vulnerability", "injection"], suggestInstead: "security", reason: "This is a security review, not a feature build" },
|
|
57
57
|
{ notForSignals: ["launch", "announce", "release notes", "messaging"], suggestInstead: "launch", reason: "This is launch/messaging work, not feature implementation" },
|
|
58
|
+
{ notForSignals: ["bug", "fix", "crash", "broken", "regression"], suggestInstead: "bugfix", reason: "This is a bug to fix, not a feature to build" },
|
|
59
|
+
{ notForSignals: ["handbook", "documentation", "restructure docs"], suggestInstead: "docs", reason: "This is docs work, not feature implementation" },
|
|
60
|
+
{ notForSignals: ["research", "should we", "competitive", "trend"], suggestInstead: "research", reason: "This is a research/strategy question, not a feature build" },
|
|
61
|
+
{ notForSignals: ["treatment", "repo audit", "shipcheck", "full treatment", "polish"], suggestInstead: "treatment", reason: "This is repo treatment work, not feature implementation" },
|
|
58
62
|
],
|
|
59
63
|
},
|
|
60
64
|
|
|
@@ -68,7 +72,7 @@ export const TEAM_PACKS = {
|
|
|
68
72
|
"Test Engineer",
|
|
69
73
|
"Critic Reviewer",
|
|
70
74
|
],
|
|
71
|
-
orchestratorRequired: false,
|
|
75
|
+
orchestratorRequired: false,
|
|
72
76
|
optionalRoles: ["Frontend Developer", "Performance Engineer"],
|
|
73
77
|
chainOrder: "Repo Researcher → Backend Engineer → Test Engineer",
|
|
74
78
|
requiredArtifacts: ["repo map / diagnosis", "fix implementation", "regression tests", "verdict"],
|
|
@@ -78,10 +82,14 @@ export const TEAM_PACKS = {
|
|
|
78
82
|
],
|
|
79
83
|
escalationOwner: "Critic Reviewer",
|
|
80
84
|
dispatchDefaults: { model: "sonnet", maxTurns: 20, maxBudgetUsd: 3.0 },
|
|
81
|
-
trialEvidence: "G2 (Engineering), G7 (Repo Researcher), I-2 (shipped real fix).
|
|
85
|
+
trialEvidence: "G2 (Engineering), G7 (Repo Researcher), I-2 (shipped real fix).",
|
|
82
86
|
mismatchGuards: [
|
|
83
|
-
{ notForSignals: ["launch", "announce", "release notes"], suggestInstead: "launch", reason: "This is launch work, not a bugfix" },
|
|
87
|
+
{ notForSignals: ["launch", "announce", "release notes", "messaging"], suggestInstead: "launch", reason: "This is launch work, not a bugfix" },
|
|
84
88
|
{ notForSignals: ["research", "should we", "tradeoff", "strategy"], suggestInstead: "research", reason: "This is a research/strategy question, not a bug to fix" },
|
|
89
|
+
{ notForSignals: ["feature", "new command", "add", "create", "implement"], suggestInstead: "feature", reason: "This is a new feature, not a bug to fix" },
|
|
90
|
+
{ notForSignals: ["handbook", "documentation", "restructure docs", "starlight"], suggestInstead: "docs", reason: "This is docs work, not a bugfix" },
|
|
91
|
+
{ notForSignals: ["security review", "threat model", "vulnerability"], suggestInstead: "security", reason: "This is a security review, not a bugfix" },
|
|
92
|
+
{ notForSignals: ["treatment", "repo audit", "shipcheck", "polish"], suggestInstead: "treatment", reason: "This is repo treatment, not a bugfix" },
|
|
85
93
|
],
|
|
86
94
|
},
|
|
87
95
|
|
|
@@ -108,10 +116,14 @@ export const TEAM_PACKS = {
|
|
|
108
116
|
mismatchGuards: [
|
|
109
117
|
{ notForSignals: ["documentation", "handbook", "restructure", "navigation"], suggestInstead: "docs", reason: "This is docs/structure work, not a security review" },
|
|
110
118
|
{ notForSignals: ["feature", "implement", "build", "add command"], suggestInstead: "feature", reason: "This is feature work, not a security review" },
|
|
119
|
+
{ notForSignals: ["bug", "fix", "crash", "broken", "regression"], suggestInstead: "bugfix", reason: "This is a bug to fix, not a security review" },
|
|
120
|
+
{ notForSignals: ["launch", "announce", "release notes", "messaging"], suggestInstead: "launch", reason: "This is launch work, not a security review" },
|
|
121
|
+
{ notForSignals: ["research", "should we", "competitive", "strategy"], suggestInstead: "research", reason: "This is a research question, not a security review" },
|
|
122
|
+
{ notForSignals: ["treatment", "repo audit", "shipcheck", "polish"], suggestInstead: "treatment", reason: "This is repo treatment, not a security review" },
|
|
111
123
|
],
|
|
112
124
|
},
|
|
113
125
|
|
|
114
|
-
// ── Docs / Handbook
|
|
126
|
+
// ── Docs / Handbook ─────────────────────────────────────────────────
|
|
115
127
|
docs: {
|
|
116
128
|
name: "Docs / Handbook",
|
|
117
129
|
description: "Triage → synthesize → structure → write → metadata → review",
|
|
@@ -136,7 +148,11 @@ export const TEAM_PACKS = {
|
|
|
136
148
|
trialEvidence: "G4 (Docs Architect), G7 (Treatment), I-4 (shipped page). Calibrated: Support Triage Lead + Feedback Synthesizer upstream. Release/Deploy moved to optional (overhead for docs-only tasks).",
|
|
137
149
|
mismatchGuards: [
|
|
138
150
|
{ notForSignals: ["research", "should we", "competitive", "strategy"], suggestInstead: "research", reason: "This is a research/strategy question — decide before documenting" },
|
|
139
|
-
{ notForSignals: ["security", "threat", "vulnerability"], suggestInstead: "security", reason: "This is a security review, not docs work" },
|
|
151
|
+
{ notForSignals: ["security review", "threat", "vulnerability", "injection"], suggestInstead: "security", reason: "This is a security review, not docs work" },
|
|
152
|
+
{ notForSignals: ["feature", "implement", "build", "add command"], suggestInstead: "feature", reason: "This is feature work, not docs" },
|
|
153
|
+
{ notForSignals: ["bug", "fix", "crash", "broken"], suggestInstead: "bugfix", reason: "This is a bugfix, not docs work" },
|
|
154
|
+
{ notForSignals: ["launch", "announce", "go-to-market", "messaging"], suggestInstead: "launch", reason: "This is launch work, not docs" },
|
|
155
|
+
{ notForSignals: ["treatment", "repo audit", "shipcheck", "full treatment"], suggestInstead: "treatment", reason: "This is repo treatment, not docs only" },
|
|
140
156
|
],
|
|
141
157
|
},
|
|
142
158
|
|
|
@@ -163,6 +179,10 @@ export const TEAM_PACKS = {
|
|
|
163
179
|
mismatchGuards: [
|
|
164
180
|
{ notForSignals: ["bug", "fix", "crash", "broken", "error"], suggestInstead: "bugfix", reason: "This is a bug to fix, not a launch to plan" },
|
|
165
181
|
{ notForSignals: ["implement", "build", "add command", "new feature"], suggestInstead: "feature", reason: "This is feature work — build first, launch second" },
|
|
182
|
+
{ notForSignals: ["security review", "threat", "vulnerability", "injection"], suggestInstead: "security", reason: "This is a security review, not launch work" },
|
|
183
|
+
{ notForSignals: ["handbook", "documentation", "restructure docs"], suggestInstead: "docs", reason: "This is docs work, not launch messaging" },
|
|
184
|
+
{ notForSignals: ["research", "should we", "competitive", "trend"], suggestInstead: "research", reason: "This is research, not launch messaging" },
|
|
185
|
+
{ notForSignals: ["treatment", "repo audit", "shipcheck", "polish"], suggestInstead: "treatment", reason: "This is repo treatment, not launch" },
|
|
166
186
|
],
|
|
167
187
|
},
|
|
168
188
|
|
|
@@ -192,6 +212,10 @@ export const TEAM_PACKS = {
|
|
|
192
212
|
mismatchGuards: [
|
|
193
213
|
{ notForSignals: ["implement", "build", "add command", "write code"], suggestInstead: "feature", reason: "This is implementation work, not research" },
|
|
194
214
|
{ notForSignals: ["bug", "fix", "crash", "broken"], suggestInstead: "bugfix", reason: "This is a bugfix, not a research question" },
|
|
215
|
+
{ notForSignals: ["security review", "threat", "vulnerability", "injection"], suggestInstead: "security", reason: "This is a security review, not research" },
|
|
216
|
+
{ notForSignals: ["handbook", "documentation", "restructure docs"], suggestInstead: "docs", reason: "This is docs work, not research" },
|
|
217
|
+
{ notForSignals: ["launch", "announce", "release notes", "messaging"], suggestInstead: "launch", reason: "This is launch work, not research" },
|
|
218
|
+
{ notForSignals: ["treatment", "repo audit", "shipcheck", "polish"], suggestInstead: "treatment", reason: "This is repo treatment, not research" },
|
|
195
219
|
],
|
|
196
220
|
},
|
|
197
221
|
|
|
@@ -224,6 +248,10 @@ export const TEAM_PACKS = {
|
|
|
224
248
|
mismatchGuards: [
|
|
225
249
|
{ notForSignals: ["launch", "announce", "release notes", "social", "messaging"], suggestInstead: "launch", reason: "This is launch/messaging work — Treatment audits repos, it doesn't write announcements" },
|
|
226
250
|
{ notForSignals: ["research", "should we", "competitive", "strategy"], suggestInstead: "research", reason: "This is a research/strategy question, not a repo treatment" },
|
|
251
|
+
{ notForSignals: ["feature", "new command", "implement", "add", "create"], suggestInstead: "feature", reason: "This is feature work, not repo treatment" },
|
|
252
|
+
{ notForSignals: ["bug", "fix", "crash", "broken"], suggestInstead: "bugfix", reason: "This is a bugfix, not a full treatment" },
|
|
253
|
+
{ notForSignals: ["security review", "threat model", "injection"], suggestInstead: "security", reason: "This is a security review — treatment includes security but this task is security-only" },
|
|
254
|
+
{ notForSignals: ["handbook", "documentation", "restructure docs"], suggestInstead: "docs", reason: "This is docs-only work, not a full treatment" },
|
|
227
255
|
],
|
|
228
256
|
},
|
|
229
257
|
};
|