WW-76 — Veteran Vertical: MOS Translation, USAJOBS Pipeline, Contractor Bids¶
Plain-language version: Getting Out — which states up front that this is a backlog plan, not a shipped feature.
Status: Backlog (specced 2026-07-11, Kerr-checked at requirements altitude)
Epic: Sources / Insights
Why now: converts the YC pitch's veteran-channel claim (COO / Veteran GTM framing, council verdict llm-council/reports/2026-07-11_135155_*) from decorative to load-bearing: ~200k service members separate yearly through TAP/SkillBridge and are the highest-urgency job seekers WorkWingman can serve.
Framing rule (inherited from the pitch council)¶
The vertical is service to transitioning veterans, not extraction from them. Copy, metrics, and UI must read as helping a veteran represent real experience honestly — never as monetizing transition stress. This rule binds every phase.
Kerr guardrails (binding, requirements-altitude pass 2026-07-11)¶
- B vs A: B = veteran lands a right-fit job/contract with experience honestly represented. A (the folly) = translated-skill volume — the DMDC crosswalk maps one MOS to many O*NET skills, so auto-adding crosswalk output would keyword-stuff the profile, inflate every fit band, and pressure the resume drafter against DraftHonestyGuard.
- Alignment fix (core design decision): crosswalk output enters ONLY as proposals through the existing confirm→apply review rows (the WW-39 field-path convention, same seam as résumé paste and the WW-43 LinkedIn import). Provenance-tagged (
source: MOC crosswalk). Never auto-applied. Only confirmed skills feed fit calculation and document generation. - FitLevel enum UNCHANGED (standing Kerr lock).
- Hiring-path / veterans-preference chips = factual posting metadata, never a sort key, filter default, or ranking oracle (WW-74 rule class). Copy states eligibility comes from the posting, not a WW judgement.
- Banned outputs (WW-75 ban class): contract "win likelihood %", composite bid score 0–100, "federal-fit" percentage, translation-quality score shown as a number. Eligibility (set-aside type, NAICS match) = GATES, not weighted axes.
- Bids/applications submitted is never a hero metric. Headline stays the WW-68 outcome-anchored metric ($ per tailored application submitted + kept 7d); federal/contract pipelines join the existing qualified-gated selectivity funnel.
- Translation UI shows the denominator and a confirm prompt ("suggested from your MOS — confirm the ones you actually did"), mirroring the skill-coverage precedent ("3 of 5 listed skills", no suitability claim).
- Tests assert the guardrails (proposals-not-auto-apply, chip-never-sorts, gate-not-score), not proxy values.
Phase 1 — MOS/MOC → civilian skill translation (the wedge)¶
What: a veteran enters their MOS/AFSC/NEC/rating (+ branch, pay grade); WorkWingman proposes civilian-legible skills, occupation matches (O*NET codes), and experience phrasing — through the confirm→apply review flow.
Data source (local-first): bundle the DMDC/O*NET MOC crosswalk as a static data file (public domain, quarterly refresh from the O*NET Resource Center crosswalk files). No API key, works offline, deterministic tests — same pattern as the WW-45 ATS reference vocab corpus. O*NET Web Services MOC search and CareerOneStop MOC APIs documented as optional online enrichment later — not a phase dependency.
Components (C#, follows WW-43a shape):
- MocCrosswalk — pure lookup over the bundled corpus: MOC code (+ branch) → ONET occupations + skill/title suggestions; corpus-integrity test like the WW-44a/45 precedent.
- MocProfileProposalBuilder — crosswalk hits → proposals on the WW-39 field-path convention (skills, suggested experience titles), provenance-tagged.
- /api/vocab/moc/* — code search + suggest endpoints (token-gated, same as /api/vocab/*).
- Angular: onboarding step "Military background (optional)" — MOS picker (prefix search over corpus), proposal rows into the SAME confirm→apply UI as résumé paste/LinkedIn import.
- Tailored-doc drafters: military-context hint (translate jargon, spell out acronyms) added to the prompt context from confirmed profile data only*; DraftHonestyGuard untouched (redesign the reward, never weaken the guard).
Out of scope P1: clearance handling (sensitive; local-only field + encryption question → own ticket), federal résumé format (P2).
Phase 2 — USAJOBS as a first-class source pipeline¶
What: federal postings flow into the same job list/tracker as LinkedIn scrapes — USAJOBS is a pipeline peer, not a separate tab.
API: USAJOBS Search API — free self-service key (email + User-Agent header + Authorization-Key header), REST/JSON, rate-limited per User-Agent. BYO-key pattern (WW policy: user brings key, vault-stored) with in-app "request your free key" guidance — key issuance is instant and free, so onboarding friction is minutes. Codelist endpoints (hiring paths, pay plans, clearance levels) are unauthenticated reference data.
Components:
- UsaJobsSource — search by confirmed skills/ONET-derived keywords + location + hiring-path filters; normalizes postings into the existing job entity (follow the established source-registration pattern; JSON API — no Playwright needed, cheapest source in the fleet).
- Posting chips: hiring paths (veterans, military spouse, disabled-vet), pay plan/grade, announcement close date — informational metadata (Kerr rule above).
- Apply-prep checklist card (informational): federal applications need DD-214 / SF-15 / federal-format résumé — surfaced as a document checklist, not automated.
- Drafter: federal résumé template mode (longer form, duty-detail conventions) as an explicit user-selected template — same honesty guard, same confirmed-data-only rule.
- Apply flow: USAJOBS applications happen on usajobs.gov (login.gov auth) — WW deep-links out and tracks status manually/via user confirmation. The automation never clicks Submit and never touches login.gov* (standing ground rule).
Phase 3 — SAM.gov contract opportunities (individual-contractor pipeline, later)¶
What: for users operating as independent/individual contractors (incl. veteran-owned), federal contract opportunities become a third pipeline: discover → eligibility gates → deadline tracker → link out to bid.
API: SAM.gov Get Opportunities Public API — free key via SAM.gov account, basic tier ~10 requests/day → design = one scheduled daily pull per saved search + cached results + explicit user-refresh budget indicator; BYO-key, vault-stored.
Components:
- SamOpportunitiesSource — saved searches by NAICS code(s) + set-aside type (SDVOSB/VOSB/8(a)/WOSB/none) + keywords; normalized into a distinct ContractOpportunity entity (LocalJsonStore — not the job entity; different lifecycle).
- Eligibility gates, not scores: set-aside match, NAICS match, active/expired — pass/fail chips with the source line quoted. No win-probability anything (Kerr ban).
- Deadline tracker: response-due dates into the existing follow-up/reminder machinery.
- Bid prep = document checklist + link out to sam.gov. WW never submits a bid, never registers an entity, never touches UEI workflows — informational + tracking only.
- Positioning note: this phase also serves the David-Gaffney GTM story (SDVOSB owner, gov-contracting instructor) — the roadmap slide's "down the line" beat.
Phasing & effort¶
| Phase | Ticket scope | Depends on | Effort class |
|---|---|---|---|
| P1 MOS translation | corpus + lookup + proposals + onboarding step | nothing new (WW-39/43/44/45 seams) | Sonnet-standard, ~3-5 files each side |
| P2 USAJOBS source | source + chips + checklist + fed-résumé template | P1 useful not required; BYOK vault seam exists | Sonnet-standard; template mode needs ceiling review (drafter prompt surface) |
| P3 SAM.gov contractor | new entity + source + gates + deadlines | P2 patterns; rate-limit design | Ceiling-specced, Sonnet-built |
Each phase ships behind the standard per-commit council gate; P1's proposal-flow guardrails and P2's drafter template get explicit Kerr lines in review.
Council architecture verdict (2026-07-23, fable tier) — build-ready rulings¶
Full deliberation: llm-council/reports/2026-07-23_175341_architecture-review-for-workwingman-epic-ww-76wi.md (4/5 main seats — local gemma4 timed out; + gaming-PC house council 3/3; consensus ranking Gronktayvius, Clahadore, Cedric, Jenny). These rulings extend the spec above; where they touch deferred items (clearance) they bring them in-scope. All Kerr guardrails above remain binding and unchanged.
Feature toggle (new — epic ships behind it, off by default)¶
SUPERSEDED IN PART (2026-07-23 evening, cross-epic resolution): the education epic's
FeatureFlagsService(WING-102) is the council-ruled SHARED flag infrastructure — backend-persisted (GET/PUT api/settings/features, LocalJsonStorefeaturescollection), fail-closed unknown keys, children AND-ed with master. The standaloneTrackService+ITrackGate/IPC-sync design below is replaced: the federal track registersfederal.master(default OFF) in that shared service, WW-109 becomes the thin consumer layer (nav filter, route guards, settings toggle row), and WW-110 becomes "scheduler consults the shared flag before polling federal sources". One flag store, ever. The composition rule stands: visible = flagOn AND modePolicy (WW-96 UiModeService = presentation only). Details: WING-114/WING-115 descriptions. - GenericTrackService(never per-track):Map<TrackId, WritableSignal<boolean>>, localStorage keywingman-tracks, same shape asThemeServiceand WW-96'sUiModeService. Track ids:federalnow,degree/internshiplater. - Composition with WW-96 Simple Mode (contract ratified with WT-c9de): surface visible ifftrackEnabled(track) && modeAllows(surface). Mode = presentation axis, track = capability axis, orthogonal. TrackService consumes WW-96a'spersistedSignalprimitive once landed (until then: a plainsignal+ manual localStorage persistence, swapped when WW-96a merges); mode composition goes through a localmodeAllows()shim that defaults true until WW-96's UiModeService exists — no hard dependency on UiModeService. - UI enforcement layered: nav filtering hides entry points;canActivateguard on federal routes redirects home (deep-link/bookmark protection);@ifonly for inline elements on shared pages (chips on job detail). - Backend: adapters registered unconditionally inProgram.cs(matches the 20+ existing); execution gated viaITrackGate.IsEnabled("federal")checked by scheduler/orchestrator — dormant = never polled, no key demanded, zero network. Track state syncs frontend→backend settings store over existing IPC. Per-requestenabledTrackspayloads rejected (breaks scheduled pulls). - Simple-mode route policy (WT-c9de contract): MOS onboarding step, federal job chips, apply-prep checklist, résumé template select =simple: true(calm restyle); SAM.gov pipeline + clearance detail = power-only. Confirm→apply keeps confirmation in BOTH modes.
Clearance matching (was deferred; now in-scope)¶
- Extend the existing
EligibilityLogistics.SecurityClearancefield (IntakeProfile.cs) to closed enumNone | PublicTrust | Secret | TS | TsSci— no parallel field. - Encrypted at rest (Offer-comp parity). Local-only; WorkWingman never transmits it on its own and never sells/shares it. The single exception is user-directed: if the user explicitly places their clearance in a confirmed resume field, it flows into documents drafted via their own BYOK drafter — their key, their call, stated in UI copy.
- LLM-payload strip rule (hard): prompt builders strip clearance from all drafting payloads unless the user explicitly places it in a confirmed resume field. Never in source requests, analytics, telemetry.
- Chip = two facts side by side, never a verdict: "Requires Secret — you listed Secret" / "Requires TS/SCI — above your listed clearance", fine print "Self-reported; agency verifies." Never sort key, filter default, or score.
Data-source rulings¶
- USAJOBS hybrid: live discovery = per-user BYO Search key via BYOK vault (user self-registers → ToS "requesting company" is the user; WW ships no shared key; in-app get-key walkthrough). Historic JOA = offline corpus only — closed announcements; never surfaced as applyable jobs (used for coverage tests, crosswalk validation, template fixtures). Codelists bundled at build + periodic refresh.
- SAM.gov: daily-pull + cache design stands (P3, unchanged).
- GovCon employer discovery (Contract Awards/Entity APIs, "cleared employers near you"): out of this epic — separate spike ticket (different entity model, new surface, contends for the 10 req/day budget).
MOS translation & federal resume¶
- P1 crosswalk-only MVP confirmed. LLM NCO/leadership phrasing = post-MVP follow-on through the existing
IClaudeDrafterseam,DraftHonestyGuarduntouched; LLM rephrases only user-confirmed crosswalk output + the user's own record. Warning label for that ticket: military-to-civilian translation is the highest fabrication-risk zone in the product. - Federal resume: new template in
TemplateDrafter+federalprompt mode inClaudeDrafter(no parallel drafter class). Field split: per-application facts (announcement number, target GS/pay plan) captured at draft time on job-scoped apply-prep, never on profile; durable per-role facts (supervisor name/phone/may-contact, hours/week, salary) extend work-history entries. Supervisor contacts + salary history join the encrypted store. Collection = OfferGapAnalyzer-style bounded flow, ≤6 questions/session, resumable, lazily triggered at first federal draft.
GS bands & veterans' preference (MVP — cheap static)¶
- No official GS↔rank table exists (OPM classifies positions, not people) — GS guidance ships as an informational card: "E-5, ~6 yrs often targets GS-7–9. Informational only — no official mapping exists." Never rank→GS as truth.
- Vet preference: static OPM rules engine, self-attested inputs (5-pt/10-pt/none/unsure), chip copy "May claim 5-point preference — claimed on USAJOBS, verified by agency, not WorkWingman." Posting hiring-path chips from codelist. Eligibility = gate/metadata, never a fit axis.
MVP ticket wave (track off by default)¶
| ID | Scope | Tier |
|---|---|---|
| WW-109 (WING-114) | TrackService + route guards + nav filter + modeAllows shim (persistedSignal consumer once WW-96a lands) |
Haiku |
| WW-110 (WING-115) | Backend track-state sync + ITrackGate scheduler gating |
Sonnet |
| WW-111 (WING-116) | USAJOBS BYOK Search adapter + codelist bundle + get-key flow | Sonnet |
| WW-112 (WING-117) | MOC corpus bundle + crosswalk lookup + WW-39 proposal rows + onboarding step | Sonnet |
| WW-113 (WING-118) | Clearance enum + encryption + LLM-payload strip + chips | Ceiling directly (security-sensitive skip-list — escalation-ladder rule overrides the council's Sonnet+review suggestion) |
| WW-114 (WING-119) | Fed resume template + fed prompt mode + job-scoped gap flow + work-history field extensions | Sonnet; the drafter prompt surface itself authored + reviewed at ceiling |
| WW-115 (WING-120) | Vet-pref rules engine + GS band card + hiring-path chips | Haiku |
| WW-116 (WING-121) | Apply-prep checklist + USAJOBS deep-link out (no Submit, no login.gov) | Haiku |
Later wave: LLM MOS/leadership phrasing (ceiling prompt validation); SAM opportunities daily cache (P3); Historic JOA corpus tooling; GovCon employer-discovery spike. WW-78..83 spike outputs feed T3/T4; WW-80/81 settle Historic-hybrid details.
Sources¶
Phase-2 research spikes (deep backlog — GTM-driven)¶
Timeboxed research spikes that de-risk the USAJOBS pipeline before any P2 build. Each is research-only: output = a decision doc + recommendation committed under docs/technical/, no production code. Grounded on the confirmed API surface: USAJOBS exposes three REST APIs — Search (auth: API key via request form + User-Agent + Authorization-Key headers; pages 250/default, up to 500 via ResultsPerPage), Historic JOA (NO auth, bulk dataset), and Announcement Text — plus unauthenticated codelist endpoints (/codelist/hiringpaths, /codelist/occupationalseries, /codelist/militarystatuscodes, payplans, securityclearances, …).
| Spike | GTM goal it serves | Scope / deliverable |
|---|---|---|
| WW-78 — API surface + auth/rate-limit recon | Prove "USAJOBS as a pipeline" is real and cheap before promising it in the pitch | Enumerate all 3 REST APIs + codelists; document the API-key request flow, required User-Agent/Authorization-Key headers, rate limits, and paging (250/500). Deliverable: capability matrix + go/no-go on JSON-only ingestion (no Playwright). |
| WW-79 — Hiring-path & veterans-preference codelist mapping | The veteran-preference surfacing that differentiates our channel | Pull /codelist/hiringpaths + /codelist/militarystatuscodes; map veterans-preference paths (vet, 30%-disabled, disabled, mil-spouse, VEOA/VRA) to our neutral posting-chip taxonomy. Kerr gate: chips are factual posting metadata, never sort keys/scores. Deliverable: mapping table + chip spec. |
| WW-80 — Occupational-series ↔ MOS crosswalk join | Closes the core loop: a veteran's MOS → real federal jobs | Join USAJOBS /codelist/occupationalseries to the DoD/O*NET MOC crosswalk (P1). Produce MOS → federal series → Search-keyword query builder. Deliverable: join feasibility doc + coverage/gaps report (which MOSs map cleanly, which don't). |
| WW-81 — Search (BYO-key) vs Historic JOA (bulk) ingestion strategy | Distribution friction: how fast a veteran gets value on install | Compare per-user BYO Search key (rate limit, minutes-to-issue onboarding friction) vs Historic JOA bulk pull (no auth, our-side ingest, freshness lag). Deliverable: architecture decision (which powers discovery vs refresh) + onboarding-friction estimate. |
| WW-82 — Federal apply-path reality (login.gov, DD-214/SF-15, federal résumé) | Honest end-to-end path, not just discovery — the pitch's credibility | Research what a USAJOBS application actually requires: login.gov auth (which WW never touches — deep-link out only), required docs (DD-214, SF-15), federal-résumé conventions. Deliverable: apply-prep checklist spec + federal-résumé template scope. |
| WW-83 — SAM.gov opportunities API recon (P3 precursor) | The contractor pipeline for veterans going independent | Endpoints, key tiers (~10 req/day basic), set-aside/NAICS filters, and the entity-registration (UEI/SAM) reality for an individual contractor. Deliverable: P3 feasibility doc + daily-pull + cache design; flags whether individual-contractor bidding is realistic for our users. |
Sequencing: WW-78 first (unblocks all others); WW-79/WW-80 parallel (codelist-driven); WW-81 depends on WW-78's rate-limit findings; WW-82 independent; WW-83 is the P3 on-ramp. All six are deep backlog — pulled forward only when P2 is greenlit.