@forwardimpact/outpost 3.0.3

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.
Files changed (120) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +15 -0
  3. package/bin/fit-outpost.js +3 -0
  4. package/config/scheduler.json +34 -0
  5. package/package.json +63 -0
  6. package/src/agent-runner.js +189 -0
  7. package/src/index.js +5 -0
  8. package/src/kb-manager.js +228 -0
  9. package/src/outpost.js +417 -0
  10. package/src/scheduler.js +195 -0
  11. package/src/socket-server.js +336 -0
  12. package/src/state-manager.js +126 -0
  13. package/templates/.claude/agents/chief-of-staff.md +58 -0
  14. package/templates/.claude/agents/concierge.md +46 -0
  15. package/templates/.claude/agents/head-hunter.md +57 -0
  16. package/templates/.claude/agents/librarian.md +60 -0
  17. package/templates/.claude/agents/postman.md +47 -0
  18. package/templates/.claude/agents/recruiter.md +63 -0
  19. package/templates/.claude/settings.json +88 -0
  20. package/templates/.claude/skills/candidate-report/SKILL.md +139 -0
  21. package/templates/.claude/skills/candidate-report/references/report-template.html +203 -0
  22. package/templates/.claude/skills/candidate-report/references/report.css +438 -0
  23. package/templates/.claude/skills/candidate-report/references/rubric.md +76 -0
  24. package/templates/.claude/skills/candidate-report/scripts/render-pdf.mjs +48 -0
  25. package/templates/.claude/skills/deck-create/SKILL.md +75 -0
  26. package/templates/.claude/skills/deck-create/references/slide.css +35 -0
  27. package/templates/.claude/skills/deck-create/scripts/convert-to-pdf.mjs +47 -0
  28. package/templates/.claude/skills/deck-summarize/SKILL.md +139 -0
  29. package/templates/.claude/skills/deck-summarize/references/brief-template.md +85 -0
  30. package/templates/.claude/skills/deck-summarize/scripts/extract-pptx.mjs +210 -0
  31. package/templates/.claude/skills/doc-collab/SKILL.md +112 -0
  32. package/templates/.claude/skills/doc-create/SKILL.md +105 -0
  33. package/templates/.claude/skills/doc-create/scripts/convert-to-pdf.mjs +50 -0
  34. package/templates/.claude/skills/draft-emails/SKILL.md +141 -0
  35. package/templates/.claude/skills/draft-emails/references/template.md +26 -0
  36. package/templates/.claude/skills/draft-emails/scripts/scan-emails.mjs +66 -0
  37. package/templates/.claude/skills/draft-emails/scripts/send-email.mjs +139 -0
  38. package/templates/.claude/skills/extract-entities/SKILL.md +187 -0
  39. package/templates/.claude/skills/extract-entities/references/TEMPLATES.md +14 -0
  40. package/templates/.claude/skills/extract-entities/references/conditions.md +55 -0
  41. package/templates/.claude/skills/extract-entities/references/content.md +104 -0
  42. package/templates/.claude/skills/extract-entities/references/links.md +48 -0
  43. package/templates/.claude/skills/extract-entities/references/recruitment.md +57 -0
  44. package/templates/.claude/skills/extract-entities/references/resolution.md +71 -0
  45. package/templates/.claude/skills/extract-entities/references/sources.md +70 -0
  46. package/templates/.claude/skills/extract-entities/references/templates-conditions.md +36 -0
  47. package/templates/.claude/skills/extract-entities/references/templates-goals-priorities.md +63 -0
  48. package/templates/.claude/skills/extract-entities/references/templates-people-orgs.md +67 -0
  49. package/templates/.claude/skills/extract-entities/references/templates-projects-topics.md +68 -0
  50. package/templates/.claude/skills/extract-entities/scripts/state.mjs +131 -0
  51. package/templates/.claude/skills/hyprnote-follow/SKILL.md +174 -0
  52. package/templates/.claude/skills/hyprnote-follow/references/coaching.md +118 -0
  53. package/templates/.claude/skills/hyprnote-follow/references/debrief.md +59 -0
  54. package/templates/.claude/skills/hyprnote-follow/references/meeting-types.md +14 -0
  55. package/templates/.claude/skills/hyprnote-follow/scripts/follow.mjs +243 -0
  56. package/templates/.claude/skills/hyprnote-process/SKILL.md +147 -0
  57. package/templates/.claude/skills/hyprnote-process/references/extraction.md +84 -0
  58. package/templates/.claude/skills/hyprnote-process/references/sessions.md +81 -0
  59. package/templates/.claude/skills/hyprnote-process/scripts/scan.mjs +246 -0
  60. package/templates/.claude/skills/hyprnote-trim/SKILL.md +177 -0
  61. package/templates/.claude/skills/manage-tasks/SKILL.md +152 -0
  62. package/templates/.claude/skills/manage-tasks/references/format.md +73 -0
  63. package/templates/.claude/skills/meeting-prep/SKILL.md +180 -0
  64. package/templates/.claude/skills/organize-files/SKILL.md +146 -0
  65. package/templates/.claude/skills/organize-files/scripts/organize-by-type.mjs +105 -0
  66. package/templates/.claude/skills/organize-files/scripts/summarize.mjs +91 -0
  67. package/templates/.claude/skills/req-assess/SKILL.md +137 -0
  68. package/templates/.claude/skills/req-assess/references/interview-template.md +62 -0
  69. package/templates/.claude/skills/req-assess/references/panel-template.md +117 -0
  70. package/templates/.claude/skills/req-assess/references/rubric.md +65 -0
  71. package/templates/.claude/skills/req-decide/SKILL.md +163 -0
  72. package/templates/.claude/skills/req-decide/references/rubric.md +63 -0
  73. package/templates/.claude/skills/req-decide/references/template.md +118 -0
  74. package/templates/.claude/skills/req-forget/SKILL.md +154 -0
  75. package/templates/.claude/skills/req-forget/references/classify.md +23 -0
  76. package/templates/.claude/skills/req-forget/references/locations.md +59 -0
  77. package/templates/.claude/skills/req-forget/references/report-template.md +67 -0
  78. package/templates/.claude/skills/req-scan/SKILL.md +145 -0
  79. package/templates/.claude/skills/req-scan/references/fallbacks.md +76 -0
  80. package/templates/.claude/skills/req-scan/references/filters.md +83 -0
  81. package/templates/.claude/skills/req-scan/references/sources.md +76 -0
  82. package/templates/.claude/skills/req-scan/references/state.md +54 -0
  83. package/templates/.claude/skills/req-scan/references/template.md +36 -0
  84. package/templates/.claude/skills/req-scan/scripts/state.mjs +396 -0
  85. package/templates/.claude/skills/req-screen/SKILL.md +151 -0
  86. package/templates/.claude/skills/req-screen/references/rubric.md +94 -0
  87. package/templates/.claude/skills/req-screen/references/template.md +91 -0
  88. package/templates/.claude/skills/req-track/SKILL.md +189 -0
  89. package/templates/.claude/skills/req-track/references/fields.md +114 -0
  90. package/templates/.claude/skills/req-track/references/signals.md +42 -0
  91. package/templates/.claude/skills/req-track/references/statuses.md +32 -0
  92. package/templates/.claude/skills/req-track/references/templates.md +127 -0
  93. package/templates/.claude/skills/req-workday/SKILL.md +162 -0
  94. package/templates/.claude/skills/req-workday/references/field-mapping.md +40 -0
  95. package/templates/.claude/skills/req-workday/references/status-mapping.md +38 -0
  96. package/templates/.claude/skills/req-workday/references/templates.md +126 -0
  97. package/templates/.claude/skills/req-workday/references/xlsx-format.md +56 -0
  98. package/templates/.claude/skills/req-workday/scripts/parse-workday.mjs +313 -0
  99. package/templates/.claude/skills/send-chat/SKILL.md +170 -0
  100. package/templates/.claude/skills/sync-apple-calendar/SKILL.md +145 -0
  101. package/templates/.claude/skills/sync-apple-calendar/references/SCHEMA.md +80 -0
  102. package/templates/.claude/skills/sync-apple-calendar/scripts/query.mjs +307 -0
  103. package/templates/.claude/skills/sync-apple-calendar/scripts/sync.mjs +332 -0
  104. package/templates/.claude/skills/sync-apple-mail/SKILL.md +151 -0
  105. package/templates/.claude/skills/sync-apple-mail/references/SCHEMA.md +115 -0
  106. package/templates/.claude/skills/sync-apple-mail/scripts/parse-emlx.mjs +389 -0
  107. package/templates/.claude/skills/sync-apple-mail/scripts/sync-helpers.mjs +372 -0
  108. package/templates/.claude/skills/sync-apple-mail/scripts/sync.mjs +239 -0
  109. package/templates/.claude/skills/sync-teams/SKILL.md +177 -0
  110. package/templates/.claude/skills/sync-teams/scripts/idb-reader.mjs +147 -0
  111. package/templates/.claude/skills/sync-teams/scripts/leveldb-reader.mjs +288 -0
  112. package/templates/.claude/skills/sync-teams/scripts/sync.mjs +381 -0
  113. package/templates/.claude/skills/upstream-skill/SKILL.md +130 -0
  114. package/templates/.claude/skills/upstream-skill/references/examples.md +80 -0
  115. package/templates/.claude/skills/weekly-update/SKILL.md +141 -0
  116. package/templates/.claude/skills/weekly-update/references/template.md +55 -0
  117. package/templates/CLAUDE.md +134 -0
  118. package/templates/USER.md +3 -0
  119. package/templates/apm.yml +5 -0
  120. package/templates/knowledge/Briefings/.gitkeep +0 -0
@@ -0,0 +1,154 @@
1
+ ---
2
+ name: req-forget
3
+ description: >
4
+ Process GDPR Article 17 data erasure requests. Finds and removes all personal
5
+ data related to a named individual from the knowledge base, cached data, and
6
+ agent state files. Use when the user receives a right-to-be-forgotten
7
+ request, asks to delete all data about a person, or needs to comply with a
8
+ data erasure obligation.
9
+ compatibility: Requires macOS filesystem access
10
+ ---
11
+
12
+ # Right to Be Forgotten
13
+
14
+ Process data erasure requests under GDPR Article 17. Given a person's name,
15
+ systematically find and remove all personal data from the knowledge base, cached
16
+ synced data, and agent state files. Produces an **erasure report** documenting
17
+ what was found, deleted, and redacted — the compliance audit trail.
18
+
19
+ ## Trigger
20
+
21
+ - A formal GDPR erasure request arrives.
22
+ - The user asks to delete all data about a specific person.
23
+ - A candidate withdraws and requests data deletion.
24
+ - The user asks to "forget" someone.
25
+
26
+ ## Prerequisites
27
+
28
+ - The person's full name (and any known aliases or email addresses).
29
+ - User confirmation before any deletion.
30
+
31
+ ## Inputs
32
+
33
+ - **Name** (required) — full name of the data subject.
34
+ - **Aliases** (optional) — alternative names, maiden names, nicknames.
35
+ - **Emails** (optional) — improves search coverage.
36
+ - **Scope** — `all` (default) or `recruitment-only`.
37
+
38
+ ## Outputs
39
+
40
+ - `knowledge/Erasure/{Name}--{YYYY-MM-DD}.md` — erasure report.
41
+ - Deleted files and redacted references across the knowledge base.
42
+
43
+ <do_confirm_checklist goal="Verify erasure is complete and the audit trail is
44
+ sound">
45
+
46
+ - [ ] User confirmed intent before any deletion.
47
+ - [ ] All discovery recipes ran; inventory covers knowledge, cache, state,
48
+ drafts.
49
+ - [ ] All dedicated files and directories deleted.
50
+ - [ ] All mentions and backlinks redacted from other notes.
51
+ - [ ] Cached email threads, attachments, and calendar entries handled.
52
+ - [ ] Agent state and `graph_processed` cleaned.
53
+ - [ ] Erasure report saved; contains **no** personal data beyond the name and
54
+ actions taken.
55
+ - [ ] Final `rg` search shows only the erasure report as a match.
56
+
57
+ </do_confirm_checklist>
58
+
59
+ ## Procedure
60
+
61
+ ### 0. Confirm intent
62
+
63
+ State to the user:
64
+
65
+ > **Data erasure request for: {Name}**
66
+ >
67
+ > This will permanently delete all personal data related to {Name} from:
68
+ >
69
+ > - Knowledge base notes (People, Candidates, Organizations mentions)
70
+ > - Cached email threads and attachments
71
+ > - Agent state and triage files
72
+ >
73
+ > This action cannot be undone. Proceed?
74
+
75
+ **Wait for explicit confirmation before continuing.**
76
+
77
+ ### 1. Discovery
78
+
79
+ Run every recipe in [references/locations.md](references/locations.md) and
80
+ compile a complete inventory of every file and reference found.
81
+
82
+ ### 2. Classify
83
+
84
+ For each match, pick an action from the table in
85
+ [references/classify.md](references/classify.md). Apply its redaction rules
86
+ where redaction is the action.
87
+
88
+ ### 3. Execute deletions
89
+
90
+ Process most-specific to most-general.
91
+
92
+ **3a. Dedicated files and directories:**
93
+
94
+ ```bash
95
+ rm -rf "knowledge/Candidates/{Name}/"
96
+ rm -f "knowledge/People/{Name}.md"
97
+ find ~/.cache/fit/outpost/apple_mail/attachments/ -iname "*{Name}*" -delete
98
+ ```
99
+
100
+ **3b. Redact mentions in other notes:** read each file, remove the specific
101
+ lines/bullets/sections per the rules in
102
+ [references/classify.md](references/classify.md#redaction-rules), remove broken
103
+ `[[backlinks]]` to deleted notes, write the file back.
104
+
105
+ **3c. Email threads:** delete sole-subject threads; redact paragraphs only in
106
+ multi-person threads.
107
+
108
+ **3d. Agent state files:**
109
+
110
+ ```bash
111
+ for f in ~/.cache/fit/outpost/state/*_triage.md; do
112
+ if rg -q "{Name}" "$f" 2>/dev/null; then
113
+ rg -v "{Name}" "$f" > "$f.tmp" && mv "$f.tmp" "$f"
114
+ fi
115
+ done
116
+ ```
117
+
118
+ **3e. Processing state:** drop deleted paths from `graph_processed`:
119
+
120
+ ```bash
121
+ rg -v "{deleted_path}" ~/.cache/fit/outpost/state/graph_processed \
122
+ > ~/.cache/fit/outpost/state/graph_processed.tmp \
123
+ && mv ~/.cache/fit/outpost/state/graph_processed.tmp \
124
+ ~/.cache/fit/outpost/state/graph_processed
125
+ ```
126
+
127
+ ### 4. Write the erasure report
128
+
129
+ Save to `knowledge/Erasure/{Name}--{YYYY-MM-DD}.md` using the template in
130
+ [references/report-template.md](references/report-template.md). Record **only**
131
+ what was deleted — never CV content, skills, or assessments.
132
+
133
+ ### 5. Verify
134
+
135
+ ```bash
136
+ rg "{Name}" knowledge/ ~/.cache/fit/outpost/ drafts/
137
+ ```
138
+
139
+ The only match should be the erasure report. If other matches remain, process
140
+ them and update the report.
141
+
142
+ ## Scope variants
143
+
144
+ **`recruitment-only`** limits erasure to:
145
+
146
+ - `knowledge/Candidates/{Name}/`
147
+ - `knowledge/Candidates/Insights.md` mentions
148
+ - Recruitment threads (known agency domains)
149
+ - `recruiter_triage.md`
150
+
151
+ Leaves `knowledge/People/{Name}.md` and the wider graph intact — the person may
152
+ be a colleague or non-recruitment contact.
153
+
154
+ **`all`** (default): full erasure across knowledge base, cache, and state.
@@ -0,0 +1,23 @@
1
+ # Reference Classification
2
+
3
+ Reference for `req-forget` Step 2. For every match in the inventory, pick the
4
+ action below.
5
+
6
+ | Reference Type | Action | Example |
7
+ | ---------------------------------- | ------------------------------------------- | ----------------------------------------------- |
8
+ | **Dedicated note** (sole subject) | Delete entire file | `knowledge/People/{Name}.md` |
9
+ | **Dedicated directory** | Delete entire directory | `knowledge/Candidates/{Name}/` |
10
+ | **Mention in another note** | Redact: remove lines referencing the person | Backlink in `knowledge/Organizations/Agency.md` |
11
+ | **Email thread** (sole subject) | Delete file | `~/.cache/fit/outpost/apple_mail/thread.md` |
12
+ | **Email thread** (multiple people) | Redact: remove paragraphs about the person | Thread discussing multiple candidates |
13
+ | **Attachment** (their CV, etc.) | Delete file | `attachments/{thread}/CV.pdf` |
14
+ | **Triage/state file** | Redact: remove lines mentioning them | `recruiter_triage.md` |
15
+ | **Insights file** | Redact: remove bullets mentioning them | `knowledge/Candidates/Insights.md` |
16
+
17
+ ## Redaction rules
18
+
19
+ - Remove entire bullet points that mention the person by name.
20
+ - Remove table rows containing the person's name.
21
+ - Remove `## Connected to` entries linking to deleted notes.
22
+ - If a section becomes empty after redaction, remove its header too.
23
+ - Do **not** remove surrounding context that doesn't identify the person.
@@ -0,0 +1,59 @@
1
+ # Discovery Search Recipes
2
+
3
+ Reference for `req-forget` Step 1. Run every recipe; record every match into the
4
+ inventory.
5
+
6
+ ## Knowledge base — direct notes
7
+
8
+ ```bash
9
+ ls -d "knowledge/Candidates/{Name}/" 2>/dev/null
10
+ ls "knowledge/People/{Name}.md" 2>/dev/null
11
+
12
+ # Common name variations
13
+ ls "knowledge/People/{First} {Last}.md" 2>/dev/null
14
+ ls "knowledge/People/{Last}, {First}.md" 2>/dev/null
15
+ ```
16
+
17
+ ## Knowledge base — backlinks and mentions
18
+
19
+ ```bash
20
+ rg -l "{Name}" knowledge/
21
+ rg -l "{First name} {Last name}" knowledge/
22
+ rg -l "\[\[.*{Name}.*\]\]" knowledge/
23
+ rg -l "{email}" knowledge/
24
+ ```
25
+
26
+ ## Cached email threads
27
+
28
+ ```bash
29
+ rg -l "{Name}" ~/.cache/fit/outpost/apple_mail/ 2>/dev/null
30
+ rg -l "{email}" ~/.cache/fit/outpost/apple_mail/ 2>/dev/null
31
+ find ~/.cache/fit/outpost/apple_mail/attachments/ -iname "*{Name}*" 2>/dev/null
32
+ ```
33
+
34
+ ## Cached calendar events
35
+
36
+ ```bash
37
+ rg -l "{Name}" ~/.cache/fit/outpost/apple_calendar/ 2>/dev/null
38
+ rg -l "{email}" ~/.cache/fit/outpost/apple_calendar/ 2>/dev/null
39
+ ```
40
+
41
+ ## Agent state files
42
+
43
+ ```bash
44
+ rg -l "{Name}" ~/.cache/fit/outpost/state/ 2>/dev/null
45
+ ```
46
+
47
+ ## Drafts
48
+
49
+ ```bash
50
+ rg -l "{Name}" drafts/ 2>/dev/null
51
+ ```
52
+
53
+ ## Final verification
54
+
55
+ ```bash
56
+ rg "{Name}" knowledge/ ~/.cache/fit/outpost/ drafts/
57
+ ```
58
+
59
+ Expected: only the erasure report matches.
@@ -0,0 +1,67 @@
1
+ # Erasure Report Template
2
+
3
+ Audit trail for `req-forget` Step 4. Save to
4
+ `knowledge/Erasure/{Name}--{YYYY-MM-DD}.md`.
5
+
6
+ **The report itself must not contain personal data** beyond the subject's name
7
+ and the actions taken. Do not copy CV content, skills, or assessments into the
8
+ report — record only what was deleted, not what it contained.
9
+
10
+ ````markdown
11
+ # Data Erasure Report — {Full Name}
12
+
13
+ **Date:** {YYYY-MM-DD HH:MM}
14
+ **Requested by:** {user or "GDPR Article 17 request"}
15
+ **Scope:** {all / recruitment-only}
16
+
17
+ ## Data Subject
18
+ - **Name:** {Full Name}
19
+ - **Known aliases:** {aliases or "none"}
20
+ - **Known emails:** {emails or "none"}
21
+
22
+ ## Actions Taken
23
+
24
+ ### Deleted Files
25
+ - `knowledge/Candidates/{Name}/brief.md`
26
+ - `knowledge/Candidates/{Name}/CV.pdf`
27
+ - `knowledge/Candidates/{Name}/screening.md`
28
+ - `knowledge/People/{Name}.md`
29
+ - {list all deleted files}
30
+
31
+ ### Redacted References
32
+ - `knowledge/Organizations/{Agency}.md` — removed backlink
33
+ - `knowledge/Candidates/Insights.md` — removed {N} bullet(s)
34
+ - {list all redacted files and what was removed}
35
+
36
+ ### Cached Data Removed
37
+ - `~/.cache/fit/outpost/apple_mail/{thread}.md` — deleted (sole subject)
38
+ - `~/.cache/fit/outpost/apple_mail/{thread2}.md` — redacted (multi-person)
39
+ - {list all cache actions}
40
+
41
+ ### State Files Cleaned
42
+ - `~/.cache/fit/outpost/state/recruiter_triage.md` — redacted
43
+ - {list all state file actions}
44
+
45
+ ## Requires Manual Action
46
+
47
+ The following are outside this tool's reach:
48
+
49
+ - **Apple Mail** — original emails remain in the user's mailbox. Search
50
+ Mail.app for "{Name}" and delete threads manually.
51
+ - **Apple Calendar** — events remain in Calendar.app.
52
+ - **Recruitment agencies** — notify {Agency} of the deletion and request
53
+ they do the same.
54
+ - **Interview notes** — check physical notebooks and external apps.
55
+ - **Shared documents** — check Google Drive, SharePoint, etc.
56
+
57
+ ## Verification
58
+
59
+ ```bash
60
+ rg "{Name}" knowledge/ ~/.cache/fit/outpost/
61
+ ````
62
+
63
+ Expected: no matches except this erasure report.
64
+
65
+ ```
66
+
67
+ ```
@@ -0,0 +1,145 @@
1
+ ---
2
+ name: req-scan
3
+ description: >
4
+ Scan publicly available sources for candidates who indicate they are open for
5
+ hire. Uses WebFetch to read public APIs (HN Algolia, GitHub, dev.to).
6
+ Writes prospect notes to knowledge/Prospects/. Maintains
7
+ cursor/dedup state in ~/.cache/fit/outpost/head-hunter/. Use when the
8
+ head-hunter agent is woken or when the user asks to scan for open candidates.
9
+ ---
10
+
11
+ # Scan Open Candidates
12
+
13
+ Fetch and filter publicly available candidate posts from platforms where people
14
+ **explicitly indicate** they are open for hire. This skill handles fetching,
15
+ filtering, deduplication, benchmarking, prospect-note writing, and memory
16
+ updates.
17
+
18
+ ## Trigger
19
+
20
+ - The head-hunter agent is woken by the scheduler.
21
+ - The user asks to scan for open candidates or prospects.
22
+
23
+ ## Prerequisites
24
+
25
+ - `WebFetch` tool available (Claude Code built-in — no curl/wget).
26
+ - `fit-pathway` CLI available (`bunx fit-pathway`).
27
+ - Memory directory `~/.cache/fit/outpost/head-hunter/`.
28
+
29
+ ## Inputs
30
+
31
+ - `~/.cache/fit/outpost/head-hunter/cursor.tsv` — source rotation.
32
+ - `~/.cache/fit/outpost/head-hunter/seen.tsv` — deduplication.
33
+ - Standard data via `bunx fit-pathway skill --list` and `bunx fit-pathway job`.
34
+
35
+ ## Outputs
36
+
37
+ - `knowledge/Prospects/{Name}.md`.
38
+ - Updated `cursor.tsv`, `seen.tsv`, `prospects.tsv`, and `log.md`.
39
+ - `~/.cache/fit/outpost/state/head_hunter_triage.md`.
40
+
41
+ <do_confirm_checklist goal="Verify the wake produced a clean, ethical scan">
42
+
43
+ - [ ] Selected the least-recently-checked, non-suspended source.
44
+ - [ ] Fetched via `WebFetch` (never curl/wget).
45
+ - [ ] Applied all 5 filters in order; passed candidates have ≥ 2
46
+ standard-relevant skills.
47
+ - [ ] Deduplicated against `seen.tsv` before processing.
48
+ - [ ] Benchmarked each prospect against a real `bunx fit-pathway job`.
49
+ - [ ] Prospect notes follow the template (no fabricated names).
50
+ - [ ] All state mutations went through `node scripts/state.mjs` — cursor, seen,
51
+ prospects, failures, log.
52
+ - [ ] Triage report written to the state directory; alternative queries (if any)
53
+ logged.
54
+ - [ ] Failures recorded; sources with ≥ 3 consecutive failures suspended.
55
+
56
+ </do_confirm_checklist>
57
+
58
+ ## Procedure
59
+
60
+ ### 1. Pick a source
61
+
62
+ ```bash
63
+ node scripts/state.mjs cursor list
64
+ ```
65
+
66
+ Pick the least-recently-checked source. Skip suspended sources (`failure get` ≥
67
+ 3). If all are suspended, report it in the triage and exit. Source URLs and
68
+ parse fields: [references/sources.md](references/sources.md).
69
+
70
+ ### 2. Fetch and scan
71
+
72
+ Use `WebFetch` per [references/sources.md](references/sources.md). On failure,
73
+ follow [references/filters.md](references/filters.md#failure-handling).
74
+
75
+ ### 3. Filter
76
+
77
+ Apply the 5-filter pipeline in
78
+ [references/filters.md](references/filters.md#filter-pipeline) — signal, dedup,
79
+ geographic, skill alignment, experience level.
80
+
81
+ ### 3b. Fallback — zero new prospects
82
+
83
+ If filtering eliminates every candidate, try up to **3 alternatives** per wake
84
+ from [references/fallbacks.md](references/fallbacks.md). Log every alternative
85
+ in `log.md`.
86
+
87
+ ### 4. Benchmark and write prospect notes
88
+
89
+ For each candidate that passes filters, benchmark:
90
+
91
+ ```bash
92
+ bunx fit-pathway job {discipline} {estimated_level} --track={best_track}
93
+ ```
94
+
95
+ Classify match strength:
96
+
97
+ - **strong** — multiple core skills match, level aligns, location works, plus
98
+ non-traditional signals for forward-deployed.
99
+ - **moderate** — some overlap, level roughly right, minor gaps.
100
+ - **weak** — few matching signals, significant gaps.
101
+
102
+ Write notes for **strong** and **moderate** matches only, using the template in
103
+ [references/template.md](references/template.md).
104
+
105
+ ```bash
106
+ mkdir -p "knowledge/Prospects"
107
+ ```
108
+
109
+ ### 5. Update state
110
+
111
+ All state changes go through `node scripts/state.mjs` — full command reference:
112
+ [references/state.md](references/state.md). Each wake:
113
+
114
+ 1. Update the cursor (`cursor set`).
115
+ 2. Reset failure count on success or increment on failure.
116
+ 3. Append every processed post ID to `seen` (`seen batch` for many).
117
+ 4. Add new prospects (`prospect add`).
118
+ 5. Append the wake summary (`log-wake`).
119
+
120
+ ### 6. Write the triage report
121
+
122
+ Save to `~/.cache/fit/outpost/state/head_hunter_triage.md`:
123
+
124
+ ```markdown
125
+ # Head Hunter Triage — {YYYY-MM-DD HH:MM}
126
+
127
+ ## Last Scan
128
+ Source: {source_id} ({description})
129
+ Posts scanned: {N}
130
+ New prospects: {N}
131
+ Skipped: {N} (dedup: {N}, location: {N}, skill fit: {N})
132
+ Alternative queries tried: {N} ({list, or "none needed"})
133
+
134
+ ## Pipeline Summary
135
+ Total prospects: {N} (strong: {N}, moderate: {N})
136
+ Sources checked today: {list}
137
+ Oldest unchecked source: {source_id} (last: {date})
138
+ Suspended sources: {list with failure counts, or "none"}
139
+
140
+ ## Recent Prospects
141
+ - **{Name}** — {match_strength}, {estimated_level} {track}, {location}
142
+
143
+ ## Retention
144
+ {List prospects older than 90 days not acted on, if any.}
145
+ ```
@@ -0,0 +1,76 @@
1
+ # Fallback Strategies
2
+
3
+ Reference for `req-scan` when a primary source query yields zero new prospects
4
+ after filtering. Try at most **3 alternatives per wake** to stay within rate
5
+ limits.
6
+
7
+ ## Strategy 1 — alternative search terms
8
+
9
+ ### HN
10
+
11
+ - Check the **previous** month's "Who Wants to Be Hired?" thread.
12
+ - Search for `"Who is hiring"` threads (candidates sometimes post in the wrong
13
+ thread; comments may link to candidate profiles).
14
+ - Try
15
+ `https://hn.algolia.com/api/v1/search?query=%22freelancer+available%22&tags=comment`.
16
+
17
+ ### GitHub
18
+
19
+ Search by skill + availability:
20
+
21
+ ```
22
+ WebFetch URL: https://api.github.com/search/users?q=%22data+engineering%22+%22open+to+work%22&per_page=30&sort=joined&order=desc
23
+ WebFetch URL: https://api.github.com/search/users?q=%22full+stack%22+%22available+for+hire%22&per_page=30&sort=joined&order=desc
24
+ WebFetch URL: https://api.github.com/search/users?q=%22devops%22+%22looking+for%22&per_page=30&sort=joined&order=desc
25
+ ```
26
+
27
+ Search repos with README signals:
28
+
29
+ ```
30
+ WebFetch URL: https://api.github.com/search/repositories?q=%22hire+me%22+in:readme&sort=updated&order=desc&per_page=10
31
+ ```
32
+
33
+ Try alternate location terms: Greece, Athens, Warsaw, Bucharest, Sofia,
34
+ Manchester, Edinburgh.
35
+
36
+ ### dev.to
37
+
38
+ Try broader tags: `jobsearch`, `career`, `remotework`, `job`, `hiring`. Or pull
39
+ from a tag and filter by title/description:
40
+
41
+ ```
42
+ WebFetch URL: https://dev.to/api/articles?tag=career&per_page=25
43
+ ```
44
+
45
+ ## Strategy 2 — relax filters
46
+
47
+ If geography eliminated everyone, re-scan without the location filter. Mark
48
+ unmatched-location prospects as "location unconfirmed" in the prospect note.
49
+
50
+ If skill alignment filtered everyone out: lower the bar from 2 standard skills
51
+ to 1; consider transferable skills (strong Python ⇒ likely data integration
52
+ capability); accept adjacent indicators ("machine learning" implies data
53
+ skills).
54
+
55
+ ## Strategy 3 — cross-reference
56
+
57
+ When a source yields very few results, enrich what you do find:
58
+
59
+ - A GitHub profile that links to a blog/portfolio → WebFetch it for more detail
60
+ before deciding skill fit.
61
+ - An HN post that mentions a GitHub username → fetch their GitHub profile for
62
+ richer signal.
63
+
64
+ ## Logging alternatives
65
+
66
+ Log every alternative in `log.md`:
67
+
68
+ ```markdown
69
+ ## 2026-03-05 14:00
70
+
71
+ Source: github_open_to_work
72
+ Primary query: "open to work" location:UK — 30 results, 0 new after dedup
73
+ Alternative 1: "data engineering" "open to work" — 12 results, 1 new prospect
74
+ Alternative 2: "full stack" "available for hire" — 8 results, 0 new
75
+ Stopped after 2 alternatives (1 prospect found)
76
+ ```
@@ -0,0 +1,83 @@
1
+ # Filter Pipeline & Failure Handling
2
+
3
+ Reference for `req-scan` Steps 3 (filter) and 5 (failure handling).
4
+
5
+ ## Filter pipeline
6
+
7
+ Apply in order. Stop at the first that excludes the candidate.
8
+
9
+ ### 1. Open-for-hire signal
10
+
11
+ - HN "Who Wants to Be Hired?" → **auto-pass** (thread is opt-in).
12
+ - GitHub → must have `hireable: true` **or** bio contains an open-to-work
13
+ phrase.
14
+ - dev.to → must be tagged `opentowork` or `lookingforwork`.
15
+
16
+ ### 2. Deduplication
17
+
18
+ ```bash
19
+ node scripts/state.mjs seen check {source} {post_id}
20
+ ```
21
+
22
+ If exit 0 (seen), skip. Otherwise continue.
23
+
24
+ ### 3. Geographic fit
25
+
26
+ Prefer or accept candidates in or open to:
27
+
28
+ - **US East Coast** (NYC, Boston, DC, Philadelphia, …).
29
+ - **UK** (London, Manchester, Edinburgh, …).
30
+ - **EU** — especially Greece, Poland, Romania, Bulgaria.
31
+ - **Remote / Anywhere / Global**.
32
+
33
+ Skip candidates explicitly locked to incompatible regions ("San Francisco only",
34
+ "APAC only"). When location is ambiguous or unstated, include the candidate —
35
+ let the user decide.
36
+
37
+ ### 4. Skill alignment
38
+
39
+ `bunx fit-pathway skill --list` for the standard inventory. Strong signals:
40
+
41
+ **Forward Deployed:** multiple industries / domains, customer-facing projects,
42
+ data integration / analytics / visualization, full-stack with business context,
43
+ non-traditional path (law, policy, academia → tech), AI/ML tool proficiency
44
+ (Claude, GPT, Cursor, "vibe coding").
45
+
46
+ **Platform:** infrastructure / cloud / DevOps, architecture and system design,
47
+ API design and shared services, performance / scalability / reliability.
48
+
49
+ **Minimum bar:** at least 2 standard-relevant skills must be identifiable from
50
+ the post. Skip candidates with purely non-technical profiles.
51
+
52
+ ### 5. Experience level
53
+
54
+ | Signal | Likely level |
55
+ | ------------------------------------------- | ------------ |
56
+ | "junior", "entry-level", 0–2 years | J040 |
57
+ | "mid-level", 3–5 years | J060 |
58
+ | "senior", 5–8 years, "lead" | J070 |
59
+ | "staff", "principal", 8+ years, "architect" | J090+ |
60
+
61
+ When uncertain, default to J060 with low confidence.
62
+
63
+ ## Failure handling
64
+
65
+ When a WebFetch fails (HTTP 4xx, 5xx, timeout, empty response, redirect to a
66
+ block page):
67
+
68
+ 1. Increment the failure count:
69
+ `node scripts/state.mjs failure increment {source}`.
70
+ 2. **Do not retry** the same source in this wake cycle.
71
+ 3. Suspend after 3 consecutive failures — skip during source selection. Retry
72
+ suspended sources once every 7 days to detect recovery.
73
+ 4. Log the failure in `log.md` with HTTP status and error.
74
+ 5. Reset on success: `node scripts/state.mjs failure reset {source}`.
75
+
76
+ ### Common failure patterns
77
+
78
+ | Symptom | Likely cause |
79
+ | ------------------------ | ------------------------------------------------------ |
80
+ | 503 with HTML redirect | Corporate proxy blocking the domain (won't recover) |
81
+ | 403 Forbidden | Auth required or automated requests blocked |
82
+ | 429 Too Many Requests | Rate limited — will recover; don't suspend permanently |
83
+ | Empty response / timeout | Transient — likely recovers next wake |
@@ -0,0 +1,76 @@
1
+ # Sources
2
+
3
+ Reference for `req-scan` Step 2 (fetch & scan). One source per wake cycle.
4
+
5
+ ## 1. Hacker News "Who Wants to Be Hired?"
6
+
7
+ Monthly thread, posted on the 1st.
8
+
9
+ ```
10
+ WebFetch URL: https://hn.algolia.com/api/v1/search?query=%22Who+wants+to+be+hired%22&tags=ask_hn&hitsPerPage=5
11
+ ```
12
+
13
+ The first hit whose title matches "Who wants to be hired?" with `created_at` in
14
+ the current or previous month is the target thread.
15
+
16
+ ```
17
+ WebFetch URL: https://hn.algolia.com/api/v1/items/{objectID}
18
+ ```
19
+
20
+ `children[]` contains top-level comments — each is one candidate. Parse the
21
+ `text` field for:
22
+
23
+ - **Location** ("Location: New York", "NYC / Remote").
24
+ - **Remote** ("Remote: Yes", "Open to remote").
25
+ - **Skills** — tech stack, language/standard mentions.
26
+ - **Experience** — years, role titles, past companies.
27
+ - **Contact** — email (often obfuscated: "name [at] domain [dot] com").
28
+ - **Resume / CV** — links to personal sites, GitHub, LinkedIn.
29
+
30
+ **Cursor:** thread `objectID` plus the ID of the last processed child comment.
31
+ **Rate:** no strict limit; one fetch per wake cycle.
32
+
33
+ ## 2. GitHub Open to Work
34
+
35
+ Search by location (rotate one query per wake):
36
+
37
+ ```
38
+ WebFetch URL: https://api.github.com/search/users?q=%22open+to+work%22+location:UK&per_page=30&sort=joined&order=desc
39
+ WebFetch URL: https://api.github.com/search/users?q=%22open+to+work%22+location:Europe&per_page=30&sort=joined&order=desc
40
+ WebFetch URL: https://api.github.com/search/users?q=%22looking+for+work%22+location:remote&per_page=30&sort=joined&order=desc
41
+ ```
42
+
43
+ Alternate bio phrases to rotate across wakes: `"available for hire"`,
44
+ `"seeking opportunities"`, `"seeking new role"`, `"open to new opportunities"`,
45
+ `"currently exploring"`, `"freelance" "available"`, `"between roles"`,
46
+ `"on the market"`, `"open to opportunities"`.
47
+
48
+ Fetch each promising candidate's full profile:
49
+
50
+ ```
51
+ WebFetch URL: https://api.github.com/users/{login}
52
+ ```
53
+
54
+ Profile fields: `name`, `bio`, `location`, `hireable`, `blog`, `company` (null =
55
+ likely available), `public_repos`, `created_at`.
56
+
57
+ **Cursor:** location query last used + page number. Rotate UK → Europe → Remote
58
+ → repeat. **Rate:** 10 requests/min unauthenticated. Fetch at most 5 full
59
+ profiles per wake (1 search + 5 profile fetches = 6 requests).
60
+
61
+ ## 3. dev.to
62
+
63
+ ```
64
+ WebFetch URL: https://dev.to/api/articles?tag=opentowork&per_page=25
65
+ WebFetch URL: https://dev.to/api/articles?tag=lookingforwork&per_page=25
66
+ ```
67
+
68
+ Fields: `title`, `description`, `user.name`, `user.username`, `url`, `tag_list`,
69
+ `published_at`. Skip articles older than 90 days.
70
+
71
+ **Cursor:** `id` of the most recent article processed. **Rate:** 30 req per 30
72
+ s. One fetch per wake.
73
+
74
+ ## Use WebFetch only
75
+
76
+ **Never use `curl` or `wget`.** WebFetch is the supported transport.