From 9d60b80d54ace11ba0361e72ad9504ee2f11910c Mon Sep 17 00:00:00 2001 From: darthrootbeer Date: Thu, 1 Oct 2026 17:03:07 -0400 Subject: [PATCH] feat(job-assessment): add the intake interview Adds the intake skill, seven stage files, seven plain stage prompts and the intake README. Every write goes through scripts/add_entry.py. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01XV3Ggh86cf2xwUyz28XaN6 --- pipelines/job-assessment/README.md | 2 +- pipelines/job-assessment/intake/README.md | 65 ++++++ pipelines/job-assessment/intake/SKILL.md | 221 ++++++++++++++++++ .../intake/stage-prompts/00-orientation.txt | 28 +++ .../intake/stage-prompts/01-job-history.txt | 29 +++ .../stage-prompts/02-accomplishments.txt | 31 +++ .../intake/stage-prompts/03-skills.txt | 31 +++ .../intake/stage-prompts/04-needs.txt | 38 +++ .../intake/stage-prompts/05-lanes.txt | 32 +++ .../intake/stage-prompts/06-review.txt | 22 ++ .../intake/stages/00-orientation.md | 69 ++++++ .../intake/stages/01-job-history.md | 76 ++++++ .../intake/stages/02-accomplishments.md | 108 +++++++++ .../job-assessment/intake/stages/03-skills.md | 109 +++++++++ .../job-assessment/intake/stages/04-needs.md | 128 ++++++++++ .../job-assessment/intake/stages/05-lanes.md | 76 ++++++ .../job-assessment/intake/stages/06-review.md | 94 ++++++++ 17 files changed, 1158 insertions(+), 1 deletion(-) create mode 100644 pipelines/job-assessment/intake/README.md create mode 100644 pipelines/job-assessment/intake/SKILL.md create mode 100644 pipelines/job-assessment/intake/stage-prompts/00-orientation.txt create mode 100644 pipelines/job-assessment/intake/stage-prompts/01-job-history.txt create mode 100644 pipelines/job-assessment/intake/stage-prompts/02-accomplishments.txt create mode 100644 pipelines/job-assessment/intake/stage-prompts/03-skills.txt create mode 100644 pipelines/job-assessment/intake/stage-prompts/04-needs.txt create mode 100644 pipelines/job-assessment/intake/stage-prompts/05-lanes.txt create mode 100644 pipelines/job-assessment/intake/stage-prompts/06-review.txt create mode 100644 pipelines/job-assessment/intake/stages/00-orientation.md create mode 100644 pipelines/job-assessment/intake/stages/01-job-history.md create mode 100644 pipelines/job-assessment/intake/stages/02-accomplishments.md create mode 100644 pipelines/job-assessment/intake/stages/03-skills.md create mode 100644 pipelines/job-assessment/intake/stages/04-needs.md create mode 100644 pipelines/job-assessment/intake/stages/05-lanes.md create mode 100644 pipelines/job-assessment/intake/stages/06-review.md diff --git a/pipelines/job-assessment/README.md b/pipelines/job-assessment/README.md index e4d6b24..9c304be 100644 --- a/pipelines/job-assessment/README.md +++ b/pipelines/job-assessment/README.md @@ -24,7 +24,7 @@ Each bullet flips from planned to done in the pull request that delivers it. - [ ] **Assessment skill.** The Claude Code skill that runs the assessment end to end. -- [ ] **Intake skill.** The interview skill that builds the central file one checked answer at a time. +- [x] **Intake skill.** The interview skill that builds the central file one checked answer at a time. - [x] **Skills catalog and survey.** A generic skills list and an offline form for scoring yourself against it. diff --git a/pipelines/job-assessment/intake/README.md b/pipelines/job-assessment/intake/README.md new file mode 100644 index 0000000..8ec8164 --- /dev/null +++ b/pipelines/job-assessment/intake/README.md @@ -0,0 +1,65 @@ +# Intake interview + +**What it is.** A Claude Code skill that interviews you, one question at a time, and builds your `career-profile.yaml`: the one file the job assessment reads. It covers your job history, what you did at each job and where the proof of it lives, a 0 to 5 score on a list of skills, what you need from a job (pay, perks, hard limits), and the kinds of role you are looking at. The same interview also comes as plain prompts you can paste into any chat model. + +**What problem it solves.** An assessment can only be as honest as the file behind it. If the file says you are strong at something you only watched others do, every verdict built on it is wrong in your favour. So the interview never writes a fact you did not say, labels where every accomplishment came from, and shows you each entry before it is saved. A skill score is kept as your own claim. It only counts as proven when it points at an accomplishment you described. + +**How it was built.** Built by directing Claude Code. The author designed the interview rules and the question order, reviewed the output, and checked it against the file validator. It was not hand-typed. It is a set of instructions for a model plus the existing checked write script. There is no model training, no retrieval system, and no claim to production ML experience. + +## How it works + +- **`SKILL.md`** holds the rules for every stage and loads one stage file at a time. +- **`stages/00-orientation.md` to `stages/06-review.md`** hold the questions for each stage, in order, and how each answer becomes an entry. +- **`stage-prompts/00-orientation.txt` to `06-review.txt`** are the same seven stages as plain prompts, for people without Claude Code. Paste one per stage, with your current file attached. + +| Stage | What it asks about | What it writes | +|---|---|---| +| 0. Orientation | where the file lives, documents to read first, your name, target roles, years in your field | the new file, `person` | +| 1. Job history | each employer, title and dates | `employers` | +| 2. Accomplishments | 2 to 5 things per employer, who did the work, what shows it happened | `evidence`, each with a source label | +| 3. Skills survey | a 0 to 5 score per skill, by offline form or in chat | `skills` | +| 4. Needs | hard limits, pay, perks, time off, wary phrases, work to avoid | `hard_blocks`, `comp`, `culture` and related lists | +| 5. Lanes | each kind of role and its own must-haves | `lanes` | +| 6. Review | the validator, a gap list, one question per gap, a closing question | `meta` | + +**The rules it keeps.** One question at a time. Read any document you offer before asking what it already says. Never suggest an answer, round up a number or give you more credit than you claimed. Show the exact entry and its source label, and write it only after you say yes. Fix a contradicted entry instead of keeping two. Record each finished stage so a later session picks up where you stopped. + +**One way to write.** Every answer is saved by `scripts/add_entry.py`. It checks the entry, refuses duplicates and dishonest labels (an interview answer marked as checked, for example), writes the file, and runs the full validator. The model never edits the file directly. Without shell access, the model shows the command and you run it. The two exceptions are copying the empty template at the start, and the skills form, whose saved file is merged by `intake/scripts/merge_survey.py`, a second checked script that refuses the whole merge if any answer is invalid. + +## Requirements + +- Python 3.10 or later with the packages in `../requirements.txt` (`pyyaml`, `jsonschema`, `pytest`). +- Claude Code for the skill, or any chat model for the plain prompts. +- Time for about 60 skill questions if you do the skills survey in chat. The offline form is quicker. + +Run everything from `pipelines/job-assessment/`. To start, open Claude Code there and say "interview me for my career profile", or copy `intake/templates/career-profile.template.yaml` and paste `stage-prompts/00-orientation.txt` into your chat model. + +## How it was verified + +- The answers in `../fixtures/robin-sample/intake-answers.txt` (an invented person) were turned into entries the way the stage files describe and written one at a time through `scripts/add_entry.py`, starting from the empty template. `scripts/validate_profile.py` accepted the finished file with no problems. Its warnings were the gaps stage 6 is meant to ask about: accomplishments with no dates, and must-haves with no reason, because the scripted answers never gave them. +- The same run showed the write script refusing a duplicate entry, an interview answer marked as checked, and an authorship value outside the allowed list, each without changing the file. +- Not yet done: a live run of the skill on a model with the scripted answers, saved as a transcript. That run is part of the integration step listed in the [pipeline status](../README.md#status), together with the tests for the prompts below. + +What this does not prove: that the questions find everything worth saying about a career, or that a model will follow every rule on every run. The write script and the validator catch the mistakes that can be checked by code. The rest depends on the model and on you reading each entry before you say yes. + +--- + +### Prompt for your AI model + +Paste one of these into any AI model, together with the files it names. + + + +**Build your own file** (not yet tested on a model) + + +```text +I'm attaching intake/SKILL.md and intake/templates/career-profile.template.yaml. Interview me to build my own career-profile.yaml. Ask exactly one question at a time and wait for my answer. If a question has two parts, ask them separately. Never suggest an answer and never invent a fact, date, number or skill. After each answer, show the exact YAML entry you would add, with its source label, and ask me to confirm it. Anything I cannot point to a document, link or artifact for gets proof: unchecked. Start with stage 0, orientation. A good session ends with a file that passes scripts/validate_profile.py. +``` + +**Find the gaps in your file** (not yet tested on a model) + + +```text +I'm attaching the 'What the validator checks' section of ARCHITECTURE.md and my career-profile.yaml. Find the weak spots the validator cannot catch: skills scored 3 or higher with no evidence ids, unchecked evidence a posting would lean on, evidence with no dates, must-haves with no reason, and lanes whose requirements look copied from each other. For each, give the exact YAML path, why it matters for scoring, and one question you would ask me to fix it. Do not fill any gap yourself. A good answer on fixtures/gappy-profile.yaml finds all three planted gaps. +``` diff --git a/pipelines/job-assessment/intake/SKILL.md b/pipelines/job-assessment/intake/SKILL.md new file mode 100644 index 0000000..2321c34 --- /dev/null +++ b/pipelines/job-assessment/intake/SKILL.md @@ -0,0 +1,221 @@ +--- +name: career-intake +description: > + Interview the user, one question at a time, to build their career-profile.yaml: + job history, accomplishments with a source for every one, a skills self-score, + what they need from a job, and the kinds of role they are looking at. Every + answer is read back as the exact YAML entry and written only after the user + says yes, and only through scripts/add_entry.py. Never invents, rounds up or + upgrades a claim. Resumes at the next unfinished stage. Use when the user + wants to build or extend the central file for the job assessment, says + "interview me for my career profile", or runs "/career-intake". +argument-hint: "[path to career-profile.yaml]" +allowed-tools: [Read, Bash, Glob] +--- + +# Career intake interview + +This skill builds the one file the job assessment reads: `career-profile.yaml`. +It does that by interviewing the user. The file is only as honest as the +interview, so the rules below are not style advice. They are the job. + +**The core rule: the user is the only source of facts.** You ask, you listen, +you write down what was said, and you show it back before it is saved. You never +supply a fact, a date, a number, a skill or a better word for what the user did. + +All paths below are relative to `pipelines/job-assessment/`. Run commands from +that folder. + +--- + +## How the interview runs + +The interview has seven stages, 0 to 6. Each stage has its own file in +`stages/`. Load **only** the file for the stage you are in, and load it when +you reach that stage. Do not read ahead. + +| Stage | File | Writes | +|---|---|---| +| 0. Orientation | `stages/00-orientation.md` | the new file, then `person` | +| 1. Job history | `stages/01-job-history.md` | `employers[]` | +| 2. Accomplishments | `stages/02-accomplishments.md` | `evidence[]` | +| 3. Skills survey | `stages/03-skills.md` | `skills[]` | +| 4. Needs | `stages/04-needs.md` | `hard_blocks`, `named_exceptions`, `comp`, `culture`, `soft_flags`, `benefits_and_terms`, `company_criteria`, `person.location_label`, `person.working_style` | +| 5. Lanes | `stages/05-lanes.md` | `lanes[]` | +| 6. Review | `stages/06-review.md` | `meta` | + +**Starting or resuming.** If the user names a file that already exists, read it +and look at `meta.intake.stages_done`. Start at the lowest stage number that is +not in that list. Tell the user in one sentence which stage that is and why +("Stages 1 and 2 are saved, so we start at stage 3, the skills survey."). If no +file exists yet, start at stage 0. + +**Users without Claude Code** can run the same interview in any chat model with +the plain prompts in `stage-prompts/`, one file per stage. + +--- + +## Rules that apply to every stage + +These seven rules hold in every stage file. A stage file can add detail. It can +never relax a rule. + +### 1. One question at a time + +Ask one question, then stop and wait for the answer. A question with two asks +in it is two questions: ask the first, wait, then ask the second. This holds +for follow-ups too. Never put a numbered list of questions in one message. + +### 2. Do the reading first + +If the user offers a résumé, portfolio link, old job description or any other +document, read it **before** asking anything it could answer. Turn what it says +into candidate entries, then confirm them one at a time ("Your résumé says you +were Technical Writer at Northwind Example Co. from 2017-02 to 2021-12. Is that +right?") instead of asking the user to retype them. + +Candidate entries taken from a résumé get `source.type: document` and +`proof: unchecked`. A résumé is the user's own summary of their work, so reading +it proves only that the résumé says so. `proof: checked` is for a document, link +or artifact that is the work itself, or a record of it, and only after you have +read the passage it rests on. + +### 3. Never put words in the user's mouth + +- No suggested answers. Ask the question as written and wait. +- No drafted accomplishments. The claim is the user's sentence, trimmed only of + filler. Do not add adjectives, results, numbers or scope they did not say. +- No rounded-up numbers. "About 40" stays "about 40". "I don't know" stays + unknown. +- No upgraded authorship. If the user says they directed the work, it is + `DIRECTED`, never `WROTE`. If they reviewed it, it is `REVIEWED`. +- No inferred skills. A skill enters the file only when the user scores it. +- "I don't know" or "I'd rather not say" is stored as a gap (the field is left + out or `null`), never filled with a guess. Stage 6 lists every gap. + +A starter menu is allowed only where a stage file says so (the hard-block +question). A menu lists options. It never pre-selects one. + +### 4. Read back, then write + +After each answer, show the exact YAML entry you will write, including its +source label, and ask: **"Is this right?"** Only a clear yes writes it. Any +other answer means: fix the entry, show it again, ask again. + +Write as soon as the confirmed answers make a complete entry (the schema's +required fields are present). Each later answer about the same entry is read +back the same way and written with `--replace`, which swaps the stored entry +for the corrected one. + +### 5. One checked door + +Every write goes through `scripts/add_entry.py`. Never edit +`career-profile.yaml` by hand, with an editor tool, with `sed`, or by writing +the whole file. The one exception is stage 0, which copies the empty template +into place before the first answer exists. + +```bash +python3 scripts/add_entry.py PROFILE --section evidence --entry - <<'YAML' +id: ev-northwind-api-rebuild +employer_id: northwind +claim: Moved the API reference from hand-edited pages to pages generated from the API spec. +authorship: DIRECTED +proof: unchecked +source: {type: interview, ref: "interview:2026-10-01:s2.q1", captured_on: "2026-10-01"} +YAML +``` + +What the door does: it checks the entry against the schema for that section, +refuses an id that is already stored (or, with `--replace`, refuses an id that +is not stored), refuses contact details and a `checked` interview entry, writes +the file, then runs the full validator and prints the result. + +- **Exit 0** means written. Read the validator lines it printed. During the + interview some problems are expected (a skill pointing at evidence not yet + written). Note them; stage 6 clears them. +- **Exit 1** means refused, and the file was not touched. Tell the user in one + plain sentence what was refused and why, fix the entry, read it back again. + Never work around a refusal. + +**Sections that hold a mapping** (`person`, `comp`, `culture`, +`company_criteria`, `meta`) merge top-level keys. A key's value is replaced +whole, so to add one perk you send the full `perks` list: the stored items plus +the new one. Read the file before every mapping write so nothing stored is lost. + +**A model with no shell access** shows the YAML entry and the full command in +the chat, and the user runs it and pastes back the output. The rule does not +change: the file is written only by `add_entry.py`. + +### 6. Correct the record + +When an answer contradicts something already stored, say so in one sentence +("Stage 1 has you starting at Placeholder Labs in 2022-01, and you just said +2021. Which is right?"), wait, then fix the stored entry with `--replace`. +Never keep both versions, and never pick one yourself. + +### 7. Save after every stage + +`add_entry.py` saves on every write. At the end of each stage, record the stage +in `meta.intake.stages_done` so a later session resumes at the next one. The +`intake` key is replaced whole, so send the complete list and keep any stored +`closing_answer`: + +```bash +python3 scripts/add_entry.py PROFILE --section meta --entry - <<'YAML' +updated: "2026-10-01" +intake: + stages_done: [0, 1, 2] +YAML +``` + +Then tell the user what was saved in one or two sentences and name the next +stage. + +--- + +## Source labels + +Every evidence entry carries a `source` that says where the fact came from. +The source decides what `proof` is allowed. + +| Where the fact came from | `source.type` | `ref` format | Allowed `proof` | +|---|---|---|---| +| Said in the interview | `interview` | `interview::s.q` | `unchecked` only | +| A document the user supplied | `document` | `#
` | `checked` once you have read the passage | +| A public page | `link` | full `https://` URL | `checked` once read | +| A repo, file or published artifact | `artifact` | repo or path | `checked` once read | +| A former colleague who could vouch | `reference` | role only, never a name or contact | `unchecked` | + +- `` in an interview ref counts the questions asked so far in that stage, + loops included, so every ref points at one line of the transcript. +- A document you have not read yet is stored with the file name alone as `ref` + and `proof: unchecked`. Add `#
` and `checked` only after you read + the passage, then write the change with `--replace`. +- A link or artifact you cannot open (no network, a login wall, a dead page) + stays `unchecked`. Say so to the user. Do not mark it `checked` because the + user says it is fine. +- `captured_on` is the day the entry is written. +- `proof: do_not_use` marks something the file records but that must never be + cited, for example work the user was near but did not do. Pair it with + `authorship: OTHER-AUTHOR` when someone else did the work. + +--- + +## What you never do + +- Invent or upgrade a claim, a number, a date, an authorship mode or a skill. +- Ask two questions in one message. +- Write without a read-back and a yes. +- Write the file any way other than `scripts/add_entry.py`. +- Store an email address, phone number or a colleague's name. The file has no + contact fields, and the validator rejects contact-shaped text anywhere. +- Mark interview or reference facts `checked`. +- Keep two stored versions of the same fact. +- Score, rate or judge the user. Stage 6 lists gaps and asks; it does not grade. + +## Finishing + +Stage 6 ends the interview. The file is done when `scripts/validate_profile.py` +prints no problems (warnings are allowed and are listed for the user), every +gap has been asked about once, and the user's closing answer is stored in their +own words in `meta.intake.closing_answer`. diff --git a/pipelines/job-assessment/intake/stage-prompts/00-orientation.txt b/pipelines/job-assessment/intake/stage-prompts/00-orientation.txt new file mode 100644 index 0000000..5859e0b --- /dev/null +++ b/pipelines/job-assessment/intake/stage-prompts/00-orientation.txt @@ -0,0 +1,28 @@ +I'm building my career-profile.yaml for a job assessment tool, one stage at a time. This is stage 0: orientation. I'm attaching my current career-profile.yaml (or tell you I don't have one yet). Interview me for this stage only. + +RULES FOR THIS WHOLE CONVERSATION +1. Ask exactly one question at a time and wait for my answer. If a question has two parts, ask them separately. +2. If I give you a document, read it before asking anything it could answer. Turn what it says into candidate entries and confirm them with me one at a time. Facts from my résumé get source type document and proof: unchecked. +3. Never put words in my mouth. Do not suggest answers, draft accomplishments, round numbers up, upgrade how much credit I take, or add skills I did not score. If I say "I don't know", leave the field out. It is a gap, not something to fill. +4. After each answer, show the exact YAML entry you would add, including its source label, and ask "Is this right?" Only my yes counts. +5. Never write the file yourself. For every confirmed entry, give me the command to run from the pipelines/job-assessment folder, with the YAML on stdin: + python3 scripts/add_entry.py career-profile.yaml --section SECTION --entry - <<'YAML' + (the entry) + YAML + Add --replace when correcting an entry that is already stored. I will run it and paste the output back. If it says "refused", fix the entry and show it again. +6. If an answer contradicts something already stored, say so in one sentence, ask which is right, and correct the stored entry with --replace. Never keep both. +7. Source labels for evidence: interview answer = {type: interview, ref: "interview:YYYY-MM-DD:sSTAGE.qN"}, always proof: unchecked. A file I gave you = {type: document, ref: "file name#section"}, proof: checked only after you read the passage. A public page = {type: link, ref: "https://..."}, checked only after you read it. A repo or artifact = {type: artifact, ref: "repo or path"}, checked only after you read it. A colleague who could vouch = {type: reference, ref: "their role only, never a name"}, always unchecked. No email addresses or phone numbers anywhere. + +THIS STAGE +Ask these questions, in this order, one at a time: +1. Where should the file live? +2. Do you have a résumé, portfolio or old job descriptions I should read first? +3. What name should the file use for you? A first name or a nickname is fine. +4. Which job titles are you aiming for? +5. How many years have you worked in your field? + +If I have no file yet, tell me to copy intake/templates/career-profile.template.yaml to the place I named. That copy is the only write that does not go through add_entry.py. +If I give you documents, read all of them now and keep a list of candidate facts for stages 1 and 2. Do not write them yet. +Questions 3 to 5 fill the person section, which needs all three together: read back each answer, then give one add_entry.py command with --section person after the third is confirmed. Use my job titles exactly as I said them. If my years answer is a range, ask which whole number to use, and never round up. + +When the stage is done, give me one last command that records it in meta.intake.stages_done (send the full list of finished stages, and keep any stored closing_answer), then tell me in one or two sentences what was saved. diff --git a/pipelines/job-assessment/intake/stage-prompts/01-job-history.txt b/pipelines/job-assessment/intake/stage-prompts/01-job-history.txt new file mode 100644 index 0000000..a764157 --- /dev/null +++ b/pipelines/job-assessment/intake/stage-prompts/01-job-history.txt @@ -0,0 +1,29 @@ +I'm building my career-profile.yaml for a job assessment tool, one stage at a time. This is stage 1: job history. I'm attaching my current career-profile.yaml. Interview me for this stage only. + +RULES FOR THIS WHOLE CONVERSATION +1. Ask exactly one question at a time and wait for my answer. If a question has two parts, ask them separately. +2. If I give you a document, read it before asking anything it could answer. Turn what it says into candidate entries and confirm them with me one at a time. Facts from my résumé get source type document and proof: unchecked. +3. Never put words in my mouth. Do not suggest answers, draft accomplishments, round numbers up, upgrade how much credit I take, or add skills I did not score. If I say "I don't know", leave the field out. It is a gap, not something to fill. +4. After each answer, show the exact YAML entry you would add, including its source label, and ask "Is this right?" Only my yes counts. +5. Never write the file yourself. For every confirmed entry, give me the command to run from the pipelines/job-assessment folder, with the YAML on stdin: + python3 scripts/add_entry.py career-profile.yaml --section SECTION --entry - <<'YAML' + (the entry) + YAML + Add --replace when correcting an entry that is already stored. I will run it and paste the output back. If it says "refused", fix the entry and show it again. +6. If an answer contradicts something already stored, say so in one sentence, ask which is right, and correct the stored entry with --replace. Never keep both. +7. Source labels for evidence: interview answer = {type: interview, ref: "interview:YYYY-MM-DD:sSTAGE.qN"}, always proof: unchecked. A file I gave you = {type: document, ref: "file name#section"}, proof: checked only after you read the passage. A public page = {type: link, ref: "https://..."}, checked only after you read it. A repo or artifact = {type: artifact, ref: "repo or path"}, checked only after you read it. A colleague who could vouch = {type: reference, ref: "their role only, never a name"}, always unchecked. No email addresses or phone numbers anywhere. + +THIS STAGE +This stage writes employers[]. Start with my most recent job and work backwards. Ask these questions, in this order, one at a time: +1. What is the most recent place you worked? +2. What was your title there? +3. What month and year did you start? +4. What month and year did you leave, or are you still there? +5. In one or two sentences, what were you there to do? +6. Was there a job before that? (If yes, loop back to question 1 for that job.) + +Follow-up only when a date is vague: "Roughly which year?" If I do not know the month, store January of that year and say so in the read-back. +Each entry is {id, name, title, start, end, summary}. id is the place's name in lowercase with _ between words. Dates are "YYYY-MM" in quotes; end can be present. The summary is my sentence, trimmed of filler only. +Give the add_entry.py command (--section employers) once id, name, title and start are confirmed, then use --replace for my later answers about the same job. + +When the stage is done, give me one last command that records it in meta.intake.stages_done (send the full list of finished stages, and keep any stored closing_answer), then tell me in one or two sentences what was saved. diff --git a/pipelines/job-assessment/intake/stage-prompts/02-accomplishments.txt b/pipelines/job-assessment/intake/stage-prompts/02-accomplishments.txt new file mode 100644 index 0000000..863ba1c --- /dev/null +++ b/pipelines/job-assessment/intake/stage-prompts/02-accomplishments.txt @@ -0,0 +1,31 @@ +I'm building my career-profile.yaml for a job assessment tool, one stage at a time. This is stage 2: accomplishments. I'm attaching my current career-profile.yaml. Interview me for this stage only. + +RULES FOR THIS WHOLE CONVERSATION +1. Ask exactly one question at a time and wait for my answer. If a question has two parts, ask them separately. +2. If I give you a document, read it before asking anything it could answer. Turn what it says into candidate entries and confirm them with me one at a time. Facts from my résumé get source type document and proof: unchecked. +3. Never put words in my mouth. Do not suggest answers, draft accomplishments, round numbers up, upgrade how much credit I take, or add skills I did not score. If I say "I don't know", leave the field out. It is a gap, not something to fill. +4. After each answer, show the exact YAML entry you would add, including its source label, and ask "Is this right?" Only my yes counts. +5. Never write the file yourself. For every confirmed entry, give me the command to run from the pipelines/job-assessment folder, with the YAML on stdin: + python3 scripts/add_entry.py career-profile.yaml --section SECTION --entry - <<'YAML' + (the entry) + YAML + Add --replace when correcting an entry that is already stored. I will run it and paste the output back. If it says "refused", fix the entry and show it again. +6. If an answer contradicts something already stored, say so in one sentence, ask which is right, and correct the stored entry with --replace. Never keep both. +7. Source labels for evidence: interview answer = {type: interview, ref: "interview:YYYY-MM-DD:sSTAGE.qN"}, always proof: unchecked. A file I gave you = {type: document, ref: "file name#section"}, proof: checked only after you read the passage. A public page = {type: link, ref: "https://..."}, checked only after you read it. A repo or artifact = {type: artifact, ref: "repo or path"}, checked only after you read it. A colleague who could vouch = {type: reference, ref: "their role only, never a name"}, always unchecked. No email addresses or phone numbers anywhere. + +THIS STAGE +This stage writes evidence[]. Work one employer at a time from my file. For each accomplishment, ask these questions, in this order, one at a time: +1. At [employer], what is one thing you made or changed that you would want a hiring manager to know? +2. What changed because of it? +3. Did you write it yourself, direct a person or an AI tool to make it, co-write it, design it for someone else to build, or review someone else's work? +4. Is there anything that shows it happened: a document, link, repo or page? +5. Which skills did it use? +Aim for 2 to 5 accomplishments per employer, asking "Is there another one at [employer]?" between them. + +Follow-ups only when there is a gap: "Roughly how many or how big?" (accept no number), and "Can I read that link now?" +Each entry is {id, employer_id, claim, details, authorship, proof, source}. The claim is my sentence, trimmed of filler only. details holds my answers to questions 2 and 5 in my words. Do not add dates unless I gave them. +Authorship: WROTE (I did it), DIRECTED (I directed a person or an AI tool), CO-WROTE, DESIGNED (someone else built it), REVIEWED, or OTHER-AUTHOR with proof: do_not_use (I was there but someone else did it). If I name two modes, ask which one describes the work as a whole. Never pick the one that gives me more credit. +Source comes from question 4, using the source labels above. Nothing to show = interview source. Copy any link or path exactly as I gave it. If you cannot open a link, keep it unchecked. +Give the add_entry.py command (--section evidence) once questions 1, 3 and 4 are answered, then --replace for the rest. + +When the stage is done, give me one last command that records it in meta.intake.stages_done (send the full list of finished stages, and keep any stored closing_answer), then tell me in one or two sentences what was saved. diff --git a/pipelines/job-assessment/intake/stage-prompts/03-skills.txt b/pipelines/job-assessment/intake/stage-prompts/03-skills.txt new file mode 100644 index 0000000..51929e4 --- /dev/null +++ b/pipelines/job-assessment/intake/stage-prompts/03-skills.txt @@ -0,0 +1,31 @@ +I'm building my career-profile.yaml for a job assessment tool, one stage at a time. This is stage 3: skills survey. I'm attaching my current career-profile.yaml and intake/templates/skills-catalog.yaml. Interview me for this stage only. + +RULES FOR THIS WHOLE CONVERSATION +1. Ask exactly one question at a time and wait for my answer. If a question has two parts, ask them separately. +2. If I give you a document, read it before asking anything it could answer. Turn what it says into candidate entries and confirm them with me one at a time. Facts from my résumé get source type document and proof: unchecked. +3. Never put words in my mouth. Do not suggest answers, draft accomplishments, round numbers up, upgrade how much credit I take, or add skills I did not score. If I say "I don't know", leave the field out. It is a gap, not something to fill. +4. After each answer, show the exact YAML entry you would add, including its source label, and ask "Is this right?" Only my yes counts. +5. Never write the file yourself. For every confirmed entry, give me the command to run from the pipelines/job-assessment folder, with the YAML on stdin: + python3 scripts/add_entry.py career-profile.yaml --section SECTION --entry - <<'YAML' + (the entry) + YAML + Add --replace when correcting an entry that is already stored. I will run it and paste the output back. If it says "refused", fix the entry and show it again. +6. If an answer contradicts something already stored, say so in one sentence, ask which is right, and correct the stored entry with --replace. Never keep both. +7. Source labels for evidence: interview answer = {type: interview, ref: "interview:YYYY-MM-DD:sSTAGE.qN"}, always proof: unchecked. A file I gave you = {type: document, ref: "file name#section"}, proof: checked only after you read the passage. A public page = {type: link, ref: "https://..."}, checked only after you read it. A repo or artifact = {type: artifact, ref: "repo or path"}, checked only after you read it. A colleague who could vouch = {type: reference, ref: "their role only, never a name"}, always unchecked. No email addresses or phone numbers anywhere. + +THIS STAGE +This stage writes skills[]. A skill score is my own claim, never proof. +First ask whether I want to use the offline form (open intake/templates/skills-survey.html, save the JSON, then run python3 intake/scripts/merge_survey.py career-profile.yaml FILE.json --dry-run and then without --dry-run) or go through the catalog here. If I pick the form, skip to the evidence questions below once I paste the merge output. + +In the chat, go group by group, item by item. Ask one at a time: +1. From 0 to 5, where 0 is [the group's zero anchor] and 5 is [the group's five anchor], where are you on [item label]? +For items I score 2 or higher, then ask one at a time: +2. Do you want more of it in your next job, don't mind it, or would you rather avoid it? (next: more | neutral | avoid) +3. When did you last use it: within 2 years, 2 to 5 years ago, longer ago, or never? (last: 2y | 5y | 5plus | never) +4. Did you do it yourself, by directing AI, or by teaching others? (how: self | ai | team, more than one allowed) +If I say a group does not apply, skip it; a skipped item is left out, never stored as 0. +Each entry is {id, label, group, match, self_score, next, last, how}, with id, label, group and match taken from the catalog item. + +Then, for each skill I scored 3 or higher, ask: which of your accomplishments shows it? List my stored evidence claims so I can pick. If I say none, leave evidence_ids empty; that marks it unproven, which is fine. Never link evidence I did not name, and never link an entry marked do_not_use or OTHER-AUTHOR. + +When the stage is done, give me one last command that records it in meta.intake.stages_done (send the full list of finished stages, and keep any stored closing_answer), then tell me in one or two sentences what was saved. diff --git a/pipelines/job-assessment/intake/stage-prompts/04-needs.txt b/pipelines/job-assessment/intake/stage-prompts/04-needs.txt new file mode 100644 index 0000000..feb66a2 --- /dev/null +++ b/pipelines/job-assessment/intake/stage-prompts/04-needs.txt @@ -0,0 +1,38 @@ +I'm building my career-profile.yaml for a job assessment tool, one stage at a time. This is stage 4: needs. I'm attaching my current career-profile.yaml. Interview me for this stage only. + +RULES FOR THIS WHOLE CONVERSATION +1. Ask exactly one question at a time and wait for my answer. If a question has two parts, ask them separately. +2. If I give you a document, read it before asking anything it could answer. Turn what it says into candidate entries and confirm them with me one at a time. Facts from my résumé get source type document and proof: unchecked. +3. Never put words in my mouth. Do not suggest answers, draft accomplishments, round numbers up, upgrade how much credit I take, or add skills I did not score. If I say "I don't know", leave the field out. It is a gap, not something to fill. +4. After each answer, show the exact YAML entry you would add, including its source label, and ask "Is this right?" Only my yes counts. +5. Never write the file yourself. For every confirmed entry, give me the command to run from the pipelines/job-assessment folder, with the YAML on stdin: + python3 scripts/add_entry.py career-profile.yaml --section SECTION --entry - <<'YAML' + (the entry) + YAML + Add --replace when correcting an entry that is already stored. I will run it and paste the output back. If it says "refused", fix the entry and show it again. +6. If an answer contradicts something already stored, say so in one sentence, ask which is right, and correct the stored entry with --replace. Never keep both. +7. Source labels for evidence: interview answer = {type: interview, ref: "interview:YYYY-MM-DD:sSTAGE.qN"}, always proof: unchecked. A file I gave you = {type: document, ref: "file name#section"}, proof: checked only after you read the passage. A public page = {type: link, ref: "https://..."}, checked only after you read it. A repo or artifact = {type: artifact, ref: "repo or path"}, checked only after you read it. A colleague who could vouch = {type: reference, ref: "their role only, never a name"}, always unchecked. No email addresses or phone numbers anywhere. + +THIS STAGE +This stage writes hard_blocks, named_exceptions, comp, culture, soft_flags, benefits_and_terms, company_criteria, person.location_label and person.working_style. Ask these questions, in this order, one at a time: +1. Which kinds of company would you never work for? (Offer this starter menu as options, never as a default: gambling or betting, weapons, tobacco, payday lending, adult content, jobs that are not remote, jobs with heavy travel.) +2. Is there any one-off exception: one company you would work for even though it trips one of those? +3. What is the lowest base pay you would take if everything else were perfect? (comp.floor) +4. What base pay would you normally accept? (comp.min) +5. What would you ask for? (comp.open_ask) +6. What would feel like a great offer? (comp.target) +7. What is the most you could see a role like this paying? (comp.stretch_ceiling) +8. Where are you, for picking a pay tier? A region label is enough. (person.location_label) +9. Which perks would really change your mind about a job? (culture.perks, kind: big or nice) +10. How much yearly paid time off is too little? (culture.low_time_off_days) +11. Which phrases in a job ad make you wary? (culture.hustle_phrases; phrases I say do not bother me go in culture.free_phrases) +12. What work would you rather avoid, even if you are good at it? (set next: avoid on the matching skill with --replace; if no skill matches, ask the 0 to 5 score question for it first) +13. Do you prefer gathering knowledge from experts, or becoming the expert yourself? (person.working_style: gather_from_experts | become_expert) +14. Which companies do you actually want to work for, and why? (company_criteria.high_interest or good_not_dream; never part of a score) + +Follow-ups only when there is a gap: "Why does this matter to you?" for a must-have or hard block with no reason; "Is that a big one or a nice one?" for a perk with no weight; "Which currency, and is that per year?" for an unclear pay answer. +Store numbers exactly as I gave them. Pay must run floor <= min <= open_ask <= target <= stretch_ceiling. Add match_hints to a hard block only from words I said. +comp, culture, person and company_criteria are mappings: a key's value is replaced whole, so when adding a perk, send the full perks list. +A worry that is a condition, not a phrase, is a soft_flags entry {id, label, why}. A benefit or term I want to see is a benefits_and_terms entry {id, label, why}. Tell me which list each answer lands in. + +When the stage is done, give me one last command that records it in meta.intake.stages_done (send the full list of finished stages, and keep any stored closing_answer), then tell me in one or two sentences what was saved. diff --git a/pipelines/job-assessment/intake/stage-prompts/05-lanes.txt b/pipelines/job-assessment/intake/stage-prompts/05-lanes.txt new file mode 100644 index 0000000..a229ee0 --- /dev/null +++ b/pipelines/job-assessment/intake/stage-prompts/05-lanes.txt @@ -0,0 +1,32 @@ +I'm building my career-profile.yaml for a job assessment tool, one stage at a time. This is stage 5: lanes. I'm attaching my current career-profile.yaml. Interview me for this stage only. + +RULES FOR THIS WHOLE CONVERSATION +1. Ask exactly one question at a time and wait for my answer. If a question has two parts, ask them separately. +2. If I give you a document, read it before asking anything it could answer. Turn what it says into candidate entries and confirm them with me one at a time. Facts from my résumé get source type document and proof: unchecked. +3. Never put words in my mouth. Do not suggest answers, draft accomplishments, round numbers up, upgrade how much credit I take, or add skills I did not score. If I say "I don't know", leave the field out. It is a gap, not something to fill. +4. After each answer, show the exact YAML entry you would add, including its source label, and ask "Is this right?" Only my yes counts. +5. Never write the file yourself. For every confirmed entry, give me the command to run from the pipelines/job-assessment folder, with the YAML on stdin: + python3 scripts/add_entry.py career-profile.yaml --section SECTION --entry - <<'YAML' + (the entry) + YAML + Add --replace when correcting an entry that is already stored. I will run it and paste the output back. If it says "refused", fix the entry and show it again. +6. If an answer contradicts something already stored, say so in one sentence, ask which is right, and correct the stored entry with --replace. Never keep both. +7. Source labels for evidence: interview answer = {type: interview, ref: "interview:YYYY-MM-DD:sSTAGE.qN"}, always proof: unchecked. A file I gave you = {type: document, ref: "file name#section"}, proof: checked only after you read the passage. A public page = {type: link, ref: "https://..."}, checked only after you read it. A repo or artifact = {type: artifact, ref: "repo or path"}, checked only after you read it. A colleague who could vouch = {type: reference, ref: "their role only, never a name"}, always unchecked. No email addresses or phone numbers anywhere. + +THIS STAGE +This stage writes lanes[]. A lane is one kind of role with its own must-haves. Every posting is read against one lane. Ask these questions, one at a time: +1. Are you looking at more than one kind of role? +Then for each kind of role, one at a time: +2. What short name should this kind of role have? (lowercase, a word or two) +3. Which emoji should mark it? +4. In one sentence, what is the work? +5. Which must-haves are different for this kind of role? +6. For each must-have: how much does it matter? (counts hard = severity: strong, softer = severity: soft, a bonus that never counts against a posting = severity: soft with bonus: true) +7. What skills does this kind of role want that you don't have yet? (known_gaps, with skill_id only when a stored skill matches) +8. What language in a posting makes you trust it? (keyword_signals.positive) +9. What language in a posting makes you distrust it? (keyword_signals.negative) +No follow-ups in this stage. Never raise a severity I gave, and never move a phrase to strong_positive or strong_negative unless I say it counts extra. +Give the add_entry.py command (--section lanes) once name, emoji and description are confirmed, with requirements: [], then --replace with the whole lane for each later answer. +At the end, if two lanes have the same must-haves, say so and ask whether that is right. + +When the stage is done, give me one last command that records it in meta.intake.stages_done (send the full list of finished stages, and keep any stored closing_answer), then tell me in one or two sentences what was saved. diff --git a/pipelines/job-assessment/intake/stage-prompts/06-review.txt b/pipelines/job-assessment/intake/stage-prompts/06-review.txt new file mode 100644 index 0000000..6904456 --- /dev/null +++ b/pipelines/job-assessment/intake/stage-prompts/06-review.txt @@ -0,0 +1,22 @@ +I'm building my career-profile.yaml for a job assessment tool, one stage at a time. This is stage 6: review. I'm attaching my current career-profile.yaml and the output of python3 scripts/validate_profile.py career-profile.yaml. Interview me for this stage only. + +RULES FOR THIS WHOLE CONVERSATION +1. Ask exactly one question at a time and wait for my answer. If a question has two parts, ask them separately. +2. If I give you a document, read it before asking anything it could answer. Turn what it says into candidate entries and confirm them with me one at a time. Facts from my résumé get source type document and proof: unchecked. +3. Never put words in my mouth. Do not suggest answers, draft accomplishments, round numbers up, upgrade how much credit I take, or add skills I did not score. If I say "I don't know", leave the field out. It is a gap, not something to fill. +4. After each answer, show the exact YAML entry you would add, including its source label, and ask "Is this right?" Only my yes counts. +5. Never write the file yourself. For every confirmed entry, give me the command to run from the pipelines/job-assessment folder, with the YAML on stdin: + python3 scripts/add_entry.py career-profile.yaml --section SECTION --entry - <<'YAML' + (the entry) + YAML + Add --replace when correcting an entry that is already stored. I will run it and paste the output back. If it says "refused", fix the entry and show it again. +6. If an answer contradicts something already stored, say so in one sentence, ask which is right, and correct the stored entry with --replace. Never keep both. +7. Source labels for evidence: interview answer = {type: interview, ref: "interview:YYYY-MM-DD:sSTAGE.qN"}, always proof: unchecked. A file I gave you = {type: document, ref: "file name#section"}, proof: checked only after you read the passage. A public page = {type: link, ref: "https://..."}, checked only after you read it. A repo or artifact = {type: artifact, ref: "repo or path"}, checked only after you read it. A colleague who could vouch = {type: reference, ref: "their role only, never a name"}, always unchecked. No email addresses or phone numbers anywhere. + +THIS STAGE +This stage writes meta, plus fixes to any section through add_entry.py --replace. +1. Go through every problem line in the validator output (lines without "warning:"). For each, explain it in one plain sentence, ask the one question that fixes it, and give the fix command. +2. Find the weak spots the validator cannot judge: skills scored 3 or higher with no evidence_ids, unchecked evidence that a score of 4 or 5 rests on, evidence with no dates, must-haves and hard blocks with no why, and lanes whose requirements look copied. +3. List every claim under one of five labels: confirmed (backed by checked evidence), no record (no evidence, or only an interview answer), stale (last used more than five years ago), overclaimed (a 4 or 5 resting only on unchecked or reviewed work), underclaimed (checked evidence shows a skill I scored 0 to 2 or did not score). These labels describe the file, not me. +4. Ask one question per gap, one at a time. What I say decides it. If I say "leave it", leave it. Never change a score, authorship or proof value without my answer, and never argue for a higher score. +5. Then ask exactly: "Does this file feel like a fair picture of you?" Store my answer, unedited, in meta.intake.closing_answer, together with stages_done: [0, 1, 2, 3, 4, 5, 6]. diff --git a/pipelines/job-assessment/intake/stages/00-orientation.md b/pipelines/job-assessment/intake/stages/00-orientation.md new file mode 100644 index 0000000..38cab63 --- /dev/null +++ b/pipelines/job-assessment/intake/stages/00-orientation.md @@ -0,0 +1,69 @@ +# Stage 0: Orientation + +**Writes:** a new `career-profile.yaml` copied from the template, then `person`. +**Rules:** every rule in `../SKILL.md` applies. One question at a time. + +## Questions, in order + +Ask one, wait for the answer, then ask the next. + +1. "Where should the file live?" +2. "Do you have a résumé, portfolio or old job descriptions I should read first?" +3. "What name should the file use for you? A first name or a nickname is fine." +4. "Which job titles are you aiming for?" +5. "How many years have you worked in your field?" + +Questions 1 and 2 are the stage 0 questions from the design. Questions 3 to 5 +exist because the file's `person` section requires a name, at least one target +role and a years figure, and later stages cannot write to `person` until those +three are stored. + +## What to do with each answer + +**Question 1.** Copy the empty template to the place the user named. This is +the only write in the whole interview that does not go through `add_entry.py`, +because there is no file yet for it to check: + +```bash +cp intake/templates/career-profile.template.yaml PATH/career-profile.yaml +``` + +If a file already exists there, do not overwrite it. Read it, check +`meta.intake.stages_done`, and resume at the right stage (see `../SKILL.md`). + +**Question 2.** If the user offers documents, read every one of them now, before +question 3. Make a short list of candidate facts the documents state (names, +titles, dates, accomplishments). Keep the list in the conversation; nothing is +written yet. In stages 1 and 2, confirm each candidate one at a time instead of +asking the user to retype it. Every candidate from a résumé is stored as +`source.type: document`, `proof: unchecked` (see "Do the reading first"). + +If the user says no, move on. Do not ask again. + +**Questions 3 to 5.** Read back each answer as you get it. Because the schema +needs all three fields together, write `person` once, after the third is +confirmed: + +```yaml +display_name: Robin Sample +target_roles: [Senior Technical Writer, Docs Platform Engineer] +years_experience: 9 +``` + +```bash +python3 scripts/add_entry.py PATH/career-profile.yaml --section person --entry - <<'YAML' +display_name: Robin Sample +target_roles: [Senior Technical Writer, Docs Platform Engineer] +years_experience: 9 +YAML +``` + +- Use the titles exactly as the user said them. +- If the years answer is a range ("eight or nine"), ask once: "Which whole + number should the file use?" If the user cannot pick, use the lower number + and say so in the read-back. Never round up. + +## End of stage + +Record stage 0 in `meta.intake.stages_done`, tell the user the file exists and +where, and move to stage 1. diff --git a/pipelines/job-assessment/intake/stages/01-job-history.md b/pipelines/job-assessment/intake/stages/01-job-history.md new file mode 100644 index 0000000..81329fa --- /dev/null +++ b/pipelines/job-assessment/intake/stages/01-job-history.md @@ -0,0 +1,76 @@ +# Stage 1: Job history + +**Writes:** `employers[]` +**Rules:** every rule in `../SKILL.md` applies. One question at a time. + +## Questions, in order + +Ask one, wait for the answer, then ask the next. Start with the most recent +job and work backwards. + +1. "What is the most recent place you worked?" +2. "What was your title there?" +3. "What month and year did you start?" +4. "What month and year did you leave, or are you still there?" +5. "In one or two sentences, what were you there to do?" +6. "Was there a job before that?" + +If the answer to question 6 is yes, loop back to question 1 for that job +(ask "What was the place you worked before that?"). Stop when the answer is no. + +If stage 0 read a résumé, confirm each employer from it instead of asking +questions 1 to 4 cold: "Your résumé lists Technical Writer at Northwind +Example Co., 2017-02 to 2021-12. Is that right?" Ask only what the résumé +does not say. + +## Follow-ups, only when an answer leaves a gap + +- A vague date ("a while ago", "after the pandemic"): "Roughly which year?" +- A year with no month: "Do you remember the month?" If not, store January of + that year and write "(start month not known, stored as January)" into the + read-back so the user sees it. Do the same for an end date. + +No other follow-ups in this stage. + +## Turning answers into an entry + +| Field | From | Notes | +|---|---|---| +| `id` | the place's name | lowercase, words joined with `_` (`northwind`, `placeholder_labs`). Show it in the read-back. | +| `name` | question 1 | the name only. Drop descriptions like "a small software company". | +| `title` | question 2 | exactly as said | +| `start` | question 3 | `YYYY-MM`, quoted | +| `end` | question 4 | `YYYY-MM`, quoted, or `present` | +| `summary` | question 5 | the user's sentence, trimmed of filler only | + +Write as soon as `id`, `name`, `title` and `start` are confirmed (after +question 3). Questions 4 and 5 then update the stored entry with `--replace`, +each after its own read-back: + +```bash +python3 scripts/add_entry.py PROFILE --section employers --entry - <<'YAML' +id: northwind +name: Northwind Example Co. +title: Technical Writer +start: "2017-02" +YAML +``` + +```bash +python3 scripts/add_entry.py PROFILE --section employers --entry - --replace <<'YAML' +id: northwind +name: Northwind Example Co. +title: Technical Writer +start: "2017-02" +end: "2021-12" +YAML +``` + +A personal project, freelance work or a career break is not an employer. +Accomplishments from them are stored in stage 2 with `employer_id: null`. + +## End of stage + +Read back the list of employers (name, title, dates) in one short block and ask +whether anything is missing. Then record stage 1 in `meta.intake.stages_done` +and move to stage 2. diff --git a/pipelines/job-assessment/intake/stages/02-accomplishments.md b/pipelines/job-assessment/intake/stages/02-accomplishments.md new file mode 100644 index 0000000..76549e6 --- /dev/null +++ b/pipelines/job-assessment/intake/stages/02-accomplishments.md @@ -0,0 +1,108 @@ +# Stage 2: Accomplishments + +**Writes:** `evidence[]` +**Rules:** every rule in `../SKILL.md` applies. One question at a time. + +This is the stage that matters most. The assessment can only claim a +qualification when an evidence entry backs it, so every entry here must be the +user's own account, with an honest label for where it came from. + +## Questions, in order + +Work one employer at a time, starting with the one the user names first. For +each accomplishment, ask one question, wait, then ask the next. + +1. "At [employer], what is one thing you made or changed that you would want a + hiring manager to know?" +2. "What changed because of it?" +3. "Did you write it yourself, direct a person or an AI tool to make it, + co-write it, design it for someone else to build, or review someone else's + work?" +4. "Is there anything that shows it happened: a document, link, repo or page?" +5. "Which skills did it use?" + +Loop: aim for 2 to 5 accomplishments per employer. After each one, ask "Is +there another one at [employer]?" When the user says no (or after five), move +to the next employer. After the last employer, ask once: "Is there anything +from outside a job, like a personal project, that you want in the file?" + +If stage 0 read a résumé, confirm its accomplishments one at a time instead +of asking question 1 cold: "Your résumé says you wrote a style guide at +Northwind Example Co. Do you want that in the file?" Then ask questions 2 to 5 +for it. + +## Follow-ups, only when an answer leaves a gap + +- A result with no size, when a size would matter: "Roughly how many or how + big?" Accept "I don't know" or no number. Never suggest one. +- A link, page or repo in the answer to question 4: "Can I read that link now?" + If yes, read it. If you cannot open it, say so and keep the entry unchecked. + +## Turning answers into an entry + +| Field | From | Notes | +|---|---|---| +| `id` | the claim | `ev--`, for example `ev-northwind-style-guide` | +| `employer_id` | the employer being asked about | `null` for a personal project | +| `claim` | question 1 | the user's sentence, trimmed of filler only. No added results, adjectives or scope. | +| `details` | questions 2 and 5, and any extra detail from question 3 | the user's words. Put the question 5 answer last, as "Skills used (own words): ...", so stage 3 can read it back. | +| `dates` | only if the user gave them | stage 2 does not ask for dates. Leave the field out; stage 6 lists it as a gap. | +| `authorship` | question 3 | see the table below | +| `proof` and `source` | question 4 | see the table below | + +**Authorship, from the user's answer to question 3:** + +| The user said | `authorship` | +|---|---| +| wrote it, built it, did it myself | `WROTE` | +| directed a person or an AI tool, had it made, wrote the prompts and checked the result | `DIRECTED` | +| wrote it with someone, co-wrote, paired | `CO-WROTE` | +| designed it, someone else built it | `DESIGNED` | +| reviewed, edited or approved someone else's work | `REVIEWED` | +| was on the team, but someone else did it | `OTHER-AUTHOR`, with `proof: do_not_use` | + +If the answer names two modes for the work as a whole ("I directed it and +wrote some of it"), ask: "Which one describes the work as a whole?" Store that +one, and put the user's full answer in `details`. If the user gives a plain +answer like "I directed the work. I wrote the build step myself", the first +sentence is about the work as a whole: store `DIRECTED` and keep the second +sentence in `details`. Never pick the mode that gives more credit. + +**Source, from the user's answer to question 4:** + +| The user said | `source` | `proof` | +|---|---|---| +| nothing to show | `{type: interview, ref: "interview::s2.q"}`, where `` is the number of the question 1 that opened this accomplishment | `unchecked` | +| a file they have (PDF, doc) | `{type: document, ref: ""}` | `unchecked` until you read it, then `ref: "#
"` and `checked` | +| a public page | `{type: link, ref: ""}` | `unchecked` until you read it, then `checked` | +| a repo, file path or published artifact | `{type: artifact, ref: ""}` | `unchecked` until you read it, then `checked` | +| a colleague who could vouch | `{type: reference, ref: ""}` | `unchecked`. Never store the colleague's name or contact. | + +Copy a URL or path exactly as the user gave it. Do not add `https://` or fix +it. If it looks wrong, ask. + +Every evidence entry has a source. No exceptions. Add `captured_on` with +today's date. + +**When to write.** The entry is complete once questions 1, 3 and 4 are +answered. Write it then. Question 5 (and any follow-up) updates it with +`--replace`. + +```bash +python3 scripts/add_entry.py PROFILE --section evidence --entry - <<'YAML' +id: ev-northwind-style-guide +employer_id: northwind +claim: Wrote a 20-page style guide that the whole support team used. +details: "Support answers got more consistent." +authorship: WROTE +proof: unchecked +source: {type: document, ref: "northwind-style-guide.pdf", captured_on: "2026-10-01"} +YAML +``` + +## End of stage + +Read back the list of claims, one line each with its authorship and source +type. Ask whether any of them overstates what happened. If the user corrects +one, fix it with `--replace`. Then record stage 2 in `meta.intake.stages_done` +and move to stage 3. diff --git a/pipelines/job-assessment/intake/stages/03-skills.md b/pipelines/job-assessment/intake/stages/03-skills.md new file mode 100644 index 0000000..16d4573 --- /dev/null +++ b/pipelines/job-assessment/intake/stages/03-skills.md @@ -0,0 +1,109 @@ +# Stage 3: Skills survey + +**Writes:** `skills[]` +**Rules:** every rule in `../SKILL.md` applies. One question at a time. + +A skill score is the user's own claim. It is never proof. The assessment +counts a skill as proven only when it points at an evidence entry, and reports +the rest as "unproven". That is not a failure; it is the file being honest. + +The list of skills comes from `intake/templates/skills-catalog.yaml`: about 60 +items in ten groups. Each group has a 0 anchor and a 5 anchor in plain words. + +## Choose a way to do it + +Ask: "There are about 60 skills to score. Do you want to fill in a form on your +own (option A), or go through them here with me, one at a time (option B)?" + +### Option A: the offline form + +1. The user opens `intake/templates/skills-survey.html` in a browser, straight + from disk. No server, nothing is sent anywhere. +2. They score the items, then click Download, which saves a JSON file. +3. They run, or you run with their go-ahead: + + ```bash + python3 intake/scripts/merge_survey.py PROFILE path/to/skills-survey-DATE.json --dry-run + python3 intake/scripts/merge_survey.py PROFILE path/to/skills-survey-DATE.json + python3 scripts/validate_profile.py PROFILE + ``` + + `merge_survey.py` is a second checked door, used only for the survey file: + it refuses the whole merge if any answer is invalid, writes only the + `skills:` block, and never touches evidence links. You do not edit its + output. Show the user the dry-run report before the real merge, and treat + it as the read-back. +4. Then go to "Linking skills to evidence" below. + +### Option B: in the chat + +Go through the catalog group by group, item by item. For each item ask, one at +a time: + +1. "From 0 to 5, where 0 is [group's 0 anchor] and 5 is [group's 5 anchor], + where are you on [item label]?" + +For items scored 2 or higher, then ask, one at a time: + +2. "Do you want more of it in your next job, don't mind it, or would you rather + avoid it?" +3. "When did you last use it: within the last 2 years, 2 to 5 years ago, longer + ago, or never?" +4. "Did you do it yourself, by directing AI, or by teaching others?" (more than + one can be true) + +If the user says a whole group does not apply ("skip the design group"), write +nothing for it. A skipped item is left out of the file, never stored as 0. + +If the user names a skill that is not in the catalog, it can be added. Use the +user's words as the `label`, make an id from it, ask question 1 for it, and +show any `match` pattern you propose in the read-back so the user can check it. + +**Turning answers into an entry** + +| Field | From | +|---|---| +| `id`, `label`, `group`, `match` | the catalog item (or the user's words for an added skill) | +| `self_score` | question 1, a whole number 0 to 5 | +| `next` | question 2: `more`, `neutral` (don't mind) or `avoid` | +| `last` | question 3: `2y`, `5y`, `5plus` or `never` | +| `how` | question 4: any of `self`, `ai`, `team` | +| `evidence_ids` | "Linking skills to evidence" below | + +Write each item once its questions are answered: + +```bash +python3 scripts/add_entry.py PROFILE --section skills --entry - <<'YAML' +id: api-specs +label: API specifications (OpenAPI) +group: web +self_score: 4 +next: more +last: 2y +how: [self, ai] +YAML +``` + +A score of 4 or 5 with `last: never` is refused by the door. If that happens, +read the contradiction back to the user and ask which part is right. + +## Linking skills to evidence + +For each skill scored 3 or higher, ask, one at a time: + +- "Which of your accomplishments shows [skill]?" + +Read back the user's own list of accomplishments (the `claim` lines from stage +2, and their "Skills used" notes) so they can pick. Do not pick for them. + +- If they name one or more, write the ids with `--replace` on that skill. +- If they say "none of them", leave `evidence_ids` empty. The skill is marked + unproven, never failed, and stage 6 will ask about it once. + +Never link evidence the user did not name, and never link an entry marked +`proof: do_not_use` or `authorship: OTHER-AUTHOR`. + +## End of stage + +Tell the user how many skills were scored, and how many at 3 or higher have no +evidence yet. Record stage 3 in `meta.intake.stages_done` and move to stage 4. diff --git a/pipelines/job-assessment/intake/stages/04-needs.md b/pipelines/job-assessment/intake/stages/04-needs.md new file mode 100644 index 0000000..0a915f5 --- /dev/null +++ b/pipelines/job-assessment/intake/stages/04-needs.md @@ -0,0 +1,128 @@ +# Stage 4: Needs + +**Writes:** `hard_blocks`, `named_exceptions`, `comp`, `culture`, `soft_flags`, +`benefits_and_terms`, `company_criteria`, `person.location_label`, +`person.working_style` +**Rules:** every rule in `../SKILL.md` applies. One question at a time. + +This stage records what the user needs from a job. These answers decide hard +blocks, the Comp score and the Culture score, so the read-back must say which +list each answer lands in and what that list does. + +## Questions, in order + +Ask one, wait for the answer, then ask the next. + +1. "Which kinds of company would you never work for?" + This is the one question with a starter menu. Offer it as options, never as + a default: gambling or betting, weapons, tobacco, payday lending, adult + content, jobs that are not remote, jobs with heavy travel. The user can pick + none, some, or name their own. +2. "Is there any one-off exception: one company you would work for even though + it trips one of those?" +3. "What is the lowest base pay you would take if everything else were + perfect?" +4. "What base pay would you normally accept?" +5. "What would you ask for?" +6. "What would feel like a great offer?" +7. "What is the most you could see a role like this paying?" +8. "Where are you, for picking a pay tier when a posting lists pay by + location?" (a region label is enough; no address) +9. "Which perks would really change your mind about a job?" +10. "How much yearly paid time off is too little?" +11. "Which phrases in a job ad make you wary?" +12. "What work would you rather avoid, even if you are good at it?" +13. "Do you prefer gathering knowledge from experts, or becoming the expert + yourself?" +14. "Which companies do you actually want to work for, and why?" + +## Follow-ups, only when an answer leaves a gap + +- A must-have or a hard block with no reason: "Why does this matter to you?" + Store the answer as `why`, in the user's words. If the user would rather not + say, leave `why` out. +- A perk with no weight given: "Is [perk] a big one, or a nice one?" (big adds + 2 to Culture, nice adds 1). +- A pay answer in another currency, or per hour: "Which currency, and is that + per year?" Store yearly numbers in one currency. Do not convert currencies + yourself. + +No other follow-ups in this stage. + +## Turning answers into entries + +**Question 1, `hard_blocks`.** One entry per kind of company: +`{id, label, why}`. The label is the user's words. Add `match_hints` only from +words the user said; never add your own list of related terms. + +**Question 2, `named_exceptions`.** `{company, block_id, why, added}`, where +`block_id` is the id of the hard block it overrides and `added` is today's +date. If the user names a company but not which block it overrides, ask. + +**Questions 3 to 7, `comp`.** One number per answer: +`floor` (3), `min` (4), `open_ask` (5), `target` (6), `stretch_ceiling` (7), +plus `currency` (a three-letter code such as `USD`). Store the number as given, +never rounded. "I don't know" leaves that key `null`. The door refuses numbers +out of order (floor ≤ min ≤ open_ask ≤ target ≤ stretch_ceiling); if it +does, read the two numbers back and ask which one is right. + +**Question 8, `person.location_label`.** The user's words, as short as they +gave them. + +**Question 9, `culture.perks`.** `{id, label, kind}` per perk, `kind: big` or +`kind: nice`. The `culture` section is a mapping, so send the whole `perks` +list each time. + +**Question 10, `culture.low_time_off_days`.** A whole number of days. "Fewer +than 15 days" is stored as `15`. + +**Question 11.** Phrases that make the user wary go in `culture.hustle_phrases` +(each found in a posting costs 1 Culture point, at most 3). Phrases the user +names but says do not bother them go in `culture.free_phrases` (they cost +nothing). A worry that is a condition rather than a phrase, such as "lots of +meetings", is a `soft_flags` entry `{id, label, why}` (costs 1 point). A +benefit or term the user wants to see, such as "pay listed in the ad", is a +`benefits_and_terms` entry `{id, label, why}`. Say which list each lands in +during the read-back. + +**Question 12, avoided work.** This updates `skills[]`, not a stage 4 section. +For each kind of work named: +- If a stored skill matches it, read back that skill with `next: avoid` and, + on a yes, write it with `--replace`. If the stored skill already says + `avoid`, say so and write nothing. +- If no stored skill matches, ask the stage 3 score question for it ("From 0 + to 5, ..."), then write a new skill with `next: avoid`. + +Avoiding work you are good at is allowed. Never argue the user out of it. + +**Question 13, `person.working_style`.** `gather_from_experts` or +`become_expert`. + +**Question 14, `company_criteria`.** Ask, for each company named, whether it +is a high-interest company or a good one that is not a dream, unless the user +already said. `high_interest` or `good_not_dream`, each `{name, why}`. This +list is shown in an assessment, and never changes a score or a verdict. Say so +in the read-back. + +## Example writes + +```bash +python3 scripts/add_entry.py PROFILE --section hard_blocks --entry - <<'YAML' +id: gambling +label: Gambling and betting +YAML +``` + +```bash +python3 scripts/add_entry.py PROFILE --section comp --entry - <<'YAML' +currency: USD +floor: 90000 +YAML +``` + +## End of stage + +Read back the whole needs picture in one short block: hard blocks, the five pay +numbers in order, perks by weight, time off, wary phrases, avoided work, +working style, companies. Record stage 4 in `meta.intake.stages_done` and move +to stage 5. diff --git a/pipelines/job-assessment/intake/stages/05-lanes.md b/pipelines/job-assessment/intake/stages/05-lanes.md new file mode 100644 index 0000000..f8dd536 --- /dev/null +++ b/pipelines/job-assessment/intake/stages/05-lanes.md @@ -0,0 +1,76 @@ +# Stage 5: Lanes + +**Writes:** `lanes[]` +**Rules:** every rule in `../SKILL.md` applies. One question at a time. + +A lane is one kind of role, with its own must-haves. Every posting is read +against exactly one lane, and lanes are never mixed. A user looking at one kind +of role still has one lane. + +## Questions, in order + +Ask one, wait for the answer, then ask the next. + +1. "Are you looking at more than one kind of role?" + +Then for each kind of role, one at a time: + +2. "What short name should this kind of role have?" (lowercase, a word or two, + for example `tech-writing`) +3. "Which emoji should mark it?" +4. "In one sentence, what is the work?" +5. "Which must-haves are different for this kind of role?" +6. For each must-have named: "How much does [must-have] matter: is it a + must-have that counts hard, a softer one, or a bonus that never counts + against a posting?" +7. "What skills does this kind of role want that you don't have yet?" +8. "What language in a posting makes you trust it?" +9. "What language in a posting makes you distrust it?" + +The design lists questions 8 and 9 as one question ("trust it, or distrust +it"). It has two asks, so it is asked as two. + +## Follow-ups + +None in this stage. A must-have with no reason is left without `why`; stage 6 +lists it. + +## Turning answers into an entry + +| Field | From | Notes | +|---|---|---| +| `name` | question 2 | lowercase, `-` or `_` between words | +| `emoji` | question 3 | as given | +| `description` | question 4 | the user's sentence | +| `requirements` | questions 5 and 6 | `{id, label, severity}`. Counts hard → `severity: strong`. Softer → `severity: soft`. Bonus → `severity: soft, bonus: true`. Never raise a severity the user gave. | +| `known_gaps` | question 7 | `{id, label, skill_id}`. Set `skill_id` only when a stored skill matches; otherwise leave it out. | +| `keyword_signals.positive` | question 8 | `{phrase}` per phrase, the user's exact words | +| `keyword_signals.negative` | question 9 | `{phrase}` per phrase, the user's exact words | + +Store trust and distrust phrases as `positive` and `negative`. Use +`strong_positive` or `strong_negative` only when the user says a phrase +counts extra. Never promote a phrase on your own. + +**When to write.** A lane is complete once `name`, `emoji` and `description` +are confirmed. Write it then with `requirements: []`. Each later answer is +read back and written with `--replace`, sending the whole lane. + +```bash +python3 scripts/add_entry.py PROFILE --section lanes --entry - --replace <<'YAML' +name: tech-writing +emoji: "✍️" +description: Roles writing guides, references and help content for a product. +requirements: + - {id: expert_access, label: Regular access to experts, severity: strong} +YAML +``` + +The `autonomy` signals of a lane are not asked in this stage. They can be +added later in the same way if the user wants them. + +## End of stage + +Read back each lane in one short block. If two lanes have the same must-haves, +say so in one sentence and ask whether that is right; copied requirements are +the most common lane mistake. Record stage 5 in `meta.intake.stages_done` and +move to stage 6. diff --git a/pipelines/job-assessment/intake/stages/06-review.md b/pipelines/job-assessment/intake/stages/06-review.md new file mode 100644 index 0000000..2b50361 --- /dev/null +++ b/pipelines/job-assessment/intake/stages/06-review.md @@ -0,0 +1,94 @@ +# Stage 6: Review + +**Writes:** `meta` (and fixes to any section, through `add_entry.py --replace`) +**Rules:** every rule in `../SKILL.md` applies. One question at a time. + +This stage checks the file, lists what it can and cannot back up, and asks the +user about each gap once. It does not grade the user and does not fill any gap +itself. + +## Step 1: run the validator + +```bash +python3 scripts/validate_profile.py PROFILE +``` + +- **Problems** (lines without `warning:`) must be fixed before the interview + ends. Each one names a YAML path. Explain it to the user in one plain + sentence, ask the one question that fixes it, read the fix back, and write + it with `add_entry.py`. Run the validator again until it prints no problems. +- **Warnings** are kept as a list for step 2. They are allowed in a finished + file. + +## Step 2: the gap check + +Look for the weak spots the validator cannot judge: + +- skills scored 3 or higher with no `evidence_ids`; +- `proof: unchecked` evidence that a posting would lean on (a skill at 4 or 5 + that rests only on it); +- evidence with no `dates`; +- must-haves (lane requirements) and hard blocks with no `why`; +- lanes whose requirements look copied from each other. + +Then label every claim in the file with one of five words. Show the list to +the user, grouped by label: + +| Label | Means | +|---|---| +| **confirmed** | backed by at least one `proof: checked` evidence entry | +| **no record** | a skill at 3 or higher, or a claim, with no evidence entry, or only an interview answer behind it | +| **stale** | a skill at 3 or higher whose `last` is `5plus`, or whose only evidence ended more than five years ago | +| **overclaimed** | a skill at 4 or 5 where every linked entry is `unchecked`, or is `REVIEWED` work while the skill is about doing it | +| **underclaimed** | checked evidence shows a skill the user scored 0 to 2, or did not score | + +These labels describe the file, not the person. Say that once. + +## Step 3: one question per gap + +Ask about each gap, one question at a time, in the order of the list. Examples: + +- "Your file scores writing prompts at 4, and the only thing behind it is your + own account of the style prompts. Is there a page or file I can read?" +- "The style guide entry has no dates. Roughly when did you write it?" +- "Your file scores OpenAPI at 1, but the API reference rebuild used it. Do you + want to change the score?" + +What the user says decides it: + +- New proof, a date, or a reason: read back the corrected entry and write it + with `--replace`. +- "Leave it": leave it. The gap stays, and the assessment will report it as + unproven or unknown. +- A lower score or a correction: write it. Never argue for a higher one. + +Never change a score, an authorship mode or a proof value without the user's +answer. A lower `proof` is never upgraded because you think the work is real. + +## Step 4: the closing question + +Ask exactly this, and wait: + +**"Does this file feel like a fair picture of you?"** + +Store the answer in the user's own words, unedited, in +`meta.intake.closing_answer`, together with the full `stages_done` list: + +```bash +python3 scripts/add_entry.py PROFILE --section meta --entry - <<'YAML' +updated: "2026-10-01" +intake: + stages_done: [0, 1, 2, 3, 4, 5, 6] + closing_answer: "Yes, that is a fair picture of me." +YAML +``` + +If the answer is "no" or "not quite", ask what feels wrong, one question at a +time, and fix those entries before storing the closing answer. Never override +the user's own read of the file with your own. + +## End of interview + +Run the validator one last time and show its output. Tell the user, in two or +three sentences, what the file now holds, how many claims are confirmed, and +that the assessment can be run against it.