@forwardimpact/basecamp 2.5.0 → 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/config/scheduler.json +6 -6
- package/package.json +1 -1
- package/src/basecamp.js +101 -729
- package/template/.claude/agents/head-hunter.md +35 -34
- package/template/.claude/agents/librarian.md +1 -1
- package/template/.claude/skills/analyze-cv/SKILL.md +39 -7
- package/template/.claude/skills/draft-emails/scripts/send-email.mjs +1 -1
- package/template/.claude/skills/meeting-prep/SKILL.md +7 -4
- package/template/.claude/skills/process-hyprnote/SKILL.md +17 -8
- package/template/.claude/skills/process-hyprnote/scripts/scan.mjs +246 -0
- package/template/.claude/skills/scan-open-candidates/SKILL.md +103 -13
- package/template/.claude/skills/scan-open-candidates/scripts/state.mjs +396 -0
- package/template/.claude/skills/sync-apple-calendar/SKILL.md +41 -0
- package/template/.claude/skills/sync-apple-calendar/scripts/query.mjs +301 -0
- package/template/.claude/skills/synthesize-deck/SKILL.md +296 -0
- package/template/.claude/skills/synthesize-deck/scripts/extract-pptx.mjs +210 -0
- package/template/.claude/skills/track-candidates/SKILL.md +45 -0
- package/template/.claude/skills/workday-requisition/SKILL.md +60 -60
- package/template/.claude/skills/workday-requisition/scripts/parse-workday.mjs +1 -5
- package/template/CLAUDE.md +2 -2
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Query synced Apple Calendar events by date/time window.
|
|
4
|
+
*
|
|
5
|
+
* Reads JSON event files from ~/.cache/fit/basecamp/apple_calendar/ and filters
|
|
6
|
+
* them by date range, time window, or upcoming interval. Designed to eliminate
|
|
7
|
+
* the need for agents to write bespoke calendar-parsing scripts.
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* node scripts/query.mjs --today Today's events
|
|
11
|
+
* node scripts/query.mjs --tomorrow Tomorrow's events
|
|
12
|
+
* node scripts/query.mjs --upcoming 2h Events in the next 2 hours
|
|
13
|
+
* node scripts/query.mjs --date 2026-03-09 Events on a specific date
|
|
14
|
+
* node scripts/query.mjs --range 2026-03-09 2026-03-11 Events in date range
|
|
15
|
+
* node scripts/query.mjs --today --tomorrow Combine multiple filters
|
|
16
|
+
* node scripts/query.mjs --json Output as JSON (default: table)
|
|
17
|
+
* node scripts/query.mjs --include-all-day Include all-day events
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { readFileSync, readdirSync } from "node:fs";
|
|
21
|
+
import { join } from "node:path";
|
|
22
|
+
import { homedir } from "node:os";
|
|
23
|
+
|
|
24
|
+
const HOME = homedir();
|
|
25
|
+
const CAL_DIR = join(HOME, ".cache/fit/basecamp/apple_calendar");
|
|
26
|
+
|
|
27
|
+
if (process.argv.includes("-h") || process.argv.includes("--help")) {
|
|
28
|
+
console.log(`query — filter synced calendar events by date/time
|
|
29
|
+
|
|
30
|
+
Usage:
|
|
31
|
+
node scripts/query.mjs [options]
|
|
32
|
+
|
|
33
|
+
Time filters (combinable):
|
|
34
|
+
--today Events starting today
|
|
35
|
+
--tomorrow Events starting tomorrow
|
|
36
|
+
--upcoming <interval> Events starting within interval (e.g., 2h, 30m, 1d)
|
|
37
|
+
--date <YYYY-MM-DD> Events on a specific date
|
|
38
|
+
--range <start> <end> Events between two dates (inclusive)
|
|
39
|
+
|
|
40
|
+
Output options:
|
|
41
|
+
--json Output as JSON array (default: formatted table)
|
|
42
|
+
--include-all-day Include all-day events (excluded by default)
|
|
43
|
+
--no-attendees Omit attendee names from output
|
|
44
|
+
|
|
45
|
+
Defaults to --today if no time filter is specified.`);
|
|
46
|
+
process.exit(0);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// --- Parse arguments ---
|
|
50
|
+
|
|
51
|
+
const args = process.argv.slice(2);
|
|
52
|
+
const flags = {
|
|
53
|
+
today: args.includes("--today"),
|
|
54
|
+
tomorrow: args.includes("--tomorrow"),
|
|
55
|
+
json: args.includes("--json"),
|
|
56
|
+
includeAllDay: args.includes("--include-all-day"),
|
|
57
|
+
noAttendees: args.includes("--no-attendees"),
|
|
58
|
+
upcoming: null,
|
|
59
|
+
date: null,
|
|
60
|
+
rangeStart: null,
|
|
61
|
+
rangeEnd: null,
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const upcomingIdx = args.indexOf("--upcoming");
|
|
65
|
+
if (upcomingIdx !== -1 && args[upcomingIdx + 1]) {
|
|
66
|
+
flags.upcoming = parseInterval(args[upcomingIdx + 1]);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const dateIdx = args.indexOf("--date");
|
|
70
|
+
if (dateIdx !== -1 && args[dateIdx + 1]) {
|
|
71
|
+
flags.date = args[dateIdx + 1];
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const rangeIdx = args.indexOf("--range");
|
|
75
|
+
if (rangeIdx !== -1 && args[rangeIdx + 1] && args[rangeIdx + 2]) {
|
|
76
|
+
flags.rangeStart = args[rangeIdx + 1];
|
|
77
|
+
flags.rangeEnd = args[rangeIdx + 2];
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Default to --today if no time filter specified
|
|
81
|
+
if (
|
|
82
|
+
!flags.today &&
|
|
83
|
+
!flags.tomorrow &&
|
|
84
|
+
!flags.upcoming &&
|
|
85
|
+
!flags.date &&
|
|
86
|
+
!flags.rangeStart
|
|
87
|
+
) {
|
|
88
|
+
flags.today = true;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Parse an interval string like "2h", "30m", "1d" into milliseconds.
|
|
93
|
+
*/
|
|
94
|
+
function parseInterval(str) {
|
|
95
|
+
const match = str.match(/^(\d+(?:\.\d+)?)\s*(m|min|h|hr|d)$/i);
|
|
96
|
+
if (!match) {
|
|
97
|
+
console.error(`Invalid interval: ${str}. Use e.g., 2h, 30m, 1d`);
|
|
98
|
+
process.exit(1);
|
|
99
|
+
}
|
|
100
|
+
const n = parseFloat(match[1]);
|
|
101
|
+
const unit = match[2].toLowerCase();
|
|
102
|
+
if (unit === "m" || unit === "min") return n * 60 * 1000;
|
|
103
|
+
if (unit === "h" || unit === "hr") return n * 60 * 60 * 1000;
|
|
104
|
+
if (unit === "d") return n * 24 * 60 * 60 * 1000;
|
|
105
|
+
return n * 60 * 60 * 1000; // default hours
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Get the start of a day in local time.
|
|
110
|
+
*/
|
|
111
|
+
function dayStart(date) {
|
|
112
|
+
const d = new Date(date);
|
|
113
|
+
d.setHours(0, 0, 0, 0);
|
|
114
|
+
return d;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Get the end of a day in local time.
|
|
119
|
+
*/
|
|
120
|
+
function dayEnd(date) {
|
|
121
|
+
const d = new Date(date);
|
|
122
|
+
d.setHours(23, 59, 59, 999);
|
|
123
|
+
return d;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// --- Load events ---
|
|
127
|
+
|
|
128
|
+
let files;
|
|
129
|
+
try {
|
|
130
|
+
files = readdirSync(CAL_DIR).filter((f) => f.endsWith(".json"));
|
|
131
|
+
} catch {
|
|
132
|
+
console.error(
|
|
133
|
+
`Calendar directory not found: ${CAL_DIR}\nRun sync first: node scripts/sync.mjs`,
|
|
134
|
+
);
|
|
135
|
+
process.exit(1);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const events = [];
|
|
139
|
+
for (const f of files) {
|
|
140
|
+
try {
|
|
141
|
+
const ev = JSON.parse(readFileSync(join(CAL_DIR, f), "utf8"));
|
|
142
|
+
|
|
143
|
+
// Skip all-day events unless requested
|
|
144
|
+
if (ev.allDay && !flags.includeAllDay) continue;
|
|
145
|
+
|
|
146
|
+
const startStr = ev.start?.dateTime || ev.start?.date;
|
|
147
|
+
if (!startStr) continue;
|
|
148
|
+
|
|
149
|
+
const startDt = new Date(startStr);
|
|
150
|
+
if (isNaN(startDt.getTime())) continue;
|
|
151
|
+
|
|
152
|
+
const endStr = ev.end?.dateTime || ev.end?.date;
|
|
153
|
+
const endDt = endStr ? new Date(endStr) : startDt;
|
|
154
|
+
|
|
155
|
+
events.push({
|
|
156
|
+
summary: ev.summary || "(untitled)",
|
|
157
|
+
start: startDt,
|
|
158
|
+
end: endDt,
|
|
159
|
+
startIso: startStr,
|
|
160
|
+
endIso: endStr || startStr,
|
|
161
|
+
timeZone: ev.start?.timeZone || null,
|
|
162
|
+
allDay: Boolean(ev.allDay),
|
|
163
|
+
location: ev.location || null,
|
|
164
|
+
conferenceUrl: ev.conferenceUrl || null,
|
|
165
|
+
calendar: ev.calendar || null,
|
|
166
|
+
organizer: ev.organizer || null,
|
|
167
|
+
attendees: (ev.attendees || [])
|
|
168
|
+
.filter((a) => !a.self)
|
|
169
|
+
.map((a) => ({ name: a.name || null, email: a.email })),
|
|
170
|
+
});
|
|
171
|
+
} catch {
|
|
172
|
+
// Skip malformed files
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
events.sort((a, b) => a.start - b.start);
|
|
177
|
+
|
|
178
|
+
// --- Filter events ---
|
|
179
|
+
|
|
180
|
+
const now = new Date();
|
|
181
|
+
const today = dayStart(now);
|
|
182
|
+
const todayE = dayEnd(now);
|
|
183
|
+
const tomorrow = dayStart(new Date(now.getTime() + 86400000));
|
|
184
|
+
const tomorrowE = dayEnd(new Date(now.getTime() + 86400000));
|
|
185
|
+
|
|
186
|
+
const filtered = events.filter((ev) => {
|
|
187
|
+
let match = false;
|
|
188
|
+
|
|
189
|
+
if (flags.today) {
|
|
190
|
+
match = match || (ev.start >= today && ev.start <= todayE);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (flags.tomorrow) {
|
|
194
|
+
match = match || (ev.start >= tomorrow && ev.start <= tomorrowE);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (flags.upcoming) {
|
|
198
|
+
const cutoff = new Date(now.getTime() + flags.upcoming);
|
|
199
|
+
match = match || (ev.start >= now && ev.start <= cutoff);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (flags.date) {
|
|
203
|
+
const dStart = dayStart(flags.date + "T00:00:00");
|
|
204
|
+
const dEnd = dayEnd(flags.date + "T00:00:00");
|
|
205
|
+
match = match || (ev.start >= dStart && ev.start <= dEnd);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (flags.rangeStart && flags.rangeEnd) {
|
|
209
|
+
const rStart = dayStart(flags.rangeStart + "T00:00:00");
|
|
210
|
+
const rEnd = dayEnd(flags.rangeEnd + "T00:00:00");
|
|
211
|
+
match = match || (ev.start >= rStart && ev.start <= rEnd);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return match;
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
// --- Output ---
|
|
218
|
+
|
|
219
|
+
if (flags.json) {
|
|
220
|
+
const output = filtered.map((ev) => {
|
|
221
|
+
const obj = {
|
|
222
|
+
summary: ev.summary,
|
|
223
|
+
start: ev.startIso,
|
|
224
|
+
end: ev.endIso,
|
|
225
|
+
timeZone: ev.timeZone,
|
|
226
|
+
allDay: ev.allDay,
|
|
227
|
+
location: ev.location,
|
|
228
|
+
conferenceUrl: ev.conferenceUrl,
|
|
229
|
+
calendar: ev.calendar,
|
|
230
|
+
organizer: ev.organizer,
|
|
231
|
+
};
|
|
232
|
+
if (!flags.noAttendees) {
|
|
233
|
+
obj.attendees = ev.attendees;
|
|
234
|
+
}
|
|
235
|
+
return obj;
|
|
236
|
+
});
|
|
237
|
+
console.log(JSON.stringify(output, null, 2));
|
|
238
|
+
} else {
|
|
239
|
+
// Formatted table output
|
|
240
|
+
if (filtered.length === 0) {
|
|
241
|
+
console.log("No events found.");
|
|
242
|
+
process.exit(0);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Group by date
|
|
246
|
+
const byDate = new Map();
|
|
247
|
+
for (const ev of filtered) {
|
|
248
|
+
const dateKey = ev.start.toLocaleDateString("sv-SE"); // YYYY-MM-DD
|
|
249
|
+
if (!byDate.has(dateKey)) byDate.set(dateKey, []);
|
|
250
|
+
byDate.get(dateKey).push(ev);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
for (const [date, dayEvents] of byDate) {
|
|
254
|
+
const isToday = date === now.toLocaleDateString("sv-SE");
|
|
255
|
+
const isTomorrow =
|
|
256
|
+
date === new Date(now.getTime() + 86400000).toLocaleDateString("sv-SE");
|
|
257
|
+
const label = isToday ? " (today)" : isTomorrow ? " (tomorrow)" : "";
|
|
258
|
+
console.log(`\n=== ${date}${label} ===`);
|
|
259
|
+
|
|
260
|
+
for (const ev of dayEvents) {
|
|
261
|
+
const tz =
|
|
262
|
+
ev.timeZone || Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
263
|
+
const time = ev.allDay
|
|
264
|
+
? "all-day"
|
|
265
|
+
: ev.start.toLocaleTimeString("en-GB", {
|
|
266
|
+
hour: "2-digit",
|
|
267
|
+
minute: "2-digit",
|
|
268
|
+
timeZone: tz,
|
|
269
|
+
});
|
|
270
|
+
const endTime = ev.allDay
|
|
271
|
+
? ""
|
|
272
|
+
: "-" +
|
|
273
|
+
ev.end.toLocaleTimeString("en-GB", {
|
|
274
|
+
hour: "2-digit",
|
|
275
|
+
minute: "2-digit",
|
|
276
|
+
timeZone: tz,
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
// Minutes until start (only for future events)
|
|
280
|
+
let countdown = "";
|
|
281
|
+
if (ev.start > now && flags.upcoming) {
|
|
282
|
+
const mins = Math.round((ev.start - now) / 60000);
|
|
283
|
+
countdown = ` [in ${mins}min]`;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const parts = [`${time}${endTime}`, ev.summary];
|
|
287
|
+
|
|
288
|
+
if (!flags.noAttendees && ev.attendees.length > 0) {
|
|
289
|
+
const names = ev.attendees
|
|
290
|
+
.slice(0, 8)
|
|
291
|
+
.map((a) => a.name || a.email)
|
|
292
|
+
.join(", ");
|
|
293
|
+
parts.push(names);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
console.log(parts.join(" | ") + countdown);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
console.log(`\n${filtered.length} event(s) | now: ${now.toISOString()}`);
|
|
301
|
+
}
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: synthesize-deck
|
|
3
|
+
description: Synthesize PowerPoint decks into engineer-friendly markdown briefs covering Jobs-To-Be-Done, dependencies, and synthetic data needs. Use when the user asks to break down, summarize, or make sense of a slide deck (.pptx) for engineering work.
|
|
4
|
+
compatibility: Node.js only — no external dependencies.
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Synthesize Deck
|
|
8
|
+
|
|
9
|
+
Turn messy PowerPoint specification decks into clear, actionable markdown briefs
|
|
10
|
+
that forward deployed engineers can actually build from. Strips out business
|
|
11
|
+
jargon and focuses on what matters: what needs to be built, what blocks
|
|
12
|
+
progress, and what data you need to start prototyping.
|
|
13
|
+
|
|
14
|
+
## Trigger
|
|
15
|
+
|
|
16
|
+
Run when the user asks to:
|
|
17
|
+
|
|
18
|
+
- Summarize, synthesize, or break down a `.pptx` deck
|
|
19
|
+
- Make sense of a specification or proposal deck for engineering
|
|
20
|
+
- Create an engineering brief from a slide deck
|
|
21
|
+
- Understand what a project deck is actually asking for
|
|
22
|
+
|
|
23
|
+
## Prerequisites
|
|
24
|
+
|
|
25
|
+
- Node.js 18+
|
|
26
|
+
- Input files must be `.pptx` format
|
|
27
|
+
|
|
28
|
+
## Inputs
|
|
29
|
+
|
|
30
|
+
- One or more `.pptx` file paths
|
|
31
|
+
- Optional: specific focus areas the engineer cares about
|
|
32
|
+
|
|
33
|
+
## Outputs
|
|
34
|
+
|
|
35
|
+
- One markdown file per deck (or one combined file for related decks) written to
|
|
36
|
+
`knowledge/Projects/` with the naming pattern
|
|
37
|
+
`{Project Name} - Engineering Brief.md`
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Workflow
|
|
42
|
+
|
|
43
|
+
### Step 1: Extract Text from PPTX
|
|
44
|
+
|
|
45
|
+
Use the extraction script to pull all slide text from the deck:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
node .claude/skills/synthesize-deck/scripts/extract-pptx.mjs "$FILE_PATH"
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
For multiple decks, pass all files at once:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
node .claude/skills/synthesize-deck/scripts/extract-pptx.mjs deck1.pptx deck2.pptx
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
To save the extracted text for analysis:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
node .claude/skills/synthesize-deck/scripts/extract-pptx.mjs "$FILE_PATH" -o /tmp/deck_extract.txt
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Read the full extracted text before proceeding.
|
|
64
|
+
|
|
65
|
+
### Step 2: Identify the Core Problem
|
|
66
|
+
|
|
67
|
+
Read through all slides and answer:
|
|
68
|
+
|
|
69
|
+
- What process or workflow exists today?
|
|
70
|
+
- What is broken, slow, or painful about it?
|
|
71
|
+
- Who suffers from these problems (the actual humans doing the work)?
|
|
72
|
+
|
|
73
|
+
Write this up in plain language. Avoid repeating the deck's marketing framing.
|
|
74
|
+
Describe the problem like you're explaining it to a teammate over coffee.
|
|
75
|
+
|
|
76
|
+
### Step 3: Extract Jobs-To-Be-Done
|
|
77
|
+
|
|
78
|
+
JTBD captures what users actually need to accomplish. For each distinct user
|
|
79
|
+
role identified in the deck, extract jobs using this format:
|
|
80
|
+
|
|
81
|
+
```
|
|
82
|
+
When [situation], I need to [action], so that [outcome].
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
**Rules for good JTBDs:**
|
|
86
|
+
|
|
87
|
+
- Focus on the user's goal, not the proposed solution
|
|
88
|
+
- Use concrete, specific language (not "leverage AI to optimize")
|
|
89
|
+
- One job per statement — don't combine multiple needs
|
|
90
|
+
- Include the "so that" — the outcome matters for prioritization
|
|
91
|
+
- Group by user role/persona
|
|
92
|
+
|
|
93
|
+
**Common traps to avoid:**
|
|
94
|
+
|
|
95
|
+
- Don't just restate the deck's feature list as jobs
|
|
96
|
+
- Don't use the deck's jargon ("orchestration engine", "intelligence hub")
|
|
97
|
+
- A job should make sense even if you threw away the proposed solution
|
|
98
|
+
|
|
99
|
+
### Step 4: Map Dependencies
|
|
100
|
+
|
|
101
|
+
Engineers need to know what blocks them before they can build. Extract three
|
|
102
|
+
categories:
|
|
103
|
+
|
|
104
|
+
#### 4a. Data Dependencies
|
|
105
|
+
|
|
106
|
+
For each data source mentioned or implied:
|
|
107
|
+
|
|
108
|
+
| Data | Where It Lives | Format | Access | Blocker? |
|
|
109
|
+
| ----------- | -------------------------- | ---------------------------------- | ------------------------------- | -------------- |
|
|
110
|
+
| _What data_ | _System/team that owns it_ | _Structured/unstructured/API/file_ | _Do we have it? Can we get it?_ | _Yes/No + why_ |
|
|
111
|
+
|
|
112
|
+
Flag any data that is:
|
|
113
|
+
|
|
114
|
+
- Mentioned but doesn't seem to exist yet
|
|
115
|
+
- Locked behind systems the team may not have access to
|
|
116
|
+
- Unstructured and would need significant preprocessing
|
|
117
|
+
- Subject to compliance/privacy constraints
|
|
118
|
+
|
|
119
|
+
#### 4b. System Dependencies
|
|
120
|
+
|
|
121
|
+
List every external system, API, or platform the solution needs to integrate
|
|
122
|
+
with. For each, note:
|
|
123
|
+
|
|
124
|
+
- What the integration does
|
|
125
|
+
- Whether it's read-only or read-write
|
|
126
|
+
- Whether an API exists or if it's manual/scraping
|
|
127
|
+
- Whether access has been confirmed
|
|
128
|
+
|
|
129
|
+
#### 4c. People Dependencies
|
|
130
|
+
|
|
131
|
+
List approvals, reviews, or co-creation required from specific roles or teams
|
|
132
|
+
before engineering work can proceed. Flag anything that looks like a long
|
|
133
|
+
lead-time dependency (legal review, compliance sign-off, vendor contracts).
|
|
134
|
+
|
|
135
|
+
### Step 5: Define Synthetic Data Needs
|
|
136
|
+
|
|
137
|
+
Engineers need data to prototype before real data pipelines exist. For each core
|
|
138
|
+
feature or use case, specify:
|
|
139
|
+
|
|
140
|
+
**What to generate:**
|
|
141
|
+
|
|
142
|
+
- The data entity (e.g., "copay claims", "enrollment forms", "contract
|
|
143
|
+
documents")
|
|
144
|
+
- Key fields and their types
|
|
145
|
+
- Realistic value ranges and distributions
|
|
146
|
+
- Edge cases that matter (e.g., incomplete forms, anomalous claims)
|
|
147
|
+
- Volume needed for meaningful testing
|
|
148
|
+
|
|
149
|
+
**What to simulate:**
|
|
150
|
+
|
|
151
|
+
- Workflows and state transitions (e.g., case moving from intake to BV to PA)
|
|
152
|
+
- Time-series patterns (e.g., weekly claims with seasonal variation)
|
|
153
|
+
- Multi-actor interactions (e.g., patient submits, specialist reviews, payer
|
|
154
|
+
responds)
|
|
155
|
+
- Error conditions and failure modes
|
|
156
|
+
|
|
157
|
+
**Format guidance:**
|
|
158
|
+
|
|
159
|
+
- Prefer CSV/JSON for structured data
|
|
160
|
+
- Include realistic PII-shaped data (fake names, addresses, IDs) — never real
|
|
161
|
+
PII
|
|
162
|
+
- Include both happy-path and adversarial examples
|
|
163
|
+
- Consider what data would be needed to train/evaluate ML models
|
|
164
|
+
|
|
165
|
+
### Step 6: Summarize the Proposed Solution
|
|
166
|
+
|
|
167
|
+
Describe what the deck is proposing to build, but translate it into engineering
|
|
168
|
+
terms:
|
|
169
|
+
|
|
170
|
+
- What are the core system components?
|
|
171
|
+
- What does each component actually do (in plain terms)?
|
|
172
|
+
- How do they connect to each other?
|
|
173
|
+
- What is the data flow end-to-end?
|
|
174
|
+
- What AI/ML capabilities are needed and what are they actually doing?
|
|
175
|
+
|
|
176
|
+
Avoid just listing the deck's branded feature names. Translate:
|
|
177
|
+
|
|
178
|
+
- "Intelligent Intake Hub" → "OCR + NLP pipeline that extracts structured fields
|
|
179
|
+
from scanned enrollment forms"
|
|
180
|
+
- "Case Intelligence Hub" → "Dashboard pulling case status from CRM + ML risk
|
|
181
|
+
score for each active case"
|
|
182
|
+
- "Copay Guardian" → "Anomaly detection model on weekly claims data that flags
|
|
183
|
+
unusual patterns"
|
|
184
|
+
|
|
185
|
+
### Step 7: Identify What's Missing
|
|
186
|
+
|
|
187
|
+
Call out gaps an engineer would notice:
|
|
188
|
+
|
|
189
|
+
- Features described without clear data sources
|
|
190
|
+
- AI capabilities mentioned without training data strategy
|
|
191
|
+
- Integrations assumed but not detailed
|
|
192
|
+
- User workflows that skip important error/edge cases
|
|
193
|
+
- Metrics promised without measurement infrastructure
|
|
194
|
+
- Timeline vs. scope mismatches
|
|
195
|
+
|
|
196
|
+
### Step 8: Write the Brief
|
|
197
|
+
|
|
198
|
+
Assemble into a single markdown document with this structure:
|
|
199
|
+
|
|
200
|
+
```markdown
|
|
201
|
+
---
|
|
202
|
+
project: {Project Name}
|
|
203
|
+
source: {list of deck filenames}
|
|
204
|
+
date_synthesized: {today's date}
|
|
205
|
+
status: engineering-brief
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
# {Project Name} — Engineering Brief
|
|
209
|
+
|
|
210
|
+
> One-paragraph plain-English summary of what this project is and why it exists.
|
|
211
|
+
|
|
212
|
+
## The Problem
|
|
213
|
+
|
|
214
|
+
{Step 2 output — what's broken, who it affects, why it matters}
|
|
215
|
+
|
|
216
|
+
## Jobs-To-Be-Done
|
|
217
|
+
|
|
218
|
+
### {Persona 1}
|
|
219
|
+
- When [situation], I need to [action], so that [outcome].
|
|
220
|
+
- ...
|
|
221
|
+
|
|
222
|
+
### {Persona 2}
|
|
223
|
+
- ...
|
|
224
|
+
|
|
225
|
+
## What They Want to Build
|
|
226
|
+
|
|
227
|
+
{Step 6 output — solution in engineering terms, components, data flow}
|
|
228
|
+
|
|
229
|
+
### System Architecture (Simplified)
|
|
230
|
+
|
|
231
|
+
{Text-based diagram or description of how components connect}
|
|
232
|
+
|
|
233
|
+
### AI/ML Components
|
|
234
|
+
|
|
235
|
+
{What models/capabilities are needed, what they do, what data they need}
|
|
236
|
+
|
|
237
|
+
## Dependencies
|
|
238
|
+
|
|
239
|
+
### Data
|
|
240
|
+
{Step 4a table}
|
|
241
|
+
|
|
242
|
+
### Systems & Integrations
|
|
243
|
+
{Step 4b list}
|
|
244
|
+
|
|
245
|
+
### People & Approvals
|
|
246
|
+
{Step 4c list}
|
|
247
|
+
|
|
248
|
+
## Synthetic Data for Prototyping
|
|
249
|
+
|
|
250
|
+
### {Feature/Use Case 1}
|
|
251
|
+
{Step 5 output}
|
|
252
|
+
|
|
253
|
+
### {Feature/Use Case 2}
|
|
254
|
+
{Step 5 output}
|
|
255
|
+
|
|
256
|
+
## Gaps & Open Questions
|
|
257
|
+
|
|
258
|
+
{Step 7 output as a numbered list}
|
|
259
|
+
|
|
260
|
+
## Phasing
|
|
261
|
+
|
|
262
|
+
{Timeline and wave structure from the deck, with engineering commentary on
|
|
263
|
+
what's realistic and what depends on what}
|
|
264
|
+
|
|
265
|
+
## Key Metrics
|
|
266
|
+
|
|
267
|
+
{What success looks like, translated into measurable engineering terms}
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### Step 9: Save and Report
|
|
271
|
+
|
|
272
|
+
1. Write the brief to `knowledge/Projects/{Project Name} - Engineering Brief.md`
|
|
273
|
+
2. Tell the user where the file is and give a 3-sentence summary of the project
|
|
274
|
+
|
|
275
|
+
## Writing Style
|
|
276
|
+
|
|
277
|
+
- **Plain language.** Write like you're briefing a smart engineer who has zero
|
|
278
|
+
context on this project. No "synergize", no "orchestrate", no "leverage".
|
|
279
|
+
- **Concrete over abstract.** "Parse PDF enrollment forms into structured JSON"
|
|
280
|
+
beats "Intelligent document processing capability".
|
|
281
|
+
- **Honest about uncertainty.** If the deck is vague about something important,
|
|
282
|
+
say so. Don't fill gaps with assumptions.
|
|
283
|
+
- **Opinionated where helpful.** If a dependency looks like it will block the
|
|
284
|
+
team for weeks, flag it clearly. If a timeline looks unrealistic given the
|
|
285
|
+
scope, say that.
|
|
286
|
+
- **Short sentences.** Engineers scan, they don't read essays.
|
|
287
|
+
|
|
288
|
+
## Constraints
|
|
289
|
+
|
|
290
|
+
- Never invent requirements not present in the source deck
|
|
291
|
+
- Always flag when you're interpreting vs. directly extracting
|
|
292
|
+
- Keep the brief under 2000 lines — this is a summary, not a transcription
|
|
293
|
+
- Use the knowledge base for additional context about mentioned people, orgs, or
|
|
294
|
+
projects
|
|
295
|
+
- If multiple decks describe related projects, create one combined brief with
|
|
296
|
+
clear sections per project and a shared dependencies section
|