@geraldmaron/construct 1.0.3 → 1.0.5
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/README.md +51 -42
- package/agents/prompts/cx-ai-engineer.md +6 -26
- package/agents/prompts/cx-architect.md +1 -0
- package/agents/prompts/cx-business-strategist.md +2 -0
- package/agents/prompts/cx-data-analyst.md +6 -26
- package/agents/prompts/cx-docs-keeper.md +1 -31
- package/agents/prompts/cx-explorer.md +1 -0
- package/agents/prompts/cx-orchestrator.md +40 -112
- package/agents/prompts/cx-platform-engineer.md +2 -22
- package/agents/prompts/cx-product-manager.md +2 -1
- package/agents/prompts/cx-qa.md +0 -20
- package/agents/prompts/cx-rd-lead.md +2 -0
- package/agents/prompts/cx-researcher.md +77 -31
- package/agents/prompts/cx-security.md +11 -49
- package/agents/prompts/cx-sre.md +9 -43
- package/agents/prompts/cx-ux-researcher.md +1 -0
- package/agents/role-manifests.json +4 -4
- package/bin/construct +279 -4
- package/commands/understand/research.md +5 -3
- package/db/schema/004_recommendations.sql +46 -0
- package/db/schema/005_strategy.sql +21 -0
- package/lib/auto-docs.mjs +3 -4
- package/lib/beads-automation.mjs +16 -7
- package/lib/cli-commands.mjs +52 -2
- package/lib/comment-lint.mjs +7 -1
- package/lib/config/schema.mjs +3 -0
- package/lib/embed/conflict-detection.mjs +26 -9
- package/lib/embed/customer-profiles.mjs +37 -17
- package/lib/embed/daemon.mjs +10 -8
- package/lib/embed/recommendation-store.mjs +213 -14
- package/lib/embed/workspaces.mjs +53 -18
- package/lib/flavors/loader.mjs +136 -0
- package/lib/gates-audit.mjs +3 -3
- package/lib/health-check.mjs +1 -1
- package/lib/hooks/agent-tracker.mjs +22 -3
- package/lib/hooks/pre-compact.mjs +3 -0
- package/lib/hooks/pre-push-gate.mjs +14 -1
- package/lib/hooks/read-tracker.mjs +10 -101
- package/lib/hooks/session-optimize.mjs +3 -2
- package/lib/hooks/session-reflect.mjs +68 -0
- package/lib/host-capabilities.mjs +90 -1
- package/lib/init-unified.mjs +25 -2
- package/lib/init-update.mjs +246 -131
- package/lib/intake/classify.mjs +61 -183
- package/lib/intake/prepare.mjs +7 -0
- package/lib/intake/tables/creative.mjs +94 -0
- package/lib/intake/tables/operations.mjs +85 -0
- package/lib/intake/tables/research.mjs +85 -0
- package/lib/intake/tables/rnd.mjs +175 -0
- package/lib/intent-classifier.mjs +1 -0
- package/lib/knowledge/layout.mjs +10 -0
- package/lib/knowledge/research-store.mjs +109 -0
- package/lib/mcp/tools/telemetry.mjs +30 -78
- package/lib/model-router.mjs +61 -1
- package/lib/observation-store.mjs +19 -0
- package/lib/ollama-manager.mjs +1 -1
- package/lib/opencode-telemetry.mjs +4 -5
- package/lib/orchestration-policy.mjs +9 -0
- package/lib/outcomes/aggregate.mjs +104 -0
- package/lib/outcomes/record.mjs +115 -0
- package/lib/parity.mjs +121 -21
- package/lib/profiles/lifecycle.mjs +388 -0
- package/lib/profiles/loader.mjs +123 -0
- package/lib/profiles/validate-custom.mjs +114 -0
- package/lib/prompt-composer.js +106 -29
- package/lib/read-tracker-store.mjs +149 -0
- package/lib/reflect/extractor.mjs +193 -0
- package/lib/reflect.mjs +89 -2
- package/lib/sandbox.mjs +102 -0
- package/lib/server/index.mjs +76 -0
- package/lib/server/telemetry-login.mjs +17 -25
- package/lib/service-manager.mjs +30 -22
- package/lib/services/local-postgres.mjs +15 -0
- package/lib/services/telemetry-backend.mjs +1 -2
- package/lib/setup.mjs +8 -43
- package/lib/status.mjs +51 -5
- package/lib/storage/backend.mjs +12 -2
- package/lib/strategy-store.mjs +371 -0
- package/lib/telemetry/backends/local.mjs +6 -4
- package/lib/telemetry/client.mjs +185 -0
- package/lib/telemetry/ingest.mjs +13 -5
- package/lib/telemetry/team-rollup.mjs +9 -2
- package/lib/worker/trace.mjs +17 -27
- package/package.json +10 -2
- package/personas/construct.md +20 -20
- package/platforms/claude/settings.template.json +13 -0
- package/rules/common/research.md +44 -12
- package/scripts/sync-agents.mjs +11 -0
- package/skills/docs/backlog-proposal-workflow.md +2 -2
- package/skills/docs/customer-profile-workflow.md +1 -1
- package/skills/docs/evidence-ingest-workflow.md +5 -5
- package/skills/docs/prfaq-workflow.md +1 -1
- package/skills/docs/product-intelligence-review.md +1 -1
- package/skills/docs/product-intelligence-workflow.md +3 -3
- package/skills/docs/product-signal-workflow.md +48 -18
- package/skills/docs/research-workflow.md +26 -14
- package/skills/docs/strategy-workflow.md +36 -0
- package/skills/roles/architect.ai-systems.md +4 -2
- package/skills/roles/architect.data.md +4 -2
- package/skills/roles/architect.enterprise.md +4 -2
- package/skills/roles/architect.integration.md +4 -2
- package/skills/roles/architect.md +7 -5
- package/skills/roles/architect.platform.md +4 -2
- package/skills/roles/data-analyst.experiment.md +4 -2
- package/skills/roles/data-analyst.md +9 -7
- package/skills/roles/data-analyst.product-intelligence.md +5 -3
- package/skills/roles/data-analyst.product.md +4 -2
- package/skills/roles/data-analyst.telemetry.md +4 -2
- package/skills/roles/data-engineer.pipeline.md +4 -2
- package/skills/roles/data-engineer.vector-retrieval.md +4 -2
- package/skills/roles/data-engineer.warehouse.md +4 -2
- package/skills/roles/debugger.md +7 -5
- package/skills/roles/designer.accessibility.md +4 -2
- package/skills/roles/designer.md +10 -8
- package/skills/roles/engineer.ai.md +4 -2
- package/skills/roles/engineer.data.md +5 -3
- package/skills/roles/engineer.md +14 -12
- package/skills/roles/engineer.platform.md +5 -3
- package/skills/roles/operator.docs.md +6 -4
- package/skills/roles/operator.md +6 -4
- package/skills/roles/operator.release.md +4 -2
- package/skills/roles/operator.sre.md +5 -3
- package/skills/roles/orchestrator.md +5 -3
- package/skills/roles/product-manager.ai-product.md +4 -2
- package/skills/roles/product-manager.business-strategy.md +4 -2
- package/skills/roles/product-manager.enterprise.md +4 -2
- package/skills/roles/product-manager.growth.md +4 -2
- package/skills/roles/product-manager.md +6 -4
- package/skills/roles/product-manager.platform.md +4 -2
- package/skills/roles/product-manager.product.md +4 -2
- package/skills/roles/qa.ai-eval.md +4 -2
- package/skills/roles/qa.api-contract.md +4 -2
- package/skills/roles/qa.data-pipeline.md +4 -2
- package/skills/roles/qa.md +7 -5
- package/skills/roles/qa.test-automation.md +5 -3
- package/skills/roles/qa.web-ui.md +4 -2
- package/skills/roles/researcher.explorer.md +4 -2
- package/skills/roles/researcher.md +35 -20
- package/skills/roles/researcher.ux.md +4 -2
- package/skills/roles/reviewer.devil-advocate.md +4 -2
- package/skills/roles/reviewer.evaluator.md +4 -2
- package/skills/roles/reviewer.md +14 -12
- package/skills/roles/reviewer.trace.md +4 -2
- package/skills/roles/security.ai.md +4 -2
- package/skills/roles/security.appsec.md +4 -2
- package/skills/roles/security.cloud.md +4 -2
- package/skills/roles/security.legal-compliance.md +4 -2
- package/skills/roles/security.md +7 -5
- package/skills/roles/security.privacy.md +4 -2
- package/skills/roles/security.supply-chain.md +4 -2
- package/skills/routing.md +8 -1
- package/templates/docs/persona-artifact.md +36 -0
- package/templates/docs/research-brief.md +63 -9
- package/templates/docs/research-finding.md +26 -0
- package/templates/docs/skill-artifact.md +27 -0
- package/templates/docs/strategy.md +36 -0
- package/templates/homebrew/construct.rb +6 -6
package/lib/intake/classify.mjs
CHANGED
|
@@ -1,47 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* lib/intake/classify.mjs —
|
|
2
|
+
* lib/intake/classify.mjs — Profile-aware triage classification for intake packets.
|
|
3
3
|
*
|
|
4
|
-
* Deterministic, keyword-driven classifier that maps a raw intake signal
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* Deterministic, keyword-driven classifier that maps a raw intake signal onto
|
|
5
|
+
* the active org profile's loop. Returns a triage object with: intakeType,
|
|
6
|
+
* rdStage, primaryOwner, recommendedChain, recommendedAction, risk,
|
|
7
|
+
* requiresApproval, confidence, rationale.
|
|
8
8
|
*
|
|
9
9
|
* NO LLM call. The daemon path must remain synchronous and cheap; intent
|
|
10
10
|
* verification belongs in offline measurement, not inline classification.
|
|
11
|
-
*
|
|
11
|
+
*
|
|
12
|
+
* Default profile is `rnd` so existing R&D users see no behavior change.
|
|
13
|
+
* The `tests/intake/golden-rnd.test.mjs` test locks that invariant.
|
|
12
14
|
*/
|
|
13
15
|
|
|
14
16
|
import path from 'node:path';
|
|
15
17
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
'eval-finding',
|
|
23
|
-
'architecture',
|
|
24
|
-
'incident',
|
|
25
|
-
'launch-asset',
|
|
26
|
-
'ops',
|
|
27
|
-
'security',
|
|
28
|
-
'legal-compliance',
|
|
29
|
-
'unknown',
|
|
30
|
-
];
|
|
31
|
-
|
|
32
|
-
export const RD_STAGES = [
|
|
33
|
-
'signal',
|
|
34
|
-
'framing',
|
|
35
|
-
'hypothesis',
|
|
36
|
-
'research',
|
|
37
|
-
'artifact',
|
|
38
|
-
'design',
|
|
39
|
-
'implementation',
|
|
40
|
-
'evaluation',
|
|
41
|
-
'release',
|
|
42
|
-
'operations',
|
|
43
|
-
'unknown',
|
|
44
|
-
];
|
|
18
|
+
// Backward-compatible exports. Default to RND values for any module that
|
|
19
|
+
// imports these constants directly (the symbol surface predates B2 and is
|
|
20
|
+
// referenced by intake-config, prepare, and tests).
|
|
21
|
+
import rndTable from './tables/rnd.mjs';
|
|
22
|
+
export const INTAKE_TYPES = rndTable.INTAKE_TYPES;
|
|
23
|
+
export const RD_STAGES = rndTable.STAGES;
|
|
45
24
|
|
|
46
25
|
export const RECOMMENDED_ACTIONS = [
|
|
47
26
|
'summarize',
|
|
@@ -60,146 +39,36 @@ export const RECOMMENDED_ACTIONS = [
|
|
|
60
39
|
'archive',
|
|
61
40
|
];
|
|
62
41
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
requiresApproval: true,
|
|
80
|
-
},
|
|
81
|
-
{
|
|
82
|
-
intakeType: 'incident',
|
|
83
|
-
keywords: ['incident', 'outage', 'slo breach', 'sla breach', 'latency spike', 'availability', 'down', 'p0 ', 'p1 ', 'pagerduty', '5xx', 'oncall'],
|
|
84
|
-
rdStage: 'operations',
|
|
85
|
-
primaryOwner: 'sre',
|
|
86
|
-
recommendedChain: ['sre', 'debugger', 'platform-engineer'],
|
|
87
|
-
recommendedAction: 'create-runbook',
|
|
88
|
-
risk: 'high',
|
|
89
|
-
requiresApproval: true,
|
|
90
|
-
},
|
|
91
|
-
{
|
|
92
|
-
intakeType: 'legal-compliance',
|
|
93
|
-
keywords: ['gdpr', 'ccpa', 'hipaa', 'sox', 'soc2', 'license', 'lawsuit', 'dpa', 'data retention', 'pii', 'subpoena', 'compliance audit'],
|
|
94
|
-
rdStage: 'operations',
|
|
95
|
-
primaryOwner: 'legal-compliance',
|
|
96
|
-
recommendedChain: ['legal-compliance', 'security', 'product-manager'],
|
|
97
|
-
recommendedAction: 'clarify',
|
|
98
|
-
risk: 'high',
|
|
99
|
-
requiresApproval: true,
|
|
100
|
-
},
|
|
101
|
-
{
|
|
102
|
-
intakeType: 'architecture',
|
|
103
|
-
keywords: ['architecture', 'adr', 'rfc', 'interface', 'tradeoff', 'boundary', 'system design', 'data model', 'api contract', 'migration plan'],
|
|
104
|
-
rdStage: 'design',
|
|
105
|
-
primaryOwner: 'architect',
|
|
106
|
-
recommendedChain: ['architect', 'devil-advocate', 'engineer'],
|
|
107
|
-
recommendedAction: 'draft-rfc',
|
|
108
|
-
risk: 'medium',
|
|
109
|
-
requiresApproval: false,
|
|
110
|
-
},
|
|
111
|
-
{
|
|
112
|
-
intakeType: 'eval-finding',
|
|
113
|
-
keywords: ['eval', 'evaluation', 'hallucination', 'judge', 'trace', 'score regression', 'recall@', 'precision@', 'mrr', 'ndcg', 'failure case', 'rubric'],
|
|
114
|
-
rdStage: 'evaluation',
|
|
115
|
-
primaryOwner: 'evaluator',
|
|
116
|
-
recommendedChain: ['evaluator', 'ai-engineer', 'trace-reviewer'],
|
|
117
|
-
recommendedAction: 'evaluate',
|
|
118
|
-
risk: 'medium',
|
|
119
|
-
requiresApproval: false,
|
|
120
|
-
},
|
|
121
|
-
{
|
|
122
|
-
intakeType: 'bug',
|
|
123
|
-
keywords: ['bug', 'broken', 'error', 'stack trace', 'regression', 'crash', 'exception', 'fails', 'failing', 'throws', 'not working', 'reproduce', 'repro:'],
|
|
124
|
-
rdStage: 'implementation',
|
|
125
|
-
primaryOwner: 'debugger',
|
|
126
|
-
recommendedChain: ['debugger', 'engineer', 'qa', 'reviewer'],
|
|
127
|
-
recommendedAction: 'diagnose',
|
|
128
|
-
risk: 'medium',
|
|
129
|
-
requiresApproval: false,
|
|
130
|
-
},
|
|
131
|
-
{
|
|
132
|
-
intakeType: 'experiment',
|
|
133
|
-
keywords: ['hypothesis', 'experiment', 'spike', 'prototype', 'falsifiable', 'research question', 'a/b test', 'pilot'],
|
|
134
|
-
rdStage: 'hypothesis',
|
|
135
|
-
primaryOwner: 'rd-lead',
|
|
136
|
-
recommendedChain: ['rd-lead', 'researcher', 'evaluator'],
|
|
137
|
-
recommendedAction: 'create-experiment',
|
|
138
|
-
risk: 'low',
|
|
139
|
-
requiresApproval: false,
|
|
140
|
-
},
|
|
141
|
-
{
|
|
142
|
-
intakeType: 'launch-asset',
|
|
143
|
-
keywords: ['release', 'changelog', 'version bump', 'ship', 'launch', 'rollout', 'cut a release', 'rc1', 'rc2', 'release candidate'],
|
|
144
|
-
rdStage: 'release',
|
|
145
|
-
primaryOwner: 'release-manager',
|
|
146
|
-
recommendedChain: ['release-manager', 'qa', 'docs-keeper'],
|
|
147
|
-
recommendedAction: 'release-review',
|
|
148
|
-
risk: 'medium',
|
|
149
|
-
requiresApproval: false,
|
|
150
|
-
},
|
|
151
|
-
{
|
|
152
|
-
intakeType: 'research',
|
|
153
|
-
keywords: ['competitor', 'market', 'pricing', 'positioning', 'industry', 'state of the art', 'literature', 'benchmark study', 'desk research'],
|
|
154
|
-
rdStage: 'research',
|
|
155
|
-
primaryOwner: 'business-strategist',
|
|
156
|
-
recommendedChain: ['business-strategist', 'researcher', 'product-manager'],
|
|
157
|
-
recommendedAction: 'research',
|
|
158
|
-
risk: 'low',
|
|
159
|
-
requiresApproval: false,
|
|
160
|
-
},
|
|
161
|
-
{
|
|
162
|
-
intakeType: 'user-signal',
|
|
163
|
-
keywords: ['customer', 'feedback', 'pain point', 'user says', 'user feedback', 'support ticket', 'churn', 'nps', 'usability', 'frustrated'],
|
|
164
|
-
rdStage: 'signal',
|
|
165
|
-
primaryOwner: 'product-manager',
|
|
166
|
-
recommendedChain: ['product-manager', 'ux-researcher', 'researcher'],
|
|
167
|
-
recommendedAction: 'clarify',
|
|
168
|
-
risk: 'low',
|
|
169
|
-
requiresApproval: false,
|
|
170
|
-
},
|
|
171
|
-
{
|
|
172
|
-
intakeType: 'requirement',
|
|
173
|
-
keywords: ['acceptance criteria', 'requirement', 'must have', 'should have', 'feature request', 'prd', 'use case', 'success metric'],
|
|
174
|
-
rdStage: 'framing',
|
|
175
|
-
primaryOwner: 'product-manager',
|
|
176
|
-
recommendedChain: ['product-manager', 'architect', 'engineer'],
|
|
177
|
-
recommendedAction: 'draft-prd',
|
|
178
|
-
risk: 'low',
|
|
179
|
-
requiresApproval: false,
|
|
180
|
-
},
|
|
181
|
-
{
|
|
182
|
-
intakeType: 'ops',
|
|
183
|
-
keywords: ['runbook', 'cron', 'scheduled job', 'maintenance', 'backup', 'restore', 'capacity plan', 'cost optimization', 'dependency upgrade'],
|
|
184
|
-
rdStage: 'operations',
|
|
185
|
-
primaryOwner: 'operations',
|
|
186
|
-
recommendedChain: ['operations', 'sre', 'engineer'],
|
|
187
|
-
recommendedAction: 'create-runbook',
|
|
188
|
-
risk: 'low',
|
|
189
|
-
requiresApproval: false,
|
|
190
|
-
},
|
|
191
|
-
];
|
|
42
|
+
// Static profile → table map. New curated profiles register themselves here.
|
|
43
|
+
// Custom profiles (escape hatch) declare classificationTable as a repo-relative
|
|
44
|
+
// path; that path is loaded dynamically in classifyRdIntake when a profile arg
|
|
45
|
+
// supplies it.
|
|
46
|
+
import operationsTable from './tables/operations.mjs';
|
|
47
|
+
import creativeTable from './tables/creative.mjs';
|
|
48
|
+
import researchTable from './tables/research.mjs';
|
|
49
|
+
import { outcomeBoost } from '../outcomes/aggregate.mjs';
|
|
50
|
+
|
|
51
|
+
// Outcome boost is a soft re-rank applied when a cwd is supplied. Capped at
|
|
52
|
+
// ±0.05 in outcomeBoost itself, so the primary keyword signal (integer hits)
|
|
53
|
+
// always dominates. Returns 0 if no outcome data exists for the role.
|
|
54
|
+
function outcomeBoostFor(cwd, role) {
|
|
55
|
+
if (!cwd || !role) return 0;
|
|
56
|
+
try { return outcomeBoost(cwd, role); } catch { return 0; }
|
|
57
|
+
}
|
|
192
58
|
|
|
193
|
-
const
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
recommendedAction: 'summarize',
|
|
199
|
-
risk: 'low',
|
|
200
|
-
requiresApproval: false,
|
|
59
|
+
const TABLES = {
|
|
60
|
+
rnd: rndTable,
|
|
61
|
+
operations: operationsTable,
|
|
62
|
+
creative: creativeTable,
|
|
63
|
+
research: researchTable,
|
|
201
64
|
};
|
|
202
65
|
|
|
66
|
+
function resolveTable(profile) {
|
|
67
|
+
if (!profile) return rndTable;
|
|
68
|
+
const id = typeof profile === 'string' ? profile : profile.id;
|
|
69
|
+
return TABLES[id] || rndTable;
|
|
70
|
+
}
|
|
71
|
+
|
|
203
72
|
function normalize(text) {
|
|
204
73
|
return String(text || '').toLowerCase();
|
|
205
74
|
}
|
|
@@ -225,27 +94,35 @@ function buildSignalText({ sourcePath, extractedText, related }) {
|
|
|
225
94
|
}
|
|
226
95
|
|
|
227
96
|
/**
|
|
228
|
-
* Classify an
|
|
97
|
+
* Classify an intake signal against the active profile (defaults to RND).
|
|
98
|
+
*
|
|
99
|
+
* Backward-compatible: callers that did not pass a `profile` arg keep getting
|
|
100
|
+
* RND output, byte-identical to the pre-B2 behavior.
|
|
229
101
|
*
|
|
230
|
-
*
|
|
231
|
-
*
|
|
232
|
-
*
|
|
102
|
+
* @param {object} input
|
|
103
|
+
* @param {string} [input.sourcePath]
|
|
104
|
+
* @param {string} [input.extractedText]
|
|
105
|
+
* @param {Array} [input.related]
|
|
106
|
+
* @param {string|object} [input.profile] - profile id (string) or full profile object
|
|
233
107
|
*/
|
|
234
|
-
export function classifyRdIntake({ sourcePath = '', extractedText = '', related = [] } = {}) {
|
|
108
|
+
export function classifyRdIntake({ sourcePath = '', extractedText = '', related = [], profile = null, cwd = null } = {}) {
|
|
109
|
+
const table = resolveTable(profile);
|
|
235
110
|
const signal = buildSignalText({ sourcePath, extractedText, related });
|
|
236
111
|
|
|
237
112
|
let best = null;
|
|
238
|
-
for (const entry of CLASSIFICATION_TABLE) {
|
|
113
|
+
for (const entry of table.CLASSIFICATION_TABLE) {
|
|
239
114
|
const { hits, matched } = countMatches(signal, entry.keywords);
|
|
240
115
|
if (hits === 0) continue;
|
|
241
|
-
|
|
242
|
-
|
|
116
|
+
const boost = cwd ? outcomeBoostFor(cwd, entry.primaryOwner) : 0;
|
|
117
|
+
const score = hits + boost;
|
|
118
|
+
if (!best || score > best.score) {
|
|
119
|
+
best = { entry, hits, matched, score };
|
|
243
120
|
}
|
|
244
121
|
}
|
|
245
122
|
|
|
246
123
|
if (!best) {
|
|
247
124
|
return {
|
|
248
|
-
...UNKNOWN_TRIAGE,
|
|
125
|
+
...table.UNKNOWN_TRIAGE,
|
|
249
126
|
confidence: 0.3,
|
|
250
127
|
rationale: 'No classification keywords matched filename, content excerpt, or related-doc titles.',
|
|
251
128
|
};
|
|
@@ -272,7 +149,8 @@ export function classifyRdIntake({ sourcePath = '', extractedText = '', related
|
|
|
272
149
|
export function formatTriageLine(sourcePath, triage) {
|
|
273
150
|
const basename = sourcePath ? path.basename(sourcePath) : '(unknown source)';
|
|
274
151
|
if (!triage || triage.intakeType === 'unknown') {
|
|
275
|
-
return `${basename} → unclassified · owner: orchestrator · next: summarize`;
|
|
152
|
+
return `${basename} → unclassified · owner: ${triage?.primaryOwner ?? 'orchestrator'} · next: ${triage?.recommendedAction ?? 'summarize'}`;
|
|
276
153
|
}
|
|
277
|
-
|
|
154
|
+
const ownerLabel = triage.primaryOwner ?? 'unassigned';
|
|
155
|
+
return `${basename} → ${triage.intakeType} / ${triage.rdStage} · owner: ${ownerLabel} · next: ${triage.recommendedAction}`;
|
|
278
156
|
}
|
package/lib/intake/prepare.mjs
CHANGED
|
@@ -29,6 +29,7 @@ import { suggestDocsLaneForFile } from '../docs-routing.mjs';
|
|
|
29
29
|
import { createIntakeQueue } from './queue.mjs';
|
|
30
30
|
import { classifyRdIntake } from './classify.mjs';
|
|
31
31
|
import { detectCustomerMentions, linkSignalToCustomer, updateCustomerProfile } from '../embed/customer-profiles.mjs';
|
|
32
|
+
import { resolveActiveProfile } from '../profiles/loader.mjs';
|
|
32
33
|
|
|
33
34
|
const DEFAULT_RELATED_LIMIT = 5;
|
|
34
35
|
const EXCERPT_CHARS = 800;
|
|
@@ -67,10 +68,16 @@ export async function prepareIntakeForIngestedFile({
|
|
|
67
68
|
}
|
|
68
69
|
|
|
69
70
|
const lane = suggestDocsLaneForFile(ingestedFile.sourcePath, extracted) || null;
|
|
71
|
+
// Resolve the active profile so non-RND projects route to their own table.
|
|
72
|
+
// Falls back to rnd for any project without explicit configuration, which
|
|
73
|
+
// preserves prior behavior for every existing user.
|
|
74
|
+
const activeProfile = resolveActiveProfile(rootDir);
|
|
70
75
|
const triage = classifyFn({
|
|
71
76
|
sourcePath: ingestedFile.sourcePath,
|
|
72
77
|
extractedText: extracted,
|
|
73
78
|
related,
|
|
79
|
+
profile: activeProfile?.id,
|
|
80
|
+
cwd: rootDir,
|
|
74
81
|
});
|
|
75
82
|
|
|
76
83
|
// Detect customer mentions and update profiles
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/intake/tables/creative.mjs — Creative production intake classification.
|
|
3
|
+
*
|
|
4
|
+
* Loop: brief → research → draft → review → publish → measure. Covers content
|
|
5
|
+
* creation, campaigns, design work. Roles are creative-org roles, not engineering.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export const INTAKE_TYPES = ['brief', 'content-request', 'asset', 'experiment', 'report', 'legal-compliance', 'ops', 'unknown'];
|
|
9
|
+
export const STAGES = ['brief', 'research', 'draft', 'review', 'publish', 'measure'];
|
|
10
|
+
|
|
11
|
+
export const UNKNOWN_TRIAGE = {
|
|
12
|
+
intakeType: 'unknown',
|
|
13
|
+
rdStage: 'unknown',
|
|
14
|
+
primaryOwner: 'operator',
|
|
15
|
+
recommendedChain: ['operator'],
|
|
16
|
+
recommendedAction: 'summarize',
|
|
17
|
+
risk: 'low',
|
|
18
|
+
requiresApproval: false,
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export const CLASSIFICATION_TABLE = [
|
|
22
|
+
{
|
|
23
|
+
intakeType: 'legal-compliance',
|
|
24
|
+
keywords: ['gdpr', 'ccpa', 'privacy', 'consent', 'unsubscribe', 'can-spam', 'cookie banner', 'disclosure', 'trademark'],
|
|
25
|
+
rdStage: 'review',
|
|
26
|
+
primaryOwner: 'legal-compliance',
|
|
27
|
+
recommendedChain: ['legal-compliance', 'reviewer'],
|
|
28
|
+
recommendedAction: 'clarify',
|
|
29
|
+
risk: 'high',
|
|
30
|
+
requiresApproval: true,
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
intakeType: 'brief',
|
|
34
|
+
keywords: ['brief', 'campaign', 'launch plan', 'go-to-market', 'gtm', 'positioning', 'narrative', 'audience'],
|
|
35
|
+
rdStage: 'brief',
|
|
36
|
+
primaryOwner: 'product-lead',
|
|
37
|
+
recommendedChain: ['product-lead', 'researcher', 'content-writer'],
|
|
38
|
+
recommendedAction: 'draft-prd',
|
|
39
|
+
risk: 'medium',
|
|
40
|
+
requiresApproval: false,
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
intakeType: 'content-request',
|
|
44
|
+
keywords: ['blog', 'article', 'landing page', 'social post', 'newsletter', 'email copy', 'video script', 'headline', 'post', 'content'],
|
|
45
|
+
rdStage: 'draft',
|
|
46
|
+
primaryOwner: 'content-writer',
|
|
47
|
+
recommendedChain: ['content-writer', 'reviewer'],
|
|
48
|
+
recommendedAction: 'draft-rfc',
|
|
49
|
+
risk: 'low',
|
|
50
|
+
requiresApproval: false,
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
intakeType: 'asset',
|
|
54
|
+
keywords: ['asset', 'logo', 'image', 'banner', 'mockup', 'figma', 'brand guide', 'illustration'],
|
|
55
|
+
rdStage: 'draft',
|
|
56
|
+
primaryOwner: 'designer',
|
|
57
|
+
recommendedChain: ['designer', 'reviewer'],
|
|
58
|
+
recommendedAction: 'draft-rfc',
|
|
59
|
+
risk: 'low',
|
|
60
|
+
requiresApproval: false,
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
intakeType: 'experiment',
|
|
64
|
+
keywords: ['a/b test', 'experiment', 'hypothesis', 'multivariate', 'pilot', 'audience test'],
|
|
65
|
+
rdStage: 'measure',
|
|
66
|
+
primaryOwner: 'data-analyst',
|
|
67
|
+
recommendedChain: ['data-analyst', 'researcher'],
|
|
68
|
+
recommendedAction: 'create-experiment',
|
|
69
|
+
risk: 'low',
|
|
70
|
+
requiresApproval: false,
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
intakeType: 'report',
|
|
74
|
+
keywords: ['report', 'dashboard', 'attribution', 'ctr', 'cpa', 'roas', 'conversion', 'funnel', 'engagement'],
|
|
75
|
+
rdStage: 'measure',
|
|
76
|
+
primaryOwner: 'data-analyst',
|
|
77
|
+
recommendedChain: ['data-analyst', 'product-lead'],
|
|
78
|
+
recommendedAction: 'evaluate',
|
|
79
|
+
risk: 'low',
|
|
80
|
+
requiresApproval: false,
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
intakeType: 'ops',
|
|
84
|
+
keywords: ['process', 'workflow', 'template', 'sla', 'calendar', 'planning', 'editorial calendar'],
|
|
85
|
+
rdStage: 'review',
|
|
86
|
+
primaryOwner: 'operator',
|
|
87
|
+
recommendedChain: ['operator', 'product-lead'],
|
|
88
|
+
recommendedAction: 'create-runbook',
|
|
89
|
+
risk: 'low',
|
|
90
|
+
requiresApproval: false,
|
|
91
|
+
},
|
|
92
|
+
];
|
|
93
|
+
|
|
94
|
+
export default { INTAKE_TYPES, STAGES, CLASSIFICATION_TABLE, UNKNOWN_TRIAGE };
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/intake/tables/operations.mjs — Operations intake classification.
|
|
3
|
+
*
|
|
4
|
+
* Loop: request → triage → resolve → document → improve. Optimized for teams
|
|
5
|
+
* whose primary work is fulfilling requests and keeping things running, not
|
|
6
|
+
* building new product. Owners are operations roles (operator, sre, engineer).
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export const INTAKE_TYPES = ['request', 'bug', 'incident', 'ops', 'security', 'docs', 'unknown'];
|
|
10
|
+
export const STAGES = ['request', 'triage', 'resolve', 'document', 'improve'];
|
|
11
|
+
|
|
12
|
+
export const UNKNOWN_TRIAGE = {
|
|
13
|
+
intakeType: 'unknown',
|
|
14
|
+
rdStage: 'unknown',
|
|
15
|
+
primaryOwner: 'operator',
|
|
16
|
+
recommendedChain: ['operator'],
|
|
17
|
+
recommendedAction: 'summarize',
|
|
18
|
+
risk: 'low',
|
|
19
|
+
requiresApproval: false,
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export const CLASSIFICATION_TABLE = [
|
|
23
|
+
{
|
|
24
|
+
intakeType: 'security',
|
|
25
|
+
keywords: ['security', 'cve', 'vulnerability', 'leak', 'auth bypass', 'pii', 'secret'],
|
|
26
|
+
rdStage: 'resolve',
|
|
27
|
+
primaryOwner: 'security',
|
|
28
|
+
recommendedChain: ['security', 'engineer', 'reviewer'],
|
|
29
|
+
recommendedAction: 'diagnose',
|
|
30
|
+
risk: 'high',
|
|
31
|
+
requiresApproval: true,
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
intakeType: 'incident',
|
|
35
|
+
keywords: ['incident', 'outage', 'down', 'p0 ', 'p1 ', 'oncall', 'pager', '5xx', 'sla breach'],
|
|
36
|
+
rdStage: 'resolve',
|
|
37
|
+
primaryOwner: 'sre',
|
|
38
|
+
recommendedChain: ['sre', 'engineer'],
|
|
39
|
+
recommendedAction: 'create-runbook',
|
|
40
|
+
risk: 'high',
|
|
41
|
+
requiresApproval: true,
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
intakeType: 'bug',
|
|
45
|
+
keywords: ['bug', 'broken', 'error', 'fails', 'crash', 'exception', 'stack trace'],
|
|
46
|
+
rdStage: 'resolve',
|
|
47
|
+
primaryOwner: 'engineer',
|
|
48
|
+
recommendedChain: ['engineer', 'qa', 'reviewer'],
|
|
49
|
+
recommendedAction: 'diagnose',
|
|
50
|
+
risk: 'medium',
|
|
51
|
+
requiresApproval: false,
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
intakeType: 'docs',
|
|
55
|
+
keywords: ['docs', 'documentation', 'how to', 'guide', 'readme', 'wiki', 'runbook missing'],
|
|
56
|
+
rdStage: 'document',
|
|
57
|
+
primaryOwner: 'docs-keeper',
|
|
58
|
+
recommendedChain: ['docs-keeper', 'reviewer'],
|
|
59
|
+
recommendedAction: 'create-runbook',
|
|
60
|
+
risk: 'low',
|
|
61
|
+
requiresApproval: false,
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
intakeType: 'ops',
|
|
65
|
+
keywords: ['cron', 'scheduled job', 'maintenance', 'backup', 'restore', 'rotation', 'capacity', 'upgrade'],
|
|
66
|
+
rdStage: 'improve',
|
|
67
|
+
primaryOwner: 'sre',
|
|
68
|
+
recommendedChain: ['sre', 'engineer'],
|
|
69
|
+
recommendedAction: 'create-runbook',
|
|
70
|
+
risk: 'low',
|
|
71
|
+
requiresApproval: false,
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
intakeType: 'request',
|
|
75
|
+
keywords: ['request', 'access', 'feature request', 'integration', 'new tool', 'change request', 'how do i', 'can someone'],
|
|
76
|
+
rdStage: 'triage',
|
|
77
|
+
primaryOwner: 'operator',
|
|
78
|
+
recommendedChain: ['operator', 'engineer'],
|
|
79
|
+
recommendedAction: 'clarify',
|
|
80
|
+
risk: 'low',
|
|
81
|
+
requiresApproval: false,
|
|
82
|
+
},
|
|
83
|
+
];
|
|
84
|
+
|
|
85
|
+
export default { INTAKE_TYPES, STAGES, CLASSIFICATION_TABLE, UNKNOWN_TRIAGE };
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/intake/tables/research.mjs — Research intake classification.
|
|
3
|
+
*
|
|
4
|
+
* Loop: question → gather → analyze → synthesize → recommend. Covers analysis,
|
|
5
|
+
* market intelligence, evaluation, advisory work. Output is a written
|
|
6
|
+
* recommendation, not a shipped product.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export const INTAKE_TYPES = ['question', 'study', 'synthesis', 'report', 'experiment', 'ops', 'unknown'];
|
|
10
|
+
export const STAGES = ['question', 'gather', 'analyze', 'synthesize', 'recommend'];
|
|
11
|
+
|
|
12
|
+
export const UNKNOWN_TRIAGE = {
|
|
13
|
+
intakeType: 'unknown',
|
|
14
|
+
rdStage: 'unknown',
|
|
15
|
+
primaryOwner: 'researcher',
|
|
16
|
+
recommendedChain: ['researcher'],
|
|
17
|
+
recommendedAction: 'summarize',
|
|
18
|
+
risk: 'low',
|
|
19
|
+
requiresApproval: false,
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export const CLASSIFICATION_TABLE = [
|
|
23
|
+
{
|
|
24
|
+
intakeType: 'study',
|
|
25
|
+
keywords: ['interview', 'user study', 'survey', 'focus group', 'usability test', 'diary study', 'ethnography'],
|
|
26
|
+
rdStage: 'gather',
|
|
27
|
+
primaryOwner: 'ux-researcher',
|
|
28
|
+
recommendedChain: ['ux-researcher', 'researcher'],
|
|
29
|
+
recommendedAction: 'create-experiment',
|
|
30
|
+
risk: 'low',
|
|
31
|
+
requiresApproval: false,
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
intakeType: 'experiment',
|
|
35
|
+
keywords: ['hypothesis', 'experiment', 'controlled trial', 'a/b test', 'pilot', 'falsifiable'],
|
|
36
|
+
rdStage: 'analyze',
|
|
37
|
+
primaryOwner: 'evaluator',
|
|
38
|
+
recommendedChain: ['evaluator', 'researcher', 'data-analyst'],
|
|
39
|
+
recommendedAction: 'evaluate',
|
|
40
|
+
risk: 'low',
|
|
41
|
+
requiresApproval: false,
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
intakeType: 'report',
|
|
45
|
+
keywords: ['report', 'finding', 'dashboard', 'metric', 'benchmark', 'baseline', 'comparison'],
|
|
46
|
+
rdStage: 'recommend',
|
|
47
|
+
primaryOwner: 'data-analyst',
|
|
48
|
+
recommendedChain: ['data-analyst', 'researcher', 'reviewer'],
|
|
49
|
+
recommendedAction: 'evaluate',
|
|
50
|
+
risk: 'low',
|
|
51
|
+
requiresApproval: false,
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
intakeType: 'synthesis',
|
|
55
|
+
keywords: ['synthesis', 'literature review', 'meta', 'state of the art', 'roundup', 'survey paper', 'taxonomy'],
|
|
56
|
+
rdStage: 'synthesize',
|
|
57
|
+
primaryOwner: 'researcher',
|
|
58
|
+
recommendedChain: ['researcher', 'reviewer'],
|
|
59
|
+
recommendedAction: 'research',
|
|
60
|
+
risk: 'low',
|
|
61
|
+
requiresApproval: false,
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
intakeType: 'question',
|
|
65
|
+
keywords: ['question', 'research question', 'what is', 'why does', 'how do', 'investigate', 'figure out', 'understand'],
|
|
66
|
+
rdStage: 'question',
|
|
67
|
+
primaryOwner: 'researcher',
|
|
68
|
+
recommendedChain: ['researcher'],
|
|
69
|
+
recommendedAction: 'clarify',
|
|
70
|
+
risk: 'low',
|
|
71
|
+
requiresApproval: false,
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
intakeType: 'ops',
|
|
75
|
+
keywords: ['process', 'methodology', 'template', 'protocol', 'consent form', 'data handling'],
|
|
76
|
+
rdStage: 'gather',
|
|
77
|
+
primaryOwner: 'operator',
|
|
78
|
+
recommendedChain: ['operator', 'researcher'],
|
|
79
|
+
recommendedAction: 'create-runbook',
|
|
80
|
+
risk: 'low',
|
|
81
|
+
requiresApproval: false,
|
|
82
|
+
},
|
|
83
|
+
];
|
|
84
|
+
|
|
85
|
+
export default { INTAKE_TYPES, STAGES, CLASSIFICATION_TABLE, UNKNOWN_TRIAGE };
|