perfect-placement
AGENTS.md3 starsChanged 33 days agoMITView on GitHub
This is the only agent instruction file in this repo. There is no CLAUDE.md anywhere, and there must never be one (decisions.md #8): Claude Code reads AGENTS.md only when no CLAUDE.md exists in the working directory or any parent. pp doctor checks the parent chain. A local job-search system. It scouts roles, scores them against a learned profile, keeps the pipeline fresh, tailors applications, finds people who can open doors, watches the inbox, and learns from outcomes. The repo holds…
- Reads credentials
- Installs packages
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,700 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.
The file
We split this AGENTS.md into the rules it gives an agent
3 rules. About 0 are common, 2 are distinctive, and 1 is very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈2
Distinctive · estimate
This author's own choices. Worth a look.
1
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 42
Writing in the user's voice: no em dashes; punchy (hook, reframe, close).
- Distinctive: line 43
UI copy: no explanatory text, no tips, labels of one to three words (buttons and labels.
- Very specific: line 88
Every vault markdown file starts with frontmatter id + type.
≈ “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
Perfect Placement: operating manual for agents
This is the only agent instruction file in this repo. There is no CLAUDE.md anywhere, and there must never be one (decisions.md #8): Claude Code reads AGENTS.md only when no CLAUDE.md exists in the working directory or any parent. pp doctor checks the parent chain.
What this is
A local job-search system. It scouts roles, scores them against a learned profile, keeps the pipeline fresh, tailors applications, finds people who can open doors, watches the inbox, and learns from outcomes. The repo holds only the system. Each user's data lives in a local vault at PP_HOME (default ~/.perfect-placement): SQLite (pp.db) for structure, markdown for long-form, linked by id. The web UI, the MCP server, the pp CLI and scheduled runs all call one ops registry, so anything a human can click, an agent can do.
Background: docs/architecture.md (how the parts fit), docs/decisions.md and docs/wave1-decisions.md (why; code cites them as decisions.md #N and wave1-decisions #N), docs/design.md (the UI). Contributor workflow: CONTRIBUTING.md.
Layout
AGENTS.md this file
packages/
contracts/ zod schemas, statuses, events, ops registry, resolver + policy types, design tokens, UI inventory
engine/ openEngine(): db/ (repos, migrations, vault, doctor) ops/ (runOp + handlers) runners/ (one module per agent CLI) status/ profile/ scoring/ freshness/ people/ integrations/
resolver/ decision layer: rule, Kev, agent, human; policy; decision ops
mcp/ MCP server generated from ops (stdio `pp-mcp`, HTTP at /mcp)
cli/ `pp`: setup, up, init, kev, doctor, op, scout, score, fresh, inbox, uninstall
fixtures/ synthetic vault + db seed
apps/web/ UI from the Claude Design prototype; genui/ renders Ask replies with OpenUI
skills/<name>/SKILL.md agent-agnostic skills; tools are {{placeholders}} each runner names (packages/engine/runners/README.md)
docs/ architecture, decisions, UI design; history/ keeps the build-era contract logRules
- Every write is an op. Nothing edits
pp.dbor the vault except op handlers called throughrunOp(name, input, ctx). Skills, UI, MCP and CLI never write directly. - Model supplies judgment, code supplies mechanics. Skills produce search hits, 1 to 5 scores, classifications and drafted text, then call ops. Totals, bands, dedupe, status moves, counts, run logs and validation are code.
Decisions go through the resolver. A human never sees a blank question: they see the pick and its probability. Order: rule -> Kev (local System One model) -> the calling agent session -> human. No API key is used: LLM work happens in the agent session (Claude Code, Codex or Cursor) over MCP. Policy (confidence x impact, thresholds in
config.json,POLICY_GRIDin contracts):Impact \ p >= 0.90 0.70 to 0.90 < 0.70 Low auto auto auto + undo Medium auto auto if a second opinion agrees human High auto + undo human human Hard exclusions, always human, not tunable (
isHardHuman): submit an application, send an email, delete anything, change a dealbreaker, change the comp floor. Per-kind overrides inconfig.overridescan tighten thresholds or turn a confidentautointoauto + undo(profile diffs and inbox classifications do by default). Agents passconfidenceon medium-impact tool calls; without it the op queues for the human. System One models answer typed questions only; they never write text.- Never submit an application or send an email. Gmail is read plus draft only. Applications end at Open portal; outreach ends at Create Gmail draft. The fill assist (#106, Open and fill) may upload the rendered PDFs and fill answer fields in the portal (
form_input,file_upload; no click, key or script tool), then stops before Submit; the human submits. A Gmail draft waits until its To is confirmed (#99): a guessed address goes through the passive email check first (syntax, DNS MX or A, vendored risk lists, pattern evidence from the user's own mail and public pages; never a connection to a mail server or a verification site, no paid provider) and, unless proven, Needs you, where the user checks it by hand (Check, then Valid / Invalid); a draft already in Gmail without To gets only its To updated. LinkedIn automation is read-only, capped, slow, and never messages or connects. - No personal data in this repo. Nothing from a user's profile, pipeline, applications, interviews, resume,
.docxor vault is ever committed. Fixtures are synthetic. Tests use temp dirs and never touch the realPP_HOME..gitignoreblocks vaults,*.db,.env, resumes,.pdfand.docx; do not weaken it. - Writing in the user's voice: no em dashes; punchy (hook, reframe, close). Grounded in the Story and Voice components only; never invent facts.
- UI copy: no explanatory text, no tips, labels of one to three words (buttons and labels; onboarding interview questions are full sentences with an optional one-line example, wave1-decisions #28). One primary action per screen. Every button calls exactly one op. Dark only, tokens from
@perfect-placement/contracts. - No-downgrade: automated actors never move a role backwards out of
draft-readyor later, and a rescore never moves anon-deckpick. On-deck = picked (#105): starting Research, Tailor or Connect on an active or held role moves it there (role_picked), never from on-deck or later.
How to run
./install.sh # prerequisites, pnpm install + build, `pp` + `pp-mcp` launchers in ~/.local/bin (--check: report only)
pp setup # vault at PP_HOME (default ~/.perfect-placement), optional Kev (offers to install uv), MCP for detected agents, schedule jobs (asks), then a pass/fail summary (--yes, --no-kev, --no-mcp, --no-schedules)
pp up # API /api/ops, Ask /api/ask (agent session, SSE), MCP /mcp, web UI on http://127.0.0.1:5177; starts Kev if installed (--no-kev)
pp up --fixtures # demo on synthetic data
pp doctor # integrity checks
pp op <name> --input '<json>' # any op
pp drain # launch queued skill runs once (pp up does it continuously)
pp run <skill> [--schedule] # queue a skill run, drain until it (and what it triggers) finishes; what schedule jobs run
pp schedules install|remove|status # config.schedules as OS jobs (launchd / systemd user timers); pp setup offers it
pp kev install|start|stop|status
pp uninstall [--purge] [--dry-run] [--yes] # or ./uninstall.sh: undo setup (keeps the vault); --purge: vault (tar.gz backup first) + Kev's HF model files
corepack enable # development: pnpm version pinned in package.json
pnpm i && pnpm build && pnpm test && pnpm lint
pnpm --filter @perfect-placement/fixtures gen # after editing fixtures/src/data.ts(pp is the CLI bin; without the launcher use node packages/cli/dist/index.js ....)
- Node 22+.
better-sqlite3is native: on macOS it needs the Xcode command line tools if no prebuilt binary matches. - Kev (local decision model):
pp kev installputs it in$PP_HOME/kev/;pp upstarts it on demand and stops it on exit only if it started it. Model inconfig.backends.kev.model. Background:packages/resolver/docs/kev-setup.md. - Agents (Claude Code, Codex, Cursor, Claude Desktop):
pp setupregisters the stdiopp-mcpserver (keypp, absolute node + bin,PP_HOMEenv) after asking; manual snippets inpackages/mcp/README.md. Tool names use underscores (roles_list) because all three clients mishandle dots; op names stay dotted.runs.startqueues a headless runner fromconfig.runner(claude,codexorcursor-agent); the run queue launches it (config.runs.max_concurrent), and the pipeline chains scout -> score -> research (config.pipeline; before scoring, a freshness pre-check archives the scout's new inbox roles that are closed, #77). The runner passesPP_RUN_IDto the pp MCP server, so a headless run's ops and events carry its run id. Schedules run as OS jobs (pp schedules, seepackages/cli/README.md);pp upqueues any schedule missed since its last run. Scheduled runs (#121) keep the machine awake while they run (caffeinate / systemd-inhibit, tied topp run's pid), are capped per skill (stalled), and retry transient failures after 1, 5, 15 min (one retry policy per run; other queue runs keep #122's single 45 s connector retry). - Uninstall:
pp uninstall(./uninstall.shpicks the node and builds the CLI if needed) reverses setup through the samesrc/system/modules: stop the Kev pp started, remove schedule jobs, only theppMCP entry per agent (backup first), and launchers that are Perfect Placement's.--purgeadds the vault (verified tar.gz backup to~/perfect-placement-backupsunless--no-backup; refuses non-vault or git-tracked dirs unless--force) and Kev's Hugging Face files (Kev dirs + pinned base snapshots, unshared blobs only). Plan first, asks,--dry-run. It never touches Node, pnpm, uv, Homebrew or the checkout. Never run it against the real HOME from a test or an agent session. - Tests never touch real machine state:
pp setup/pp kevtake an injectedSystemDeps(temp HOME, fake spawn, no network);install.shis tested with--checkonly. CI jobfresh-installruns the real./install.shandscripts/fresh-install-smoke.sh(setup, up, write, restart, read back). - Web dev:
pnpm --filter @perfect-placement/web dev(port 5178) proxies/apitopp upon 5177;dev:mockruns on read-only fixtures. - OpenUI telemetry: set
OPENUI_TELEMETRY_DISABLED=1in your shell; rootpackage.jsonalso blocks its install script (pnpm.onlyBuiltDependencies). Never add@openuidev/lang-coreto that list.
How work merges
- One branch per piece of work, in its own worktree. Open a PR when the branch is finished and ready to close; merge through the PR, then delete the branch and worktree. No big-bang PRs.
- Contracts:
packages/contractsand migrations change in their own pull request that updates the schemas, tests and docs together (the build-era log isdocs/history/contract-changes.md). New schema changes are new numbered migrations; never edit an applied one. - Develop against fixtures (
@perfect-placement/fixtures, or@perfect-placement/engine/testingfor a real engine on a temp fixtures vault). Tests never touch the realPP_HOME. - Real-data steps (first scout + score, rescore, schedules, live LinkedIn or Gmail) only with the vault's owner present.
Conventions
- TypeScript strict, ESM, zod as the single schema source (JSON Schema for MCP via
opInputJsonSchema). - Ids are TEXT. Role id =
<company-slug>_<title-slug>; company id = slug; everything else<prefix>_<ulid>(ID_PREFIX). - Timestamps ISO 8601 UTC.
*_jsoncolumns hold JSON; booleans are 0/1 (BOOLEAN_COLUMNS). - Every vault markdown file starts with frontmatter
id+type; structured fields live in the db, never parsed from markdown. scoreandeventare append-only (enforced by triggers).
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.

