236 Commits
Author SHA1 Message Date
Federico EliaandClaude Opus 5.5 300fe36373 The account password is sealed at rest, with its key in a file of its own
It sat in clear in the answers table, so whoever copied jobs.db had it.
It is now encrypted with Fernet from the cryptography package, under a key
in data/secret.key, made the first time it is needed and readable by its
owner alone where the system allows it. A password kept plain by an older
AIHawk is sealed on its first read. It is opened whenever the answers are
read, because the form model still has to type it; a key that cannot
open it says the key file was replaced instead of failing quietly. The
constant that names it now lives once, in the store.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-07 16:11:05 +01:00
Federico EliaandClaude Opus 5 a59e207d3c The accounts it made are kept per site, so nobody is asked to sign in by hand
A row in Settings asked the person to open the browser and sign in to a
job site themselves. It should never have been their job. The store now
has an `accounts` table, host to email, and the brief for every form says
either "Your account on nvidia.wd5.myworkdayjobs.com: made on
2026-11-07 with [email protected], and the account_password from the
answers" or "No account of yours is known on this host: if the form needs
one, make it". The model reports with finish_form whether it made one,
and it is written whatever the form then did, because the account exists
on that site either way.

So the row is gone, and with it the hidden `l` key, the worker behind
them and the browser's blank-page sign-in. The mailbox sign-in stays:
that one is a person's own mail, and it is asked for once, when a site
sends a code.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-11-07 14:38:18 +01:00
Federico EliaandClaude Opus 5 dac9bda8e3 Settings drops "Spent since the start"
A running total nobody asked for, on a screen that is for changing
things. What each application and each scoring cost is still said in the
log as it happens, and a sent application keeps its own cost in its
details, which is where a number answers a question.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-11-07 14:30:49 +01:00
Federico EliaandClaude Opus 5 cd4a59a996 Settings drops "Look for": the titles come from the resume, not from a text box
A row that let the titles be retyped by hand sat above the graph that the
resume builds, with its nodes, forms and weights, and a retyped list
flattened the first node to plain words. The row is gone, and so is the
branch that saved it. The heading still says what the search looks for,
and a different search comes from a different resume.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-11-07 14:29:38 +01:00
Federico EliaandClaude Opus 5 5a77154ff5 Six test applications on real postings, and the facts they settled
JazzHR, BambooHR, Personio, Breezy, Teamtailor and Greenhouse, each on a
real posting with the test CV, nothing sent: 8 to 48 steps, under a cent
to a cent each. Five of the unknowns the plugins admitted to are now
facts.

The one worth saying once: a file input the page hides refuses the file,
and the upload goes to the button beside it, which the page labels. It
happened on Greenhouse and on Personio in the same run, so it is in the
rules every form gets, and each plugin keeps its own selector:
`[aria-labelledby='upload-label-resume'] button` on Greenhouse,
`[aria-labelledby="doc-label-cv"] button` on Personio, `a.resume` on
Breezy, `#resumator-resume-value` on JazzHR.

Also settled: a closed BambooHR posting sends the browser to the
company's whole list at /careers, which is that plugin's missing
closed-posting fact; Personio's company questions are
`#field-custom_attribute_<number>` and its send control is
`form button[type="submit"]`, with the page in English on some .com
postings; Breezy's send control is a `type="button"` and uploading fills
its summary field; Teamtailor's cookie banner is declined by its
aria-label in the page's language.

docs/sites.md records each run with its steps.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-11-07 13:21:27 +01:00
Federico EliaandClaude Opus 5 cc06ee41ed The plugin guide says the one rule, and stops teaching what the model already gets
The guide still described menus as if menus.md did not exist, told authors
to name a sender domain for the mailbox, and quoted a Lever plugin three
changes out of date. It now says the rule a sentence must pass, with one
example that belongs and one that does not; the two things that are not
road but boundary; and what the code already decides, which is the step
budget, the two mailbox visits, and the rules every form gets. Its menus
section points at menus.md and asks only for what is a site's own. The
Lever example is the file itself. A dry run on a real posting is named as
the first way to test a plugin.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-11-07 13:04:34 +01:00
Federico EliaandClaude Opus 5 d12ac83050 Three dry runs on the confirmed sites, and the rule that keeps the model on the form's site
Greenhouse, Ashby and Lever, each on a real posting with the test CV,
nothing sent. Lever filled in 22 steps and under a cent, with no captcha;
Ashby in 40 steps and a cent; Greenhouse met a closed posting.

What the runs taught, now in the plugins: a closed Greenhouse posting
sends the browser to the board at /<company>?error=true, which is the
closed-posting fact that plugin lacked; Ashby's fields are bare uuids read
from each label's `for`, its Yes and No are the question's own buttons or
labelled radios, its consent box is required and its send control, which
no id names, was matched alone by `#form > button:last-of-type`; Lever's
location suggestions are `#location-0` and the rest.

On the closed posting the model went to a search engine to look for the
job, which is three steps spent outside the task. The rules every form
gets now say to stay on the form's own site and the mailbox the site's
instructions name, and that a closed posting is reported, not hunted.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-11-07 12:42:48 +01:00
Federico EliaandClaude Opus 5 4cee3f5a04 A menus plugin for every form: the four kinds of list, and what to do when one fights back
The step that goes wrong on a real form is almost always a list. Instead
of repeating that in fourteen site plugins, menus.md says it once and is
added to every form's instructions, as the mail plugins are: which of the
four kinds the field is (a native select, a listbox, a typeahead, a radio
group drawn as a menu) and what opens, reads and sets each; that the
choice is read back and never assumed; that a click the tool refuses is
not clicked again, but reached with ArrowDown and Enter, or by its centre
read with browser_evaluate when it sits in a shadow root; and that a list
which has won three times is left, blocked and named, because a form half
filled says more than fifty steps spent on one menu. Paylocity's plugin
keeps only what is its own, the ids and which option declines.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-11-07 12:09:43 +01:00
Federico EliaandClaude Fable 5.1 e67a25eced Plugins: what to do when stuck, and how each kind of list is worked
From the traces of 2026-11-07: SmartRecruiters' preliminary questions take
their text on `input#question_<uuid>`, not the bare id; Workable's cookie
banner, file input and div radios by the selectors that worked. The guide
has two new sections, "When the model is stuck" and "Menus and dropdowns",
each case with the failure the model sees, the way out, and where it was
seen, so a plugin gives direction in the moments it is needed.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-11-07 11:56:51 +01:00
Federico EliaandClaude Fable 5.1 fb3dc4ab6c sites.md: a dry run on Workable, 27 steps to the send control
Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-11-07 11:49:48 +01:00
Federico EliaandClaude Fable 5.1 e18bec83e7 Paylocity: the demographic lists, step by step; sites.md: two dry runs of 2026-11-07
A dry run with a test CV reached the send control on SmartRecruiters in
42 steps and on Paylocity in 148 of the 150 allowed: the demographic
listboxes and typeahead lists took most of them. The plugin now says what
worked on that form: the listbox options by id, ArrowDown and Enter when
one is out of reach, and the typeahead's first row.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-11-07 11:47:45 +01:00
Federico EliaandClaude Fable 5.1 294eaeb744 What each application and each scoring cost, and mail looked at twice at most
The owner asked where the money went. Now the app says: every model
call's tokens are priced with the gateway's own list, read once per
start; an application keeps its cost in the store and shows it in its
details, the log says what each application and each scoring cost, and
Settings shows what the session has spent. The shortlist model is not
counted: another endpoint, under a cent a hundred postings. The mail
plugins look for the site's message twice at most and then stop with one
fixed sentence, instead of the spam folder, a new sign-in and guesses.
Six more double-encoded middle dots on the screen are real ones now.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-11-07 09:52:41 +01:00
Federico EliaandClaude Fable 5.1 72c318d4a1 A local copy of a Workday flow, and a trace of every step on request
tests/sites/workday_local.py serves the pages a Workday application
goes through, with the control names read on a real site on 2026-11-07
up to "Create Account", then a "Verify Your Email" page whose six-digit
code goes by mail when AIHAWK_TEST_SMTP is set, and the five steps to
"Submit". It is for trying the Workday plugin and the mail plugins
without an employer's site. AIHAWK_TRACE writes data/trace.log for a
real application as a dry run does.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-11-07 09:28:51 +01:00
Federico EliaandClaude Fable 5.1 1353621739 Dry run: every step the model takes is written to data/dry-run.log
A trial is for looking. With AIHAWK_DRY_RUN set, each call to the browser
and what it answered go to a file beside the proofs, with the account
password hidden, and the model's finish_form report closes the entry.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-11-07 09:24:12 +01:00
Federico EliaandClaude Fable 5.1 f0a41a3a94 The mail as one direct question, and the mailbox opened for the sign-in
After the resume, in the voice of the key question: the email you apply
with, prefilled from the CV. Gmail or Outlook is told by the domain, or
asked in a word. The browser then opens on that mailbox, not on a blank
page, and once the person says they are done the inbox is checked: a
mailbox that sends the browser to its sign-in page is said to be not
signed in, and Enter opens it again. Esc leaves it for later, once. When a
site sends a code and the mailbox is not signed in, the mail plugins say
one fixed sentence, the app asks the same question there and then, and
runs those applications again. Settings has "Your mail"; the hidden key
stays for a job site's own sign-in.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-11-07 08:37:30 +01:00
Federico EliaandClaude Fable 5.1 c64a021245 Rules: no account through another service, on any site; and a dry run
The owner's rule, now general: never "Sign in with", "Register with",
"Continue with" or "Apply with" another service; an account is made by
hand with the candidate's email and the account password, and the
mailbox is only read for a code or a link. AIHAWK_DRY_RUN set in the
environment stops an application once the form is filled and the send
control is found, with a screenshot and nothing sent: for trying a site
or a plugin with a test CV. Documented, tested.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-11-07 08:21:06 +01:00
Federico EliaandClaude Fable 5.1 9089d88825 Answers in the store, and one account password made for the person
The answers move from answers.yaml into an `answers` table of data/jobs.db,
the SQLite file that already holds the jobs: name, value, source, when.
An old answers.yaml is brought in once and renamed. At the first start
AIHawk makes `account_password`, twenty characters of every class, and
keeps it there: the one password for sites that need an account, which
the Workday plugin now names. Settings has "Your answers": see, change,
add or remove one. Docs and SECURITY say where the answers and the
password live, and that the password is in clear.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-11-07 08:10:37 +01:00
Federico EliaandClaude Fable 5.1 f863c0e000 Workday plugin: the account, the email code and the six steps
Read on a real site on 2026-11-07, up to the "Create Account" page: the
/apply page, "Sign in with email", the sign-in and create-account forms by
data-automation-id, the robots-only field never to fill, and the mail from
myworkday.com for a verification code. The steps after sign-in are
Workday's usual form, said as such; docs/sites.md says the plugin has not
yet run on a real form.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-11-07 08:01:59 +01:00
Federico EliaandClaude Fable 5.1 f8d7093c5a Job database: rebuilt every five hours instead of once a day
The biggest source, jobhive, publishes around 15:00 UTC and the new-grad
lists commit every hour; the 04:23 build read them up to a day late. Cron
23 */5 * * *, off the top of the hour; timeout 200 minutes so a hung run
does not take the next slot; six dated releases kept, about thirty hours.
Texts say five hours, and the search log gives the build's hour.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-11-07 03:34:57 +01:00
feder-cr d1e8cb3bee docs/sites.md: a new run each on JazzHR and BambooHR 2026-11-05 20:07:38 +01:00
feder-cr 9f22016d23 docs/sites.md: two runs each on SmartRecruiters and Paylocity reached the send button 2026-11-05 19:54:58 +01:00
feder-cr 7737d2293e A form does not run out of steps repeating what changed nothing
Recorded on a real SmartRecruiters form: about 40 of 150 steps went on PageUp and a screenshot, in a form that scrolls inside its own container. The model is told not to repeat an action that changed nothing, and the plugin says how that form scrolls. On the next runs SmartRecruiters and Paylocity reached the send button; nothing was sent. Also the last mentions of the per-job CV.
2026-11-05 19:34:37 +01:00
feder-cr 09b8b75e90 A send's page is read until it says something, and an error comes out as itself
The MCP client's task groups wrapped every failure during a form, so a refused key was not recognised and the person read 'unhandled errors in a TaskGroup'. A page still showing 'Submitting...' was judged at once and the browser closed. docs/sites.md records the real runs on Workable, SmartRecruiters and Paylocity.
2026-11-05 18:30:49 +01:00
feder-cr d559055ef2 README: say it in two lines, then the commands
In the voice of the other READMEs: what others do, what this one does, and the install. The measurements move to docs/sites.md.
2026-11-05 18:17:48 +01:00
feder-cr 7987df831b README: a short film in place of the banner and the demo, and no site table
One film, in the formats of the other projects' banners. It says one thing: nobody wants to apply for a job, you want the job. The table of sites stays in docs/sites.md.
2026-11-05 18:14:21 +01:00
feder-cr 5358834a9f Every posting is tried: a plugin helps where there is one 2026-11-05 18:00:58 +01:00
feder-cr 9b26b349d7 Lever: a send confirmed through the app as it is now 2026-11-05 17:42:18 +01:00
feder-cr 708c28917f The rest: the plugins name the send control, and the counts' words 2026-11-05 17:08:20 +01:00
feder-cr a7121c7c98 Four more sites reach the send button, and their plugins name it 2026-11-05 17:07:46 +01:00
feder-cr 0a331e936c Your own CV with every form, one gateway and one folder, no warnings about an old database 2026-11-05 16:22:02 +01:00
feder-cr 69d93438cd The repository's new name 2026-11-05 14:48:34 +01:00
feder-cr b23cadb276 One command, everything on the screen, and no more checks on the model 2026-11-05 14:47:50 +01:00
feder-cr a43d74c798 The screen says how each send went and what to do next, and fits 80 columns 2026-11-05 12:42:16 +01:00
feder-cr 290de45e4d Score a job with no stored text on its own page, and show the real screen in the README 2026-11-05 12:19:53 +01:00
feder-cr 8966ec97a3 Plugins: what real forms taught about each site, and a confirmed send on Teamtailor 2026-11-05 11:34:41 +01:00
feder-cr 881badd0ee Sites: a confirmed send on Recruitee, and Lever's own words 2026-11-05 10:12:55 +01:00
feder-cr 5e55568f53 Sites: what one round of labelled test sends showed 2026-11-05 08:45:27 +01:00
feder-cr 60039535b3 Engine defects: stale CVs, judge failures, config types, font wording, settings rewrite 2026-11-05 06:52:10 +01:00
feder-cr 33234f9eb3 Asking for a sign-in code is not a send, and the limits of the send check are stated 2026-11-05 06:33:19 +01:00
feder-cr 9d8c6849d9 Docs: fix README and doc claims, document the config keys 2026-11-05 06:21:47 +01:00
feder-cr a2dc29b491 Data sources: what each source's own licence says, and the credit they ask for 2026-11-05 04:58:15 +01:00
feder-cr 1c054dc6d3 Data sources: list all 24, their licences and how to remove one 2026-11-05 04:56:20 +01:00
feder-cr 90e63afe8f Docs for adopters: data, cost, responsibility, updates
README says what leaves the computer and what it costs, who answers for what is sent, how to update and uninstall, and where the search is changed. It states that the mail plugins' read-only rule is an instruction to the model, not enforced in code.

CONTRIBUTING: CI runs on Ubuntu, Windows and macOS, Python 3.11 and 3.12; ruff check runs the default rules, so the 160 line length is not enforced.
docs/plugins.md: the example is lever.md as it is.
SECURITY.md: the mail plugins' read-only rule is an instruction, not a guarantee.
Phone numbers removed from docs/sites.md and the SmartRecruiters plugin; the plugin gives a placeholder.
LLDB engine credit reworded in NOTICE and profile.py comment; CHANGELOG says what the repository was.
2026-11-05 02:24:27 +01:00
feder-cr 744d710e34 SmartRecruiters: the plugin says how the country, phone, CV and menus work; sites row updated from five real runs 2026-11-05 01:51:02 +01:00
feder-cr f1e764854f Sites: what forms did on real sites, without calling them unfinished 2026-11-05 01:07:58 +01:00
feder-cr 5356361bbb Docs: each site's real status in docs/sites.md, linked from the README
The README site list is replaced by a one-line summary and a link to a
table of status per site, from the real forms trials and the index counts.
Ashby's after-sending section quotes the page a real send showed; the
SmartRecruiters plugin says what stops it today.
2026-11-05 01:07:18 +01:00
feder-cr f7ef2564bb First run: a refused key, quitting, 80 columns, plain errors, BOM files, install and CI
A key that OrcaRouter refuses is forgotten when it came from the key file, and asked for again; a key from the
environment is named by its variable and not forgotten. Ctrl+C leaves at every prompt, and leaving while a prompt
is open no longer raises NoMatches from the heading. Log lines wrap to the whole width, and Quit stays on the
footer at 80 columns. Database and model failures say what happened in plain words, with the detail after them.
Files saved with a byte-order mark are read. The README gives the install commands checked in new venvs, the
index size from the manifest, and how to change the key. CI runs the unit job on Python 3.11 and 3.12 on Ubuntu,
Windows and macOS; gate still needs all of it. The classifiers name the three systems.
2026-11-04 22:52:08 +01:00
feder-cr 9094020f7f Open-source basics: CONTRIBUTING, the plugin guide, SECURITY, issue and pull request templates, CHANGELOG 2026-11-04 22:15:46 +01:00
feder-cr 8cead59b7b AIHawk: find the jobs that fit your CV and apply to them
This repository is AIHawk again. Dots moved to feder-cr/invisible_dots with its history.

Every day a job merges the public job databases into one (pipeline/), one row per posting, and publishes it as a
release. `aihawk scan` queries it in place, so a user scrapes nothing and downloads only what the search reads. `score`
judges jobs against the CV through OrcaRouter, `apply` tailors the CV and fills the form through invisible-playwright-mcp.

The model never sends an application: it fills the form and names the submit control, and AIHawk clicks it after the
person's yes, or by itself with --auto-submit. While the model works, a click on what would send the form and the Enter
key are refused. What the CV and the answers file do not say comes back as a question, never as a guess.
2026-11-04 11:47:28 +01:00
feder-cr 44a51c67f5 guest: install.sh alone starts the Dot's units, after the VM proxy
The units were enabled, so on every boot after the first systemd started them at multi-user.target, before
cloud-init's final stage ran install.sh (cloud-final.service is After=multi-user.target): a Dot's serial log shows
dot-agentd and the engine up about two seconds before install.sh, which is what sets the VM proxy's firewall and
tunnel. A Dot with a proxy could go out directly in that window, and install.sh's own comment said the opposite.

The units are no longer enabled (no [Install]): install.sh starts them on every boot after the proxy, as it already
did on the first, and removes the wants links an earlier runtime made. install.sh is also the first point of the
boot that has the seed's config.json, which the same final stage writes.
2026-10-15 09:23:51 +02:00