Self-assessment (technical)¶
An optional "know yourself" suite — personality, work style, communication style, learning
style — stored locally and available to feed interview prep and self-marketing. Nothing here
is required to use WorkWingman: every section is null/empty until the user chooses to take it.
Plain-language version: ../plain/self-assessment.md
The one rule that matters most: local vs. Google Forms¶
Everything in this feature is local-only by default. AssessmentService reads and writes
exactly one LocalJsonStore
key ("assessment") under %USERPROFILE%\Wingman\data\assessment.json. There is no network call
anywhere in AssessmentService, BigFiveScorer, or the frontend save paths — see
AssessmentServiceTests.Profile_NeverLeavesTheLocalStoreDirectory for the automated guard.
The one exception is GoogleFormsAssessmentExporter
(GoogleFormsAssessmentExporter.cs),
reachable only via POST /api/assessment/export/google-forms
(AssessmentFormsController, deliberately a separate controller/route from the local
read/write endpoints so it's obvious from routing alone which path leaves the machine). It mirrors
the current profile into a new Google Form in the user's own Drive, via the same OAuth flow
already used for the onboarding-interview form (GoogleFormsClient). This is:
- Explicitly opt-in — never invoked from an automatic save; the frontend only calls it from the "Export (leaves your machine)" button, labeled as such in the UI.
- The only place data leaves the machine. Google Forms/Sheets is a sync target the user chose, never a hosting or scoring backend — the local JSON file stays the source of truth regardless of whether the user ever exports.
Why this distinction matters: Google Forms responses are stored in Google's cloud, under the user's account, subject to Google's retention/access model — not "local" in any sense. A "self-assessment" feature that quietly defaulted to Google Forms would be handing personality data to a third party without the user asking for that. Making the export a clearly-labeled, separate, explicit action is the honest way to offer the convenience (a shareable/exportable copy in Drive) without violating the local-first premise the rest of the app makes everywhere else.
Big Five personality — IPIP, public domain, scored locally¶
We use the 50-item IPIP Big-Five Factor Markers (Goldberg, L. R. (1992). The development of markers for the Big-Five factor structure. Psychological Assessment, 4, 26-42), sourced from the International Personality Item Pool (https://ipip.ori.org/new_ipip-50-item-scale.htm). The IPIP project's own public-domain statement:
"because the IPIP is in the public domain, users have complete freedom to use the IPIP in any way that suits their purposes."
No license, fee, or attribution is legally required to use these items — the Goldberg citation above is included as good academic practice, not because it's mandated.
Why IPIP instead of the commercial NEO-PI-R or a homegrown item set: the IPIP-50 was purpose-built (Goldberg, 1992) to measure the same five constructs as the licensed NEO-PI-R, is extensively validated in the psychometrics literature, and — critically — is the only Big Five option that lets WorkWingman ship a real, published, validated instrument entirely locally, with zero licensing risk.
Items and keying¶
All 50 items live in
IpipItem.cs (IpipBigFiveItemBank.Items), each
tagged with its trait (BigFiveTrait) and whether it's reverse-keyed. Users rate each item 1
("Very Inaccurate") to 5 ("Very Accurate"), per the standard IPIP administration instructions.
One naming note: IPIP's fourth factor is conventionally labeled "Emotional Stability" (higher =
more stable). WorkWingman reports the more commonly recognized Neuroticism framing instead
(higher = more prone to stress) — the item set is identical, but every item's ReverseKeyed flag
for that factor is inverted relative to IPIP's own "Emotional Stability" keying, documented inline
in IpipItem.cs.
Scoring — BigFiveScorer¶
BigFiveScorer.cs is pure and
deterministic — no I/O, no randomness:
public static BigFiveResult Score(IReadOnlyDictionary<int, int> answers)
- Validates every one of the 50 items is present and every rating is 1-5 (
ArgumentExceptionotherwise — a partial or corrupt submission never silently produces a misleading score). - Per trait: sums the 10 items, reverse-scoring (
6 - rating) any item flaggedReverseKeyed. Raw score range is 10-50. - Percentile is a linear 0-100 position within the theoretical 10-50 range — not a population percentile. The IPIP project deliberately publishes no canned norm tables (https://ipip.ori.org/newNorms.htm) and recommends norming against your own sample instead. A transparent within-range position is the honest local-only alternative: WorkWingman never claims "you scored higher than X% of people," because it has no such population data and never will (no telemetry, no server).
- Each trait gets a short plain-language interpretation from a Low/Mid/High band (raw ≤22 / 23-38 / ≥39), five distinct texts per trait (25 total).
Storage and the IAssessmentInsights seam¶
AssessmentService implements two interfaces:
IAssessmentService— get/save the whole profile, plusScoreAndSaveBigFiveAsyncwhich scores and persists in one call.IAssessmentInsights— a read-only, plain-value-object seam (TraitSnapshot,StyleSnapshot) that the planned interview-coaching feature (feature-interview-coachingbranch) consumes instead of depending on this feature's storage shape or IPIP internals directly. Every getter returnsnullwhen the user hasn't taken that section — callers must treat absence as "no signal available," never as a default/neutral trait value.
MBTI: why WorkWingman doesn't administer it¶
The actual Myers-Briggs Type Indicator is a proprietary, paid instrument owned by The Myers-Briggs Company — its items, scoring algorithm, and even the underlying construct definitions are commercial IP. WorkWingman does not reproduce, adapt, or approximate it.
Instead, SelfReportedMbti is a plain self-entry field
(TypeCode, Source, EnteredAt) — the user takes a free instrument elsewhere and types in
whatever result they got. AssessmentResourceMap
(AssessmentResource.cs) curates three
verified-live options (checked at write time, 2026-07):
| Resource | What it is | Free? |
|---|---|---|
| 16Personalities | A NERIS-model 4-letter-type test, not licensed MBTI, but the most widely used free equivalent | Yes (core result) |
| Open Extended Jungian Type Scales | Built from public research data as an explicit open-source MBTI alternative | Yes |
| Truity TypeFinder | Myers-Briggs-style type test; paid upsell for the extended report only | Yes (basic result) |
WorkWingman never fetches, scrapes, or auto-fills from these sites — they're plain outbound links; the user takes the test on their own and copies the result back in.
Work / communication / learning style¶
These three are WorkWingman's own short preference questionnaires
(frontend/src/app/features/self-assessment/style-items.ts), not reproductions of any licensed
instrument:
- Work style takes inspiration from DISC's four-dimension shape (Dominance / Influence /
Steadiness / Conscientiousness), but DISC assessments themselves are commercial products with
restricted item banks — so the 8 items here are self-authored, generic workplace-preference
statements, scored by simple per-dimension averaging (
scoreDimensionsinstyle-items.ts). - Communication style (Direct / Diplomatic / Analytical / Expressive) is likewise a small, self-authored preference set, same scoring approach.
- Learning style takes inspiration from the VARK idea (Visual / Auditory / Reading-Writing / Kinesthetic sensory-modality preference), but VARK itself is copyrighted — its questionnaire "may not be placed on other websites, apps... the questionnaire must not be published on the Internet, in any form" (https://vark-learn.com/copyright-information/). We verified this directly against the VARK copyright page rather than assuming; the 8 items here are WorkWingman's own, inspired by the general sensory-modality concept (which isn't ownable) without touching VARK's actual items or wording.
All three are scored client-side by scoreDimensions/primaryDimension
(frontend/src/app/features/self-assessment/style-items.ts) — a 1-5 rating per item, averaged
per dimension, scaled to 0-100 — then saved to the backend as a WorkStyleResult /
CommunicationStyleResult / LearningStyleResult via the same local-only save path as
everything else in this feature.
API surface¶
AssessmentController (src/WorkWingman.Api/Controllers/AssessmentController.cs):
| Route | Verb | Purpose |
|---|---|---|
/api/assessment |
GET | Full AssessmentProfile |
/api/assessment/ipip-items |
GET | The 50 IPIP items (for rendering the questionnaire) |
/api/assessment/big-five |
POST | Scores + saves; body is { itemNumber: rating } for all 50 items |
/api/assessment/mbti |
PUT | Save SelfReportedMbti |
/api/assessment/work-style |
PUT | Save WorkStyleResult |
/api/assessment/communication-style |
PUT | Save CommunicationStyleResult |
/api/assessment/learning-style |
PUT | Save LearningStyleResult |
/api/assessment/resources |
GET | The curated external-test list |
/api/assessment/export/google-forms |
POST | Leaves the machine — separate controller (AssessmentFormsController) |
Testing¶
BigFiveScorerTests— pure scorer: neutral/all-min/all-max extremes, percentile boundaries, reverse-keying correctness, band-boundary interpretations, missing/out-of-range guards, item bank shape (50 items, 10 per trait, numbers 1-50 unique).AssessmentServiceTests— persistence round-trips per section, non-clobbering across sections, theIAssessmentInsightsread seam, and the local-only-directory assertion.AssessmentResourceMapTests— resource shape (non-empty fields, well-formed https URLs, unique names/URLs), guards that no curated link points back at WorkWingman itself.GoogleFormsAssessmentExporterTests— the one non-local path, tested via Flurl'sHttpTest(no real network), confirming it only fires when explicitly invoked.- Frontend:
self-assessment.spec.ts(panel navigation, Big Five completion/scoring, MBTI blank-guard, all three style submits, the opt-in export flow) andstyle-items.spec.ts(dimension scoring/averaging, tie-breaking). - All manually verified end-to-end in a running instance: took the Big Five (all-3 answers
scored 50/100 on every trait, matching
Score_AllNeutralAnswers_EveryTraitScoresMidpoint), entered a self-reported MBTI type, confirmed the saved JSON on disk.
Related docs¶
- references.md — no new packages were needed (scoring is plain arithmetic); nothing added here.
- security.md / testing.md — general posture this feature follows.