Skip to content

WW-75 — Offer Review & Decision (council-specced design)

Plain-language version: You Got an Offer

Council-gated design, 2026-07-11. Full 3-stage council (Claude / Codex / Gemini / Grok, fable tier; local seat failed) + house councils (gaming: qwen3-next:80b, gpt-oss:20b; streaming: gpt-oss:120b). Unanimous peer ranking: Grok > Claude > Codex > Gemini. Chairman synthesis is the basis of this doc. Report: llm-council/reports/2026-07-11_130158_design-question-for-workwingman-local-first-elec.md (condensed copy in llm-council/reports/COUNCIL-LOG.md).

1. What this feature is — and is not

When the user receives a job offer, WorkWingman helps them work through accept / negotiate / wait. Three levers, not an offer-ranking oracle. The app informs the decision; it never makes it.

Explicitly not built (council-unanimous): a personality test battery, earnings-call NLP/analysis, LLM-drafted per-user negotiation emails, automated "accept" recommendations, any composite offer score.

2. Evaluation axes (priority-ordered by irreversibility)

  1. Real comp — base + bonus + haircut equity, normalized post-tax and cost-of-living-adjusted against the current situation. Reuses CityComparisonService, IncomeTaxEstimator, CompensationInsights, JobImpact, and the equity valuation chain (Stooq → Alpha Vantage → Barchart).
  2. Timing / opportunity cost — offer deadline vs pipeline state, runway pressure. Hardest to model honestly; easiest to fake-precision. See §5.
  3. Hard constraints — geo, family, visa, notice/non-compete. These are gates, not weighted axes: a dealbreaker is never averaged away by a good salary.
  4. Growth delta — role vs the user's stated direction (needs a thin goals model, greenfield).
  5. Stability — will the job exist in two years; equity marked to reality. See §6.
  6. Fit — skills via existing FitAssessment; culture via interview-retro signal. See §7.

Noise (deliberately excluded): daily stock volatility charts, earnings-call analysis, personality typing, benefits micro-comparison, uncalibrated fit percentages.

3. Data gaps — minimal intake (≤8 questions lifetime)

Delivered via a new OfferGapAnalyzer on the ProfileGapAnalyzer pattern (pure static checks, no I/O): surfaces only fields missing for this decision, never re-asks answered ones.

Onboarding (optional, deferrable): - 3 ranked growth themes (skills / domain / scope) + free-text direction → IntakeProfile.GrowthGoals - One-question risk stance (stable pay vs upside) - Household constraint flag (sole earner, dependents) - Must-have / dealbreaker list (feeds the hard-constraint gates)

Offer-time (required, just-in-time): - Offer facts: base / bonus / sign-on / equity / benefits notes / location terms (hard requirement) - Deadline + exploding-offer flag - Runway, bucketed <2 / 2–6 / 6+ months (rides FinancialProfile-style local-only encryption) - Work-values rank, once, if never answered (§7)

4. Output shape — Kerr findings (mandatory, binding)

Hope = the user makes a good career decision. Folly = rewarding "maximize the app's number."

Banned metrics and their failure modes:

Banned Kerr failure
Composite 0–100 offer score False precision; implicit salary weighting; users optimize the score, not the decision
Point pipeline probability ("62%") Guaranteed over-trust at n<10; no honest basis
Equity point-dollars ("RSUs = $340k") Optimism bias; paper value presented as cash
"Negotiation success likelihood" Pure theater, no data
Culture-fit % Unmeasurable; gamed by fluffy retros
Company "health score" Finance theater sliding toward investment advice
"Accept" as primary CTA App decides an irreversible life choice

What ships instead (precedent: FitLevel 4-enum, qualified-only ValueMetrics): - Multi-axis card — each axis an ordinal band Strong / Mixed / Weak / Unknown, with Unknown first-class: missing data must never silently read as neutral. - Narrative tradeoff line per axis, citing stored numbers/sources. - Scenario cards — Accept now / Counter at X band / Ask N-day extension / Wait on Pipeline B — each with explicit assumptions and unknowns. - Decision journal — the last screen records the user's own decision + reasoning. Feeds future calibration; the app closes the loop as a journal, never a verdict. - ValueMetrics extension counts offer/decision events only — never a "good decision" score.

Residual gameables in this design itself (flagged, keep visible): users can re-rank growth themes to justify a company they already want; retro gut-feel is self-report, not ground truth. Both are labeled self-report in the UI.

5. Pipeline expected value + response timing

Honesty ruling: per-user stage calibration is statistically dishonest below ~15 outcomes per stage. Until then, use published stage-conversion base rates as coarse ranges (screen 10–20%, onsite 25–40%, post-final 40–60%), displayed as qualitative bands Unlikely / Possible / Likely with the caption "based on typical rates, not your history." Optional single-band nudge from retro gut-feel ≥4, labeled self-report. No point percentages, no P10/P90 machinery implying rigor the tiny-N data cannot support.

Timing advisor (stage-lag heuristic): compare the offer deadline against each pending pipeline's stage lag — a pipeline at recruiter screen is ≈3–5 weeks from an offer, so waiting is −EV against a 1-week exploding offer; a pipeline post-onsite makes an extension worth asking. Flag waiting as −EV when runway is Low + no late-stage pipelines + exploding deadline.

Extension asks: ship static, verified templates (study-content-verification precedent) — not per-user LLM generation. Never auto-send anything.

6. Company stability context

Shallow and decision-focused. One StabilityContext panel: three enum signals + source links + the fixed framing string "company-stability context, not investment advice."

  • In: layoff/WARN headlines, revenue trend direction over 4 quarters (up/flat/down enum), RSU volatility haircut band for high-vol tickers, private-company caveat ("paper value, illiquid").
  • Out: earnings-call NLP, DCF, analyst targets, chart analytics.

Grounding fact (Grok, verified): FMP is registry-only (ApiKeyProviderRegistry IsWired=false, no FmpSource adapter exists); Alpha Vantage IS DI-wired. No phase blocks on FMP — the stability panel starts with Alpha Vantage + user-pasted links, and degrades gracefully keyless (posted-range + BLS only).

7. Work-style fit (no personality test)

Use O*NET Work Importance work-values (Achievement / Independence / Recognition / Relationships / Support / Working Conditions) — validated, public-domain, one 6-item drag-rank, asked once via the gap analyzer. MBTI rejected unanimously. RIASEC rejected as fit-for-purpose (it measures vocational interests for career choice, not offer-vs-offer environment match — it is not pseudoscience).

Mapping: values rank = stated preference; retro HowItFelt average + WhatWasHard/Surprises themes = observed culture signal; a mismatch surfaces as one narrative line on the Fit axis — never a score, never a veto.

8. Architecture

Offer entity (LocalJsonStore, per-offer — current equity is a single profile grant and needs per-offer instances):

Offer: Id, JobId, ApplicationId, ReceivedAt, Deadline, ExplodingFlag,
  Status (Pending|Negotiating|Accepted|Declined|Expired),
  Comp { Base, Bonus, SignOn, EquityGrantIds[] }, BenefitsNotes, LocationTerms,
  NegotiationNotes, Decision (null|Accept|Negotiate|Wait), DecisionRationale,
  ExtensionAsked, EvidenceRefs, CreatedAt/UpdatedAt

Comp fields ride FinancialProfile-style local-only encryption.

New services: OfferService (CRUD), OfferEvaluationService (orchestrator → banded axes + scenarios), PipelineOutlookService (stage priors + timing heuristics), OfferGapAnalyzer, StabilityContextService (wraps Alpha Vantage; FMP later).

Reused: CityComparisonService, IncomeTaxEstimator, CompensationInsights, JobImpact, equity valuation chain, FitAssessment, InterviewRetro, FinancialProfile.

Extended: EquityGrant to per-offer; IntakeProfile gains GrowthGoals + WorkValues; ValueMetrics gains offer events.

Known gap to schedule: IncomeTaxEstimator is Single-filing-only.

9. Phases

  1. MVP: Offer entity + CRUD, deadline tracker, comp normalization card (JobImpact/COL/tax reuse), banded multi-axis view with Unknown, scenario stubs (no EV math), static extension templates, decision journal.
  2. Pipeline outlook (stage-range priors, qualitative bands), timing advisor, multi-offer side-by-side.
  3. Work-values rank + retro rollup into the Fit axis.
  4. StabilityContext (Alpha Vantage; FMP when an adapter exists), LLM narrative drafts verified against stored facts.

Hard rules: no composite score, no auto-accept, no auto-send, user confirms everything irreversible.

10. House-council dissent log

All house seats agreed on: skip the personality test, no MBTI, lightweight work-values, shallow finance (layoff risk in, stock charts out), small-sample honesty on pipeline estimates.

Overruled dissent: streaming gpt-oss:120b proposed a composite 0–100 "KERR score" alongside the axes — rejected against the unanimous frontier ban and the existing FitLevel/ValueMetrics precedent. Gaming seats used numeric per-axis scores (e.g. "8/10") — ruled to ordinal bands instead.