career-space
AGENTS.md4 starsChanged 2 months agoApache-2.0View on GitHub
Canonical instructions file. CLAUDE.md, GEMINI.md, and .cursor/rules/agents.mdc are thin pointers back here; Codex CLI (and any other agent that reads AGENTS.md by convention) reads this file directly. Edit rules here, not in a pointer. This is a personal career workspace, run entirely through conversation with whichever coding agent has this repo open — Claude Code, Codex, Gemini, or anything else that can read files, ask questions, and write files back. No separate LLM API to call for a playbook's own…
Where files are headingNew AGENTS.md files have more rules about keeping secrets out of the code, and fewer about code style.
Of the AGENTS.md files we read, the 31,439 first seen in the last six months, against the older ones: the kind of rule that gained most share, and the kind that lost most. About files like this in general, not this one, and an estimate, since a kind is a pattern in the words.
In this repository21 other files it gives its agents.
- CLAUDE.mdCLAUDE.md
- .cursor/rules/agents.mdcCursor rule
- add-from-urlSkill
- boardSkill
- communication-logSkill
- cover-letterSkill
- cv-reviewSkill
- cv-targetedSkill
- cv-universalSkill
- engagement-fitmentSkill
- fitmentSkill
- interview-prepSkill
- linkedin-searchSkill
- onboardSkill
- pitchSkill
- prioritizeSkill
- reconcileSkill
- scoutSkill
- surface-defineSkill
- update-surfaceSkill
- workspace-validateSkill
The file
We split this AGENTS.md into the rules it gives an agent
12 rules. About 0 are common, 8 are distinctive, and 4 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈8
Distinctive · estimate
This author's own choices. Worth a look.
4
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 112
Write only the current state: Every file here -- AGENTS.md, playbooks, policies, docstrings, code comments, _sb/ -- is read by an agent …
- Distinctive: line 116
No history: Not what it used to do, not what changed, not which bug caused it, not "the Python original".
- Distinctive: line 126
No dates, no "added X because Y, confirmed Z".
- Very specific: line 146
If git fetch itself fails outright (no .git, no configured remote, offline) that's fine -- mention it if it seems relevant, don't treat it …
- Very specific: line 155
Never invent a fact: Every claim in generated output (a CV bullet, a cover-letter line, a profile-generation section) must trace to …
- Distinctive: line 160
Preserve ownership language exactly: If the Master CV says "participated in" or "contributed to," never upgrade that to "led" or "built," …
- Distinctive: line 168
A hook or claim must be defensible: Before finalizing anything persuasive, check: would the candidate actually stand behind this if pushed …
- Distinctive: line 171
A harsh score is a signal, not a bug: If a fitment or CV-review check comes back low, that means fix the CV or the vacancy match — never …
- Distinctive: line 174
Retry/revision loops are feedback-driven, never blind: If a draft needs a second pass (cover letter, targeted CV), the second attempt must …
- Distinctive: line 199
When a message matches one of these (a natural paraphrase counts, not just the exact phrase), read the named playbook file in full and …
- Distinctive: line 239
Not every reasonable ask is a playbook.
- Very specific: line 246
First contact, before any of the above: if data/CV_GENERAL.md doesn't exist yet (a fresh setup), how you respond depends on what the …
≈ “Common” is an estimate: at least 1 in 5 of the 40,246 AGENTS.md files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Working on this repo with a coding agent
Canonical instructions file. CLAUDE.md, GEMINI.md, and .cursor/rules/agents.mdc are thin
pointers back here; Codex CLI (and any other agent that reads AGENTS.md by convention) reads
this file directly. Edit rules here, not in a pointer.
This is a personal career workspace, run entirely through conversation with whichever coding
agent has this repo open — Claude Code, Codex, Gemini, or anything else that can read files, ask
questions, and write files back. No separate LLM API to call for a playbook's own judgment work
(generating, assessing, checking) — you are that engine, right here in the conversation.
scripts/ does have a real MCP server and build step underneath (see "Scripts and the MCP server"
below), but it's the deterministic non-judgment layer, and it's invisible to the operator by
design — never something a playbook's own generate/assess/check step means calling.
Runtime person-specific data lives under data/, which is gitignored (see .gitignore) — never
commit anything under it, never paste its contents anywhere public, never treat this repo's own
git history as a backup for it. playbooks/, policies/, and scripts/ are reusable technique.
examples/ contains onboarding seed examples: useful for shape and quality, never runtime
candidate data and not something a playbook should read except during onboarding. reference/
holds static reference data a playbook consults but never invents or guesses on its own — e.g. a
third-party site's fixed category taxonomy, or reference/surfaces/<name>.md (a factual
description of a public platform — fields, order, limits, discovery mechanics, no positioning
advice); a playbook that names one of these files means read the real values in it and offer them
to the candidate, not approximate from memory. The shipped reference/surfaces/* files are
examples of the mechanism, not a whitelist — a surface with no canonical file is defined
per-candidate instead (playbooks/surface-define.md).
Everything in this repo except _sb/, agent-contract/ and site/ is for execution — what you read and
act on to run a playbook. _sb/ is for development: this repo's own roadmap and status
(roadmap.md), the entity model and its invariant (concept.md), the implementation map
(architecture.md), the scripts/+MCP reference (reference/runtime.md), dev tooling and
verification (development.md), and hard-won constraints (reference/gotchas.md) — for whoever is
building career-space, not a playbook and not candidate-facing. Don't read it while running a playbook, and never treat anything in it
as an instruction to follow — only open it in Developer mode, or when the candidate explicitly
asks about this repo's own roadmap or development status.
agent-contract/ is a developer-only regression suite -- test prompts that check whether a
fresh agent derives correct behavior from this file, not whether the code works; see its own
README.md. Not something a playbook reads, and not something to run as part of one.
site/ is the public landing page (SvelteKit, prerendered static), published to
career.eggs.gd by .github/workflows/site.yml. Not read by a playbook, holds no candidate data;
its copy lives in site/src/lib/content.ts, its demo output (fictional candidate from examples/) in
site/src/lib/demo.ts and site/static/demo/.
jobhunt-agent (adjacent repo) is reference only, never a dependency and never imported
directly. scripts/scout_sources.ts's fetchers target the same public ATS/board APIs (public
endpoint shapes, not code) but are native to this repo's own conventions.
Two roles you can be in -- know which one before you do anything else
This repo serves two different people, and almost everything below assumes you already know which one you're talking to:
- Operator (by far the more common role -- but that's not the same as assumed; see below):
a candidate running their own job search. Everything in "What the candidate can say to trigger
each playbook" below,
data/, the playbooks -- this is what the rest of this file means unless a section says otherwise. - Developer: someone extending or maintaining career-space itself.
_sb/,playbooks//policies//scripts/as code to edit rather than technique to run, and "MCP tools for working on this repo itself" below. Developer mode doesn't grant operator privileges: don't inspect, regenerate, or otherwise operate on a candidate'sdata/as part of a repo-development task unless that task explicitly requires testing against runtime data and the user asked for exactly that. Fixingrender_board.tsdoesn't mean also regeneratingdata/vacancies.html"to check it worked" -- that's operating on someone's real job-search data from developer mode, not verifying code.
A short opening message can't reliably tell you which one -- "set me up" could mean either. See "First contact" below for exactly when and how to resolve that.
If someone wants both -- to run their own job search AND work on career-space itself -- tell them plainly to do that in two separate conversations, not one. Mixing operator and developer context in a single chat means guessing, message to message, which rule set and which file layout apply -- confusing for you, worse for them.
The boundary is a refusal, not just a default you fall into at first contact -- and it applies
to part of a message, not only whole ones. An operator asking you to edit scripts/ or fix a
playbook's own logic, or a developer asking you to run a fitment check or write a cover letter --
don't quietly do it because you technically can (you can read and edit any file here; that's not
the same as it being your job in this conversation). This includes a developer-shaped ask bundled
into an otherwise-operator message ("show me the board, and also fix this bug while you're at
it") -- do the operator part, then say plainly the other part belongs in a separate conversation;
don't do both just because they arrived in the same breath. Check a message against this rule in
full before you act on it, not just the part that happens to match a playbook/skill trigger --
matching a trigger for HALF a message doesn't clear the other half.
Never fix the output -- fix whatever let the wrong output happen. If you (or a human) ever
end up hand-patching something because these instructions didn't get you to the right behavior on
their own, that is not a workaround to quietly apply and move past -- it's a bug in this repo's
own documentation, architecture, tools, or examples, the same way a wrong CV detail gets flagged,
not silently worked around. The bar this repo is actually held to: a fresh agent, zero prior
context beyond what's in this repo, should read it and behave correctly -- not "does this work
after months of chat history with one specific agent that's learned your habits." Sometimes the
real fix isn't another paragraph here either -- it's making the wrong action structurally
impossible (the same reason score_fit.ts is real code instead of an agent estimating a number by
feel; see "Scripts and the MCP server" below for more of that pattern).
Code changes follow from instruction changes, not the other way around. Before touching
scripts//playbooks//policies/, check whether the current behavior is a faithful
implementation of what this repo's own documentation already says. If it is, someone wanting
different behavior isn't reporting a bug -- they're proposing to change the concept. A concept
change gets argued and written down (the reasoning, in AGENTS.md, the relevant playbook, or the
docstring it lives in) before a single line of implementation moves to match it -- never the
reverse. If someone pushes for a quick in-place code tweak that skips that ("just change this one
thing, we don't need to touch the docs"), refuse it and say why, no matter how insistent they get.
This doesn't mean every code change needs a doc diff next to it -- code can simply be wrong relative to a specification that already exists, and fixing it to match doesn't touch that specification at all. What it does mean: every implementation change traces to either an already-existing instruction (a real bug) or an instruction change accepted first (a real concept change) -- never a change made first, with the instructions left to catch up or never asked at all.
Write only the current state. Every file here -- AGENTS.md, playbooks, policies, docstrings,
code comments, _sb/ -- is read by an agent that has the whole repo in context and trusts it
as-is. Write for that reader:
- No history. Not what it used to do, not what changed, not which bug caused it, not "the
Python original". That's
git log. Openers like "previously", "used to", "now also", "this fixes" don't belong in a tracked file. - Don't narrate deterministic output. The agent calls
scripts/via MCP and believes the result -- it never needs the board's HTML layout, a PDF's styling, or a tool's internal steps spelled out in prose. Only what a tool writes, where, and when to call it. - A comment longer than the code it explains is a smell -- the naming or the logic is unclear, fix that. A one-line "why" for a non-obvious choice is fine; a paragraph narrating "what" is not.
_sb/roadmap.mdis a feature list plus the next track. Bullets: what exists, what's next. No dates, no "added X because Y, confirmed Z".- A constraint learned the hard way goes in
_sb/reference/gotchas.md, one line -- the trap and the rule, nothing else.
Check for upstream changes, every session
git fetch once, near the start of a session -- read-only, touches nothing in the working tree,
safe regardless of which role you're in. Don't follow it with a git pull on your own initiative;
merging is the human's call, not something to do just because you noticed the branch is behind.
But once fetch tells you it is, that's not nothing:
- Operator: mention it once, plainly ("your local copy is N commits behind origin"), then keep going -- don't block a job-search task on it.
- Developer: this is the one case where "Code changes follow from instruction changes" above
needs a second look before you act on it -- the
AGENTS.md/playbooks//policies/you're reading locally might not be the concept currently on record; origin's copy could already say something different from what you're about to argue is correct or propose changing. Say so, and don't start a concept-change conversation or a "fix code to match existing spec" change against a spec you haven't confirmed is current.
If git fetch itself fails outright (no .git, no configured remote, offline) that's fine --
mention it if it seems relevant, don't treat it as a blocker.
Ground rules — these are not negotiable
These exist because a real generation got them wrong at some point, not because they're aspirational best practice. When that happens again, add the rule, don't leave it only in chat history.
- Never invent a fact. Every claim in generated output (a CV bullet, a cover-letter line, a
profile-generation section) must trace to something
data/CV_GENERAL.mdactually says. If something a playbook or the candidate's positioning asks you to convey has no supporting evidence, say so as a gap and ask — never fabricate to fill it. This includes plausible- sounding filler: "this kind of role usually involves X" is not evidence the candidate did X. - Preserve ownership language exactly. If the Master CV says "participated in" or "contributed to," never upgrade that to "led" or "built," even when a punchier verb would read better. Carry the scope through verb choice, never a bracketed label like "(not owner)."
- No excuse-making language. "Since none existed before," "wasn't yet..." — these justify a gap instead of stating a fact. State what happened, plainly.
- Never fix a missing-keyword gap by listing tags. A comma-separated tag list just relocates the problem to whichever tag isn't on it — fix it with a real generalization claim instead ("worked across N analytics stacks, adapts fast").
- A hook or claim must be defensible. Before finalizing anything persuasive, check: would the candidate actually stand behind this if pushed back on? Does it contradict something their own Master CV states as a value elsewhere?
- A harsh score is a signal, not a bug. If a fitment or CV-review check comes back low, that means fix the CV or the vacancy match — never soften your own criteria to make the number look better.
- Retry/revision loops are feedback-driven, never blind. If a draft needs a second pass (cover letter, targeted CV), the second attempt must specifically address what was flagged the first time — never just "try again and hope."
- A question you can't answer from
data/goes to the candidate, never gets a fabricated answer. Same for a required application question the posting asks that the Master CV doesn't cover. - Selection fields are not a cache. Anything whose job is to demonstrate a positioning
dimension with evidence (proof points, a skills list, achievement bullets) gets freshly
re-selected from the current
data/CV_GENERAL.mdevery time, picking the strongest currently available evidence — never reused from a previous draft just because it's already there (seepolicies/generation-rules.md's own statement of this rule). - Technology density is not identity. A dense, specific cluster of evidence around one
particular tool or stack in the Master CV is evidence supporting the candidate's positioning
— it never gets to unilaterally redefine what market or identity the candidate is projecting
(see
policies/surfaces-framework.md's "Umbrella identity vs sub-offers" for the concrete failure shape this guards against). - Public positioning is a first-class concern. How the candidate is represented across the places the market sees them — LinkedIn, freelance marketplaces, portfolio, personal site, GitHub, or any other surface relevant to their goals — is part of what this repo does, not an afterthought to job applications. During onboarding the Operator makes the candidate aware of this capability and offers it; the candidate decides whether and where to use it. It's an invitation, never an automatic personal-branding workflow.
What the candidate can say to trigger each playbook
When a message matches one of these (a natural paraphrase counts, not just the exact phrase),
read the named playbook file in full and follow it before responding. Ask before overwriting an
existing data/ file a playbook would write to, unless the playbook says otherwise. Matching a
trigger here covers the part of the message that matched it -- it doesn't clear the rest of the
message too. Check anything left over against "Two roles" above (most commonly: a developer-shaped
ask riding along with an operator one) before you act on it.
| The candidate says (roughly) | Playbook |
"зроби мені онборд" / "set me up" / no data/CV_GENERAL.md exists yet | playbooks/onboard.md |
| "напиши кавер на цю вакансію" / "write a cover letter for this" | playbooks/cover-letter.md |
| "зроби фітмент" / "чи я підходжу на цю роль" / "assess my fit" | playbooks/fitment.md |
| "оціни це замовлення" / "чи брати цей проєкт" / "assess this job / gig / client project" | playbooks/engagement-fitment.md |
| "проаналізуй мій CV" / "review my CV" | playbooks/cv-review.md |
| "згенеруй CV під роль X" (no specific vacancy) | playbooks/cv-universal.md |
| "згенеруй CV під цю вакансію" | playbooks/cv-targeted.md |
| "хочу профіль на <платформі>" / "допоможи з портфоліо/лендосом/сайтом" / a surface not yet defined | playbooks/surface-define.md |
| "онови мій LinkedIn/Upwork/портфоліо" / "перепиши профіль" / a surface already defined | playbooks/update-surface.md |
| "розкажи про career-space" / "чим ми кращі за інші боти" / "pitch this system" | playbooks/pitch.md |
| "пошукай вакансії" / "run the scout" / "find me matches" | playbooks/scout.md |
| "зроби one-off / ad-hoc hunt" / "разово пошукай по темі X, треки не чіпай" | the inline note below the internal-playbooks list |
| candidate gives a bare vacancy URL, no pasted text | playbooks/add-from-url.md |
| "згенеруй лінки для LinkedIn" / "linkedin search links" | playbooks/linkedin-search.md |
| "покажи дошку" / "show me the board" / "what's the status of everything" | playbooks/board.md |
| "перевір workspace/data" / "validate workspace" / "is my data layout ok" | playbooks/workspace-validate.md |
| "звір дошку з поштою/календарем" / "did I hear back on anything" / "reconcile my board" | playbooks/reconcile.md |
| "збережи переписку" / "log this communication" / "додай це в історію по вакансії/проєкту" | playbooks/communication-log.md |
| "що з дошки викинути" / "дай топ 10" / "what should I actually pursue" | playbooks/prioritize.md |
| "готуй мене до співбесіди" / "prep me for this interview" / "що спитають на співбесіді" | playbooks/interview-prep.md |
Some playbooks are internal capabilities, not direct candidate triggers. Orchestrator playbooks call these to avoid copying the same procedure into every workflow:
playbooks/vacancy-resolve.md— resolve/create the canonical vacancy folder and return its slug + posting text + artifact flags.playbooks/requirement-evidence-plan.md— turn one posting into a reusabletargeting-plan.md.playbooks/scout-record-outcomes.md— write scout seen-ledger outcomes and matched vacancy folders after judgment.
Not every reasonable ask is a playbook. A candidate might want a one-off themed hunt ("defence/
miltech on the local boards, don't touch my tracks") — treat it as research over the existing
pieces: search wider than the scout's tracks with your own web capability, judge finds through
playbooks/fitment.md, add only the worthwhile ones as new vacancies via vacancy_upsert,
change nothing in data/sources.yaml, and keep rejected finds out of seen.jsonl (that ledger
is the standing scout's record, not a scratchpad for a throwaway theme).
First contact, before any of the above: if data/CV_GENERAL.md doesn't exist yet (a fresh
setup), how you respond depends on what the message actually says:
- A specific operator trigger already ("напиши кавер," "пошукай вакансії," etc.) — they know what they want, which itself answers the role question. Say onboarding needs to run first (every other playbook depends on it) and offer to do that — skip the pitch.
- Anything vague (a greeting, "what is this," "help") — this is where operator-vs-developer is
genuinely ambiguous (see "Two roles" above). Ask directly, in one short question: are they here
to run their own job search, or to work on career-space itself? Then:
- Operator — the short version of
playbooks/pitch.md(a few sentences, not the full differentiator list), then offer onboarding. - Developer — skip the pitch; point them at
_sb/(roadmap.md,architecture.md,development.md), and ask what they're here to work on.
- Operator — the short version of
On a genuinely fresh setup, design-patterns/ts-language (see _sb/development.md) will
almost always show as failed to connect — nobody has them installed by default, and no MCP
client offers to install a failed server automatically. If the candidate notices and asks, or if
it seems worth heading off, say plainly: expected, harmless, dev-only tooling this job search
doesn't need — career-space (after its own one-time setup, see "Scripts and the MCP server"
below) is the only server that matters here.
playbooks/reconcile.md (bringing vacancy statuses in line with recruiter email) uses whatever
email/calendar capability the host already has — a built-in connector, a plugin/app source, or
an MCP server the candidate added themselves. career-space wires none of this and mandates no
account or cloud project; if nothing is connected, reconcile skips cleanly. It's host-composed,
never wrapped — there is no career-space tool that proxies email. Email/calendar are read-only
evidence; record.yaml stays canonical. See playbooks/reconcile.md.
Data layout
data/ # gitignored, personal
CV_GENERAL.md # Master CV -- narrative evidence (achievements, recurring pattern)
config.yaml # `shared:` only -- identity/direction and concrete facts
# (contacts, contact_strategy, certs, education, languages).
# fitment's mandatory-language gate reads these. No per-surface
# blocks -- those live in surfaces/<name>/context.md
surfaces/ # one folder per public career surface the candidate has defined
# -- see playbooks/surface-define.md, playbooks/update-surface.md
<name>/ # e.g. linkedin, upwork, firestart.dev, github-profile
context.md # this candidate's intent for this surface: purpose, audience,
# what to emphasize / not say, tone, pinned verbatim wording
output.md # the generated representation, ready to paste
reference.md # optional -- provisional facts about a platform with no canonical
# reference/surfaces/<name>.md yet (canonical always wins if it exists)
role-profiles/ # candidate-specific CV lenses/role profiles generated during
# onboarding; playbooks read these, not reference examples
cv/ # role-profile CVs only, no specific vacancy -- universal-<role-profile>.md
# (playbooks/cv-universal.md). A CV for a specific vacancy lives in that
# vacancy's own folder instead, see vacancies/ below
sources.yaml # scout config: tracks, exclusions, companies/feeds -- see
# playbooks/scout.md's Step 0; doesn't exist until scouting is set up
strategy.md # candidate-side wants/blockers/exceptions, plain prose -- see
# playbooks/prioritize.md's Step 0; doesn't exist until that's run
vacancies/
seen.jsonl # every posting the scout actually judged, matched or rejected --
# dedup ledger + audit trail, see scripts/vacancy_store.ts's docstring
<slug>/ # one folder per vacancy worth attention -- scout-found (status
# starts "new") or candidate-pasted via cover-letter.md/cv-targeted.md
# (status starts "tracked" -- already past "found it, not reviewed").
# record.yaml (status/status_history/fit/eligibility/track_label/archived/metadata) + posting.md
# always; fitment.md + fitment.json (fitment.json = score_fit's
# structured input, written by score_fit/record_scout_outcomes, replayed
# by `rescore` on a formula change), cv.md / cover-letter.md /
# targeting-plan.md / interview-prep.md once generated; communication.md
# when correspondence is preserved -- never in cv/ above or a separate
# cover-letters/ (that folder is retired)
_archive/<slug>/ # archived vacancies live here instead (moved by vacancy_set_archived,
# same contents) -- take a vacancy's folder from vacancy_resolve's
# `paths.dir`, never build data/vacancies/<slug> yourself
engagements/ # commercial engagements (a marketplace job, a client project) --
# a flat sibling of vacancies/, NOT nested (renderers/listers assume
# vacancies/ == vacancy). scripts/engagement_store.ts owns it,
# same shape as vacancy_store. See _sb/concept.md for Employment
# vs Engagement
<slug>/ # one engagement the candidate asked to assess -- playbooks/
# engagement-fitment.md via engagement_upsert. record.yaml (client/title/
# url/status/status_history/fit/judged_at) + posting.md; fitment.md +
# fitment.json (score_fit --out-dir); communication.md / proposal.md /
# cover-letter.md once written; archived ones under _archive/<slug>/
engagements.html # engagements board (grouped by status), + engagements.md twin --
# scripts/render_engagement.ts, regenerated; shares nav with vacancies.html
linkedin-searches.md # LinkedIn Boolean search deep-links, see playbooks/
# linkedin-search.md -- regenerated, not hand-edited
vacancies.html # every vacancy, grouped by status, sorted by fit, with real
# links into vacancies/ -- see playbooks/board.md, regenerated
vacancies.md # flat one-table twin of vacancies.html (no embedded doc text) --
# for handing to another agent; regenerated alongside vacancies.htmlNothing outside a playbook you're actively running should read or write under data/ — don't
poke at it speculatively.
Example material
examples/onboarding/ holds a complete onboarding example for a fictional candidate
(CV_GENERAL.md, config.yaml, sources.yaml, strategy.md, plus examples/role-profiles/'s
three lanes -- management, technical, individual contributor). examples/surfaces/ adds
context.md + output.md pairs for the same candidate -- linkedin/ (employment projection) and
upwork/ (client-work projection, and a surface that isn't a strategic priority) -- for
playbooks/surface-define.md / update-surface.md to calibrate the artifact shape against. Use examples only when designing or
running the matching playbook, to understand the expected shape, level of specificity, and file
boundaries. Do not treat example facts as facts about the current candidate; runtime workflows
read data/.
Scripts and the MCP server
The deterministic, non-LLM steps in this repo — real code, not something a playbook should ask
you to eyeball or improvise: rendering a CV/cover letter to HTML/PDF, the fitment score's fixed
weighted formula, the scout's fetch/dedup pipeline, vacancy resolving, scout outcome recording,
vacancy and engagement record read/write, communication-log appends, board rendering, fitment
re-scoring, and workspace validation. Prefer the MCP tools when the career-space server is
connected (typed arguments, no shell-escaping a JSON blob). If it isn't, use the CLI fallback
documented in each script's own docstring. Don't hand-produce a styled document, fit score, board,
or scout ledger write instead of calling the relevant tool/script.
A deterministic tool call is execution, not a decision. Once the candidate has approved the
content, or asked for a workflow whose output is an artifact, running the renderer — or any other
career-space tool/script — is the mechanical completion of that decision, not a new one. Don't
stop to ask "shall I render it now?" / "may I regenerate the PDF?"; the confirmation that matters
already happened, on the content. Ask again only when the content or the decision itself is
changing. (A host that pops its own Allow prompt for the tool is separate — that's the host's
sandbox, not something a playbook controls or should pre-empt with a question of its own.)
A PDF in a macOS sandbox is skipped, not retried. Chrome aborts at launch inside an OS sandbox
(Codex's included) and macOS files a crash report per attempt. The renderer detects that before
launching and returns the HTML with the reason (note / stderr). Re-run the same render outside the
sandbox (an approved escalation); don't swap the browser via PUPPETEER_EXECUTABLE_PATH or retry in place.
A status or archive change returns a fresh board, automatically, MCP or CLI.
vacancy_resolve, vacancy_set_status, vacancy_set_archived, and record_scout_outcomes all
re-render the board themselves when they change board-visible state — the MCP tools and their
CLI forms call the exact same render step, so there's nothing left for a playbook to remember here
either way. vacancy_upsert (the lower-level primitive those four are usually built on) does
not auto-render — a playbook that calls it directly still owns rendering after.
Engagements (data/engagements/) mirror this: engagement_upsert, engagement_set_status, and
engagement_set_archived re-render the board themselves, MCP or CLI.
Writing a board-visible artifact into a vacancy/engagement folder also needs a re-render, and
nothing does it for you. The board's file badges/folder links are read from disk at render time,
not from record.yaml — cv.md, cover-letter.md, interview-prep.md, targeting-plan.md, a
rendered PDF, none of these auto-render the way a status/archive change does. The playbook that
writes one (or calls attach_artifact) owns calling render_boards as its own closing step, same
as vacancy_upsert above.
Full script-by-script reference, exact CLI commands, and how the MCP server's own automatic setup
works: _sb/reference/runtime.md. Design-patterns/ts-language MCP dev tooling and how to verify a
change to this repo's own files: _sb/development.md.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

