Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .env.production.example
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Production deployments intentionally have no working secret defaults.
# Copy this file to .env.production, replace every CHANGE-ME value, keep the
# result out of version control, and pin a released OSApplyTrack version.
OSAPPLYTRACK_VERSION=1.68.0
OSAPPLYTRACK_VERSION=1.68.1
POSTGRES_USER=applytrack
POSTGRES_PASSWORD=CHANGE-ME-use-a-long-random-password
POSTGRES_DB=applytrack
Expand Down
1 change: 1 addition & 0 deletions BACKLOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ review items and unsupported forms still need human attention.
- [x] [#439 — Agent: a generic placeholder ("Enter") is taken for a field's label — fixed in 1.67.0](https://github.com/CryptoJones/OSApplyTrack/issues/439)
- [ ] [#440 — Agent: Manatal (careers-page.com) Radix selects and checkbox are not driven](https://github.com/CryptoJones/OSApplyTrack/issues/440) (Yo AI Labs: after #436 the résumé attaches and the hidden questions are found, but the Nationalities / years / salary Radix selects and the terms checkbox are custom widgets neither discovered nor driven)
- [x] [#443 — Agent: drive Workday's application wizard end to end](https://github.com/CryptoJones/OSApplyTrack/issues/443) (`BrowserCanSubmit` refused Workday, so #277's account recipe never got a run to fire in; NVIDIA JR2023533 was walked by hand on 2026-10-12. **1.68.0:** Workday is driven. The application starts at the manual flow (`…/apply/applyManually`, never the résumé autofill, whose parse explodes an "earlier roles" list into rows with no company); the way in presses Apply Manually and "Sign in with email" — the password box sits behind that choice — and the sign-in types the saved account key by key (a fill Workday sometimes never hears), presses the click_filter overlay beside the aria-hidden Sign In, waits for a complaint rather than a spinner, and loads the address again afterwards, because a sign-in from inside the flow draws "Something went wrong — please refresh". A step is read only once its controls have held still (the address and phone arrive a second after the names). Discovery and the sweep now see Workday's drop-down buttons (`aria-haspopup=listbox`, named by their label, their group, or — for a named button — the text above them), its search boxes (chips beside the input are the answer, committed with Enter), and radio groups required on their wrapper; the page header's own menus are never questions. The walk reads each step's menus so a question handed back carries its choices, ticks the terms box, attaches the résumé to the file input the packet never saw, declines a demographic drop-down Workday requires an answer to (never a guess), leaves a question the draft already answers alone, waits out the Save and Continue Workday disables while it autosaves, and hands back only starred fields when Next stays disabled. My Experience's Work Experience and Education rows are filled from the structured résumé (three jobs, every school: title, company, month/year spinbuttons, "I currently work here", description; school, Degree menu, Field of Study, years) and rows a draft already holds are completed, not duplicated. A long menu (Country) is typed at so the listbox jumps to the entry. The drafter's phone rule no longer answers "Phone Device Type" with the number. Verified live on nvidia.wd5 as a dry run: three rounds — page 2's rows and page 3's authorization pair handed back and answered — then every step to Review.)
- [x] [#446 — Agent role cannot write board_accounts: every judging pass has died since 2026-10-10](https://github.com/CryptoJones/OSApplyTrack/issues/446) (`agent_role.sql` granted the agent INSERT/UPDATE on its own tables but not on `board_accounts`, which it writes when it takes a Sign in now, keeps a renewed session, or saves an account #277 just made; `RenewSessionsAsync` runs before the judging pass, so once greenhouse.io's session came due the pass died on every tick and the Ready reconciler, the verdicts and portal renewal all stopped — the last `agent_events` row was 2026-10-09. **1.68.1:** the grant is re-applied on boot, and `DbRoleTests` proves the three writes and that a delete is still refused.)
- [x] [#417 — Agent: a résumé dropzone whose wrapping label is only "Click or drag & drop PDF" is never recognised as the résumé (Ethos) — fixed in 1.67.0: dropzone text never names a file field; its row's title does](https://github.com/CryptoJones/OSApplyTrack/issues/417)
- [x] [#418 — Agent: an application form folded behind "Easy Apply" is never revealed (N-iX) — fixed in 1.67.0: "Easy/Quick/One-click Apply" open the form (N-iX itself then ends at a reCAPTCHA v2 handoff)](https://github.com/CryptoJones/OSApplyTrack/issues/418)
- [x] [#419 — Agent: a footer newsletter signup (mc4wp) is discovered as an application question (Tecbrains) — fixed in 1.67.0: newsletter forms are page widgets](https://github.com/CryptoJones/OSApplyTrack/issues/419)
Expand Down
27 changes: 27 additions & 0 deletions api/ApplyTrack.Api.Tests/DbRoleTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,33 @@ public async Task The_agent_role_updates_application_fields_but_never_tenant_or_
await AssertDeniedAsync(agent, "SELECT * FROM app_notes");
}

/// <summary>
/// The agent writes board_accounts in three places — taking a Sign in now, keeping the
/// session it renewed, saving an account it just created (#277) — and the grant missed
/// all three, so every pass on pluto died in RenewSessionsAsync for three days (#446).
/// </summary>
[Fact]
public async Task The_agent_role_writes_board_accounts_but_never_deletes_one()
{
UpgradeWith("agent-pw", "poller-pw");
await using var owner = new NpgsqlConnection(pg.ConnectionString);
await owner.OpenAsync();
long t = await TestAuth.EnsureUserAsync(owner, TestAuth.UniqueEmail());
await owner.ExecuteAsync(
"INSERT INTO board_accounts (tenant_id, host, username, password_ciphertext, renew_requested_at) VALUES (@t, 'greenhouse.io', 'ada', 'sealed-pw', now())",
new { t });

await using var agent = await OpenAsAsync(Migrator.AgentRole, "agent-pw");
Assert.Equal(1, await agent.ExecuteAsync(
"UPDATE board_accounts SET renew_requested_at = NULL WHERE tenant_id = @t AND host = 'greenhouse.io' AND renew_requested_at IS NOT NULL", new { t }));
Assert.Equal(1, await agent.ExecuteAsync(
"UPDATE board_accounts SET session_ciphertext = 's', session_expires_at = now(), updated_at = now() WHERE tenant_id = @t AND host = 'greenhouse.io'", new { t }));
Assert.Equal(1, await agent.ExecuteAsync(
"INSERT INTO board_accounts (tenant_id, host, username, password_ciphertext, updated_at) VALUES (@t, 'acme.wd5.myworkdayjobs.com', 'ada', 'sealed-pw', now()) "
+ "ON CONFLICT (tenant_id, host) DO UPDATE SET username = EXCLUDED.username, password_ciphertext = EXCLUDED.password_ciphertext, updated_at = now()", new { t }));
await AssertDeniedAsync(agent, "DELETE FROM board_accounts WHERE tenant_id = @t", new { t });
}

[Fact]
public async Task A_changed_password_is_applied_on_the_next_boot_and_a_blank_one_is_skipped()
{
Expand Down
2 changes: 1 addition & 1 deletion api/ApplyTrack.Api/ApplyTrack.Api.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<RootNamespace>ApplyTrack.Api</RootNamespace>
<Version>1.68.0</Version>
<Version>1.68.1</Version>
<Authors>Aaron K. Clark</Authors>
<Copyright>Copyright 2026 Aaron K. Clark</Copyright>
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
Expand Down
4 changes: 4 additions & 0 deletions api/ApplyTrack.Api/Migrations/Always/agent_role.sql
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ BEGIN
REVOKE ALL ON contacts, application_contacts, app_notes FROM applytrack_agent;
GRANT INSERT, UPDATE ON agent_events, agent_packets, agent_evidence,
cover_letters, submit_requests, agent_workers, answer_bank TO applytrack_agent;
-- The agent takes a Sign in now (renew_requested_at), keeps the session it renews
-- and saves an account it has just created (#277) — no DELETE: an account is
-- removed only by the person, from Settings (#446).
GRANT INSERT, UPDATE ON board_accounts TO applytrack_agent;
-- Status flips (ready / applied) go through ApplicationRepo's structured update,
-- which writes exactly these columns — never tenant_id or name, so a compromised
-- agent cannot move a row into another tenant (#345). The REVOKE drops the
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "hatchling.build"

[project]
name = "applytrack-poller"
version = "1.68.0"
version = "1.68.1"
description = "Discovery poller for OSApplyTrack — fetches and scores remote job leads into shared Postgres."
requires-python = ">=3.10"
license = { text = "Apache-2.0" }
Expand Down
2 changes: 1 addition & 1 deletion src/applytrack/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
# Copyright 2026 Aaron K. Clark
"""applytrack — a local, markdown-backed job-application tracker."""

__version__ = "1.68.0"
__version__ = "1.68.1"
2 changes: 1 addition & 1 deletion uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading