trackly-cli 0.8.1 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  [![npm](https://img.shields.io/npm/v/trackly-cli.svg)](https://www.npmjs.com/package/trackly-cli)
2
2
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
3
- [![Node 18+](https://img.shields.io/badge/node-18+-brightgreen.svg)](https://nodejs.org/)
3
+ [![Node 20+](https://img.shields.io/badge/node-20+-brightgreen.svg)](https://nodejs.org/)
4
4
  [![MCP Server](https://img.shields.io/badge/MCP-Server-blue.svg)](https://modelcontextprotocol.io/)
5
5
 
6
6
  # trackly-cli
@@ -31,7 +31,7 @@ trackly login
31
31
  trackly jobs --function product
32
32
  ```
33
33
 
34
- > **Prerequisites:** [Node.js 18+](https://nodejs.org/) (LTS recommended). On macOS with the official `.pkg` installer, global npm installs may require `sudo`.
34
+ > **Prerequisites:** [Node.js 20+](https://nodejs.org/) (LTS recommended). On macOS with the official `.pkg` installer, global npm installs may require `sudo`.
35
35
 
36
36
  ## At a Glance
37
37
 
package/lib/agent.js CHANGED
@@ -8,7 +8,7 @@ const { spawnSync } = require('node:child_process');
8
8
  const { apiRequest, downloadFile, getConfigPaths, hasAuth, maintenanceOutput } = require('./client');
9
9
  const { version: PACKAGE_VERSION } = require('../package.json');
10
10
  const { contractVersion: MCP_CONTRACT_VERSION } = require('../contracts/trackly-apply-tools.json');
11
- const SKILL_VERSION = '4.2.1';
11
+ const SKILL_VERSION = '4.2.2';
12
12
  const CLI_USER_AGENT = `trackly-cli/${PACKAGE_VERSION}`;
13
13
  const MCP_USER_AGENT = `trackly-mcp/${PACKAGE_VERSION}`;
14
14
 
@@ -636,13 +636,13 @@ function registerApplyTools(
636
636
  role: 'user',
637
637
  content: {
638
638
  type: 'text',
639
- text: 'Compatibility and batch gate: require Trackly Apply protocol 3.3.1 or newer and skill 4.2.1 or newer for every new frozen batch. Protocol 3.2 remains valid for the explicit legacy single-run workflow. Recover the active frozen batch before creating another, including for a one-job request. Do not fetch or select from the queue until active-batch recovery proves that no active batch exists; any later generic queue-first instruction applies only to the legacy 3.2 single-run workflow. Claim its lease, keep membership/order fixed, inspect all members before asking one grouped packet of questions, bind each initial or recovered browser surface to the same run and exact backend URL, and discard older-epoch evidence. Require one exact batch resume approval plus immediate local proof before each attachment; ordinary member-version checkpoints do not revoke unchanged resume-content approval. If no form in a truth-certified subset exposes a resume control, certify truth with resumeDependency not_applicable and no resume identity. After durable review-ready checkpoints, truth-certify and hand off the exact complete subset that is currently review-ready without waiting for needs-input members. Keep unresolved members frozen and resumable; when another member becomes ready later, create a fresh certification for the then-current complete review-ready subset. After manual Submit, keep submission request, success-page or explicit user-confirmation, provider receipt, and three-part surface-close proof separate and redacted.',
639
+ text: 'Compatibility and batch gate: require Trackly Apply protocol 3.3.1 or newer and skill 4.2.2 or newer for every new frozen batch. Protocol 3.2 remains valid for the explicit legacy single-run workflow. Recover the active frozen batch before creating another, including for a one-job request. Do not fetch or select from the queue until active-batch recovery proves that no active batch exists; any later generic queue-first instruction applies only to the legacy 3.2 single-run workflow. Claim its lease, keep membership/order fixed, inspect all members before asking one grouped packet of questions, bind each initial or recovered browser surface to the same run and exact backend URL, and discard older-epoch evidence. If a bound start returns a transport failure, a non-access HTTP 5xx response, or an error explicitly marked retryable true, preserve the frozen member and browser state, refetch the same active batch, renew its lease, and retry the same complete binding exactly once. Classify the retry response independently with the same rules: route maintenance_mode or planned_maintenance from either attempt through maintenance recovery, surface controlled-access/request errors marked retryable false and every other HTTP 4xx response unchanged, and only classify a second transport failure, non-access HTTP 5xx response, or explicitly retryable error as backend_run_start_unavailable. Never relabel a permanent retry response as an outage. Preserve the unchanged frozen member as the durable resume point, continue siblings after backend_run_start_unavailable, and never checkpoint the pre-run failure or detach it into an unbound legacy run. Require one exact batch resume approval plus immediate local proof before each attachment; ordinary member-version checkpoints do not revoke unchanged resume-content approval. If no form in a truth-certified subset exposes a resume control, certify truth with resumeDependency not_applicable and no resume identity. After durable review-ready checkpoints, truth-certify and hand off the exact complete subset that is currently review-ready without waiting for needs-input members. Keep unresolved members frozen and resumable; when another member becomes ready later, create a fresh certification for the then-current complete review-ready subset. After manual Submit, keep submission request, success-page or explicit user-confirmation, provider receipt, and three-part surface-close proof separate and redacted. Treat submission reconciliation as a durable commit gate: keep the confirmation tab open until a refetch proves member lifecycle submitted and Trackly job state applied_confirmed.',
640
640
  },
641
641
  }, {
642
642
  role: 'user',
643
643
  content: {
644
644
  type: 'text',
645
- text: 'Fetch the Trackly Apply protocol, profile onboarding, profile, and approved queue. Resolve missing answers with me. Treat required completeness separately from optional reusable coverage and employer-specific contextual questions. Before starting anything, stop on every non-null executionBlocker and every manual_only item. Start only the selected approved queue item, require major(run.protocolVersion) === major(protocol.version), require protocol.compatibleSkillMajor === 4, preserve the stored version for a resumed run, and require its provider, atsCapability, required scenarios, and originPolicy to match the queue preflight. Reclaim semantic browser control, verify the exact job/run/tab binding, hash that value-free binding, and report the same-run browser_ready attestation with committed=true. Before entering private data, require the visible company and role to match the run binding and, when available, the requisition identifier to match the stored job URL. When job_identity_match is required, report a value-free committed scenario_coverage attestation only after that visible identity check passes; never include the company, role, URL, requisition identifier, page text, or any profile value in that observation. On exact-origin fallback, revalidate the frozen company, role, and available requisition identity after every navigation or redirect and before entering any additional private data. Normalize every page, redirect, and data-receiving iframe URL; accept an exact authorized origin or hostname only when host === allowedDomain or host.endsWith("." + allowedDomain), never by substring or page text. When originPolicy.verification is trackly_employer_source_exact_origin, authorize only the exact origin in authorizedOrigins: never promote it to a host suffix and never carry it across a redirect or iframe origin change. For every other vendor-hosted ATS policy, require both originPolicy.tenantRule and originPolicy.verifiedAtsTenant to be non-null or stop before private data entry. Execute the backend-owned originPolicy.tenantRule exactly after every redirect or data-receiving iframe change, including its extraction, exact-host-depth, locale, percent-decoding, normalization, and fail-closed semantics, then require the normalized result to equal originPolicy.verifiedAtsTenant; never invent or reinterpret a strategy token. Obey every capability stop condition. Determine whether the form has a semantically identified Resume or CV attachment control. Only when that specific control exists, prepare the run-bound resume locally with that browser surface and binding hash, show me its exact path, filename, size, SHA-256, run, and expiration, and obtain my explicit confirmation. Treat cover-letter, portfolio, transcript, and other supporting-document controls separately according to the profile and protocol; never upload a resume to them. Immediately before attaching the resume, use the local verifier to validate the signed proof, recompute hash and size, check expiration, and lock the file read-only. Fill the form through semantic controls, verify committed values and every required error, and report a same-run passed or corrected scenario_coverage observation with committed=true for every backend-required scenario except browser_reclaim, which is satisfied only by browser_ready with the binding hash. Before every review_ready outcome, also report value-free committed critical_contact_integrity and manual_submit_boundary evidence; never include contact values, answers, page text, or local paths. If a required or universal review scenario cannot pass, record blocked rather than review_ready. Stop before Submit. If maintenance interrupts the run, retain the run and browser context, wait for the advertised window, refetch protocol, queue, and profile state, and resume the existing agent_browser run. Never start a duplicate run, blindly retry a mutation, enter credentials or verification codes, evade human verification, or click Submit.',
645
+ text: 'Fetch the Trackly Apply protocol, profile onboarding, profile, and approved queue. Resolve missing answers with me. Treat required completeness separately from optional reusable coverage and employer-specific contextual questions. Before starting anything, stop on every non-null executionBlocker and every manual_only item. Start only the selected approved queue item, require major(run.protocolVersion) === major(protocol.version), require protocol.compatibleSkillMajor === 4, preserve the stored version for a resumed run, and require its provider, atsCapability, required scenarios, and originPolicy to match the queue preflight. Reclaim semantic browser control, verify the exact job/run/tab binding, hash that value-free binding, and report the same-run browser_ready attestation with committed=true. Before entering private data, require the visible company and role to match the run binding and, when available, the requisition identifier to match the stored job URL. When job_identity_match is required, report a value-free committed scenario_coverage attestation only after that visible identity check passes; never include the company, role, URL, requisition identifier, page text, or any profile value in that observation. On exact-origin fallback, revalidate the frozen company, role, and available requisition identity after every navigation or redirect and before entering any additional private data. Normalize every page, redirect, and data-receiving iframe URL; accept an exact authorized origin or hostname only when host === allowedDomain or host.endsWith("." + allowedDomain), never by substring or page text. When originPolicy.verification is trackly_employer_source_exact_origin, authorize only the exact origin in authorizedOrigins: never promote it to a host suffix and never carry it across a redirect or iframe origin change. For every other vendor-hosted ATS policy, require both originPolicy.tenantRule and originPolicy.verifiedAtsTenant to be non-null or stop before private data entry. Execute the backend-owned originPolicy.tenantRule exactly after every redirect or data-receiving iframe change, including its extraction, exact-host-depth, locale, percent-decoding, normalization, and fail-closed semantics, then require the normalized result to equal originPolicy.verifiedAtsTenant; never invent or reinterpret a strategy token. Obey every capability stop condition. Determine whether the form has a semantically identified Resume or CV attachment control. Only when that specific control exists, prepare the run-bound resume locally with that browser surface and binding hash, show me its exact path, filename, size, SHA-256, run, and expiration, and obtain my explicit confirmation. Treat cover-letter, portfolio, transcript, and other supporting-document controls separately according to the profile and protocol; never upload a resume to them. Immediately before attaching the resume, use the local verifier to validate the signed proof, recompute hash and size, check expiration, and lock the file read-only. Fill every visible field whose answer is already known, including optional fields, before asking one grouped packet for the remaining unknowns. Use real semantic UI actions and the provider playbook for Greenhouse, Ashby, HiBob, or the active capability; after every select, radio, checkbox, masked input, and upload, verify the committed DOM or accessibility state and that any related required error disappeared. Then sweep all required fields, duplicate contact values, correction banners, and the final consent control. Report a same-run passed or corrected scenario_coverage observation with committed=true for every backend-required scenario except browser_reclaim, which is satisfied only by browser_ready with the binding hash. Before every review_ready outcome, also report value-free committed critical_contact_integrity and manual_submit_boundary evidence; never include contact values, answers, page text, or local paths. If a required or universal review scenario cannot pass, record blocked rather than review_ready. Stop before Submit. If maintenance interrupts the run, retain the run and browser context, wait for the advertised window, refetch protocol, queue, and profile state, and resume the existing agent_browser run. Never start a duplicate run, blindly retry a mutation, enter credentials or verification codes, evade human verification, or click Submit.',
646
646
  },
647
647
  }],
648
648
  }));
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "name": "trackly-cli",
3
- "version": "0.8.1",
3
+ "version": "0.9.0",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "trackly-cli",
9
- "version": "0.8.1",
9
+ "version": "0.9.0",
10
10
  "license": "MIT",
11
11
  "dependencies": {
12
- "@modelcontextprotocol/sdk": "^1.27.1",
12
+ "@modelcontextprotocol/sdk": "1.30.0",
13
13
  "hono": "^4.12.30",
14
14
  "zod": "^3.23.0"
15
15
  },
@@ -17,28 +17,28 @@
17
17
  "trackly": "bin/trackly"
18
18
  },
19
19
  "engines": {
20
- "node": ">=18"
20
+ "node": ">=20"
21
21
  }
22
22
  },
23
23
  "node_modules/@hono/node-server": {
24
- "version": "1.19.14",
25
- "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.14.tgz",
26
- "integrity": "sha512-GwtvgtXxnWsucXvbQXkRgqksiH2Qed37H9xHZocE5sA3N8O8O8/8FA3uclQXxXVzc9XBZuEOMK7+r02FmSpHtw==",
24
+ "version": "2.0.12",
25
+ "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-2.0.12.tgz",
26
+ "integrity": "sha512-eWpQYr67tqJLeaSUl0Q+TquuYfUdTibpOJlUMV2FfUP7+KqCC5TufnwnlXL6mobZBJbGAYRd7ZvEBDCbLInjhg==",
27
27
  "license": "MIT",
28
28
  "engines": {
29
- "node": ">=18.14.1"
29
+ "node": ">=20"
30
30
  },
31
31
  "peerDependencies": {
32
32
  "hono": "^4"
33
33
  }
34
34
  },
35
35
  "node_modules/@modelcontextprotocol/sdk": {
36
- "version": "1.29.0",
37
- "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.29.0.tgz",
38
- "integrity": "sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ==",
36
+ "version": "1.30.0",
37
+ "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.30.0.tgz",
38
+ "integrity": "sha512-xKd8OIzlqNzcqcNumGAa6g+PW2kjD5vrpcKOnfldAUPP3j7lnqMPwlTXQm8gF+UwH72z0lqaRbjr9hqGz0eITA==",
39
39
  "license": "MIT",
40
40
  "dependencies": {
41
- "@hono/node-server": "^1.19.9",
41
+ "@hono/node-server": "^1.19.9 || ^2.0.5",
42
42
  "ajv": "^8.17.1",
43
43
  "ajv-formats": "^3.0.1",
44
44
  "content-type": "^1.0.5",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "trackly-cli",
3
- "version": "0.8.1",
3
+ "version": "0.9.0",
4
4
  "mcpName": "io.github.trackly-app/trackly",
5
5
  "description": "AI job search CLI + hosted MCP server with OAuth. 128K+ jobs, 1,900+ companies, 40+ ATS. Works with Claude, ChatGPT, Cursor, Windsurf, Codex via hosted streamable-http or local stdio.",
6
6
  "bin": {
@@ -62,11 +62,14 @@
62
62
  "url": "https://github.com/trackly-app/trackly-cli/issues"
63
63
  },
64
64
  "dependencies": {
65
- "@modelcontextprotocol/sdk": "^1.27.1",
65
+ "@modelcontextprotocol/sdk": "1.30.0",
66
66
  "hono": "^4.12.30",
67
67
  "zod": "^3.23.0"
68
68
  },
69
69
  "engines": {
70
- "node": ">=18"
70
+ "node": ">=20"
71
+ },
72
+ "overrides": {
73
+ "@hono/node-server": "2.0.12"
71
74
  }
72
75
  }
@@ -70,21 +70,9 @@ function validatePolicy(policy) {
70
70
  assert.ok(Array.isArray(policy.exceptions));
71
71
  assert.equal(
72
72
  policy.exceptions.length,
73
- 1,
74
- 'Exactly one temporary npm audit exception is permitted',
73
+ 0,
74
+ 'No npm audit exceptions are permitted',
75
75
  );
76
- const exception = policy.exceptions[0];
77
- assert.equal(exception.advisory, 'GHSA-frvp-7c67-39w9');
78
- assert.equal(exception.package, '@hono/node-server');
79
- assert.equal(exception.dependencyOf, '@modelcontextprotocol/sdk');
80
- assert.equal(exception.severity, 'moderate');
81
- assert.equal(exception.vulnerableRange, '<2.0.5');
82
- assert.equal(
83
- exception.upstream,
84
- 'https://github.com/modelcontextprotocol/typescript-sdk/issues/2531',
85
- );
86
- assert.ok(exception.expectedAuditShape);
87
- return exception;
88
76
  }
89
77
 
90
78
  function validateAuditReport(report, policy) {
@@ -106,7 +94,7 @@ function validateAuditReport(report, policy) {
106
94
  );
107
95
  }
108
96
  assert.equal(report?.auditReportVersion, 2, 'Unsupported npm audit report version');
109
- const exception = validatePolicy(policy);
97
+ validatePolicy(policy);
110
98
 
111
99
  if (cleanAuditResult(report)) {
112
100
  return {
@@ -114,22 +102,9 @@ function validateAuditReport(report, policy) {
114
102
  propagatedVulnerabilities: [],
115
103
  };
116
104
  }
117
-
118
- try {
119
- assert.deepEqual(normalizedAuditShape(report), exception.expectedAuditShape);
120
- } catch (error) {
121
- const wrapped = new Error(
122
- 'npm audit report does not match the temporary exception. '
123
- + 'Review every new advisory, severity, affected range, and dependency-path change.',
124
- );
125
- wrapped.cause = error;
126
- throw wrapped;
127
- }
128
-
129
- return {
130
- allowedAdvisories: [exception.advisory],
131
- propagatedVulnerabilities: Object.keys(report.vulnerabilities).sort(),
132
- };
105
+ throw new Error(
106
+ 'npm audit reported a vulnerability; Trackly CLI permits no audit exceptions.',
107
+ );
133
108
  }
134
109
 
135
110
  function loadPolicy(policyPath = DEFAULT_POLICY_PATH) {
@@ -1,70 +1,4 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "exceptions": [
4
- {
5
- "advisory": "GHSA-frvp-7c67-39w9",
6
- "source": 1124006,
7
- "package": "@hono/node-server",
8
- "dependencyOf": "@modelcontextprotocol/sdk",
9
- "severity": "moderate",
10
- "vulnerableRange": "<2.0.5",
11
- "upstream": "https://github.com/modelcontextprotocol/typescript-sdk/issues/2531",
12
- "rationale": "Trackly starts the MCP SDK only through its stdio transport. It does not import @hono/node-server, Streamable HTTP, or serveStatic, so the Windows static-file path traversal is unreachable. Automated stdio isolation and packed-consumer tests enforce that boundary until the SDK removes or upgrades this dependency.",
13
- "expectedAuditShape": {
14
- "vulnerabilityCounts": {
15
- "info": 0,
16
- "low": 0,
17
- "moderate": 2,
18
- "high": 0,
19
- "critical": 0,
20
- "total": 2
21
- },
22
- "vulnerabilities": {
23
- "@hono/node-server": {
24
- "name": "@hono/node-server",
25
- "severity": "moderate",
26
- "isDirect": false,
27
- "via": [
28
- {
29
- "source": 1124006,
30
- "name": "@hono/node-server",
31
- "dependency": "@hono/node-server",
32
- "title": "Node.js Adapter for Hono: Path traversal in `serve-static` on Windows via encoded backslash (`%5C`)",
33
- "url": "https://github.com/advisories/GHSA-frvp-7c67-39w9",
34
- "severity": "moderate",
35
- "cwe": [
36
- "CWE-22"
37
- ],
38
- "cvss": {
39
- "score": 5.9,
40
- "vectorString": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:N"
41
- },
42
- "range": "<2.0.5"
43
- }
44
- ],
45
- "effects": [
46
- "@modelcontextprotocol/sdk"
47
- ],
48
- "range": "<2.0.5",
49
- "nodes": [
50
- "node_modules/@hono/node-server"
51
- ]
52
- },
53
- "@modelcontextprotocol/sdk": {
54
- "name": "@modelcontextprotocol/sdk",
55
- "severity": "moderate",
56
- "isDirect": true,
57
- "via": [
58
- "@hono/node-server"
59
- ],
60
- "effects": [],
61
- "range": ">=1.25.0",
62
- "nodes": [
63
- "node_modules/@modelcontextprotocol/sdk"
64
- ]
65
- }
66
- }
67
- }
68
- }
69
- ]
3
+ "exceptions": []
70
4
  }
package/server.json CHANGED
@@ -17,12 +17,12 @@
17
17
  ]
18
18
  }
19
19
  ],
20
- "version": "0.8.1",
20
+ "version": "0.9.0",
21
21
  "packages": [
22
22
  {
23
23
  "registryType": "npm",
24
24
  "identifier": "trackly-cli",
25
- "version": "0.8.1",
25
+ "version": "0.9.0",
26
26
  "runtimeHint": "npx",
27
27
  "runtimeArguments": [
28
28
  {
@@ -25,7 +25,7 @@ Before selecting or resuming work, read
25
25
  references define the server-frozen batch, grouped-question, tab-reconciliation,
26
26
  and missing-surface recovery contract; they are mandatory even for a one-job batch.
27
27
 
28
- 1. Call `trackly_get_apply_protocol`. Skill 4.2.1 requires protocol major 3 and protocol 3.3.1 or newer for frozen-batch execution; protocol 3.2 remains valid for the explicit legacy single-run workflow. Require `compatibleSkillMajor: 4` and `compatibleSkillMinimumVersion` no newer than this installed skill. Reject an older or incompatible version and report that the backend must finish updating or `trackly agent setup` must update the skill. Protocol 3.2 added exact-origin trust for jobs Trackly ingested directly from employer careers sources; do not recreate the retired ownership-timestamp gate in the client.
28
+ 1. Call `trackly_get_apply_protocol`. Skill 4.2.2 requires protocol major 3 and protocol 3.3.1 or newer for frozen-batch execution; protocol 3.2 remains valid for the explicit legacy single-run workflow. Require `compatibleSkillMajor: 4` and `compatibleSkillMinimumVersion` no newer than this installed skill. Reject an older or incompatible version and report that the backend must finish updating or `trackly agent setup` must update the skill. Protocol 3.2 added exact-origin trust for jobs Trackly ingested directly from employer careers sources; do not recreate the retired ownership-timestamp gate in the client.
29
29
  2. Call `trackly_get_profile_onboarding` or fetch both the profile schema and application profile. Treat `completeness.percent` as required onboarding readiness only. Use `coverage.missingReusableKeys` to explain reusable optional gaps, while `coverage.contextualKeys` are intentionally asked only on the relevant employer form. Do not claim that 100% required completeness answers every possible application question.
30
30
  3. Save answers with `trackly_update_application_profile`:
31
31
  - Use `answered`, `intentionally_blank`, `declined`, or `unknown` exactly.
@@ -47,7 +47,8 @@ and missing-surface recovery contract; they are mandatory even for a one-job bat
47
47
  - If the user requests the next `N` jobs, freeze up to `N` available saved jobs before starting any run. Preserve inactive, insecure, or protocol-declared manual-only members with their static exclusion reason; do not replenish, replace, rescore, or expand the batch. If fewer than `N` saved jobs exist, report the frozen count rather than searching for substitutes.
48
48
  - For each fixed batch member, preserve an explicit job ID -> application run ID -> browser tab mapping. Complete the full start -> conditional resume preparation/confirmation/verification when an upload control exists -> form completion -> `review_ready` lifecycle for every member. A review-ready run does not block the next member. Never submit any of them.
49
49
  6. Call `trackly_start_apply_run` for the selected job or current fixed batch member with the complete `batchId`, `memberId`, `expectedMemberVersion`, `expectedInspectionEpoch`, and `leaseToken` returned by Trackly. This call is also the sanctioned idempotent lookup when a recovered member has no `runId`; call it with the exact recovered binding and Trackly will reuse the bound active run. Never create a replacement run because browser control was interrupted.
50
- - Require `run.protocolVersion` to be 3.1.0 or newer and to share protocol major 3 with the fetched protocol. A new batch member requires 3.3.1 or newer; an explicit 3.2 single run may start or finish through its legacy path. Never continue a pre-evidence 3.0.x run under skill 4.2.1. Preserve that run instead of starting a replacement, record it `blocked` with a value-free protocol-upgrade reason when possible, and tell the user the saved job can be retried only after the stale run is cleared through Trackly's supported lifecycle. Stop and refetch the protocol and active batch if support level, execution mode, provider, required scenarios, authorized origin policy, member version, or inspection epoch changes after run creation.
50
+ - If a bound start returns a transport failure, a non-access HTTP 5xx response, or an error explicitly marked `retryable: true`, preserve the frozen member and browser state. Refetch the active batch, renew its lease, and retry the same complete binding once. Classify the retry response independently with these same rules: route canonical `maintenance_mode` or legacy `planned_maintenance` directly through **Resume after maintenance**; surface controlled-access/request errors marked `retryable: false` and every other HTTP 4xx response unchanged; only a second transport failure, non-access HTTP 5xx response, or explicitly retryable error becomes `backend_run_start_unavailable`. Route maintenance on either attempt without consuming or relabeling the retry, and never relabel a permanent retry response as an outage. For `backend_run_start_unavailable`, continue other members and report a Trackly control-plane failure. Do not call `trackly_checkpoint_apply_batch` for this condition because start failure has not produced the required run ID. The unchanged frozen member is the durable resume point. Never switch that frozen member to an unbound legacy run or blame the employer form.
51
+ - Require `run.protocolVersion` to be 3.1.0 or newer and to share protocol major 3 with the fetched protocol. A new batch member requires 3.3.1 or newer; an explicit 3.2 single run may start or finish through its legacy path. Never continue a pre-evidence 3.0.x run under skill 4.2.2. Preserve that run instead of starting a replacement, record it `blocked` with a value-free protocol-upgrade reason when possible, and tell the user the saved job can be retried only after the stale run is cleared through Trackly's supported lifecycle. Stop and refetch the protocol and active batch if support level, execution mode, provider, required scenarios, authorized origin policy, member version, or inspection epoch changes after run creation.
51
52
  - In guided mode, inspect the page before preparing any resume bytes. Confirm the employer, role, HTTPS origin, reachable review path, semantic controls, whether an attachment control exists, and absence of a credential, verification, CAPTCHA, or submit-only wall. A missing file input is not itself a blocker; skip the resume path when the application has no attachment control. Any other failed precondition is an execution blocker, not permission to improvise.
52
53
  7. Pass the browser readiness gate before preparing resume bytes:
53
54
  - Use semantic browser control through Codex in-app browser controls, Chrome MCP/extension browser control, or Claude in Chrome.
@@ -91,7 +92,7 @@ Follow this order:
91
92
  4. Fill typed fields from the resolved Trackly profile. Clear parser-filled data when the canonical state is intentionally blank.
92
93
  5. Use real UI clicks for React/native selects, radios, and checkboxes. Resolve boolean values by their exact semantic label (`true` to Yes, `false` to No), never by option order, index, proximity, or a stale prior selection. After every selection, compare the committed value with the canonical Trackly value. If the field is required or had a validation error before selection, verify that the required-field error disappeared. An optional control with no validation error passes when its committed value is correct. Treat any value mismatch or applicable stale error as a failed field and correct it before continuing.
93
94
  6. Recheck email and phone through both browser DOM state and macOS accessibility state. Require exact values and reject duplicate/concatenated values.
94
- 7. Complete known optional fields, education, links, relocation, and source answers. Do not silently omit canonical answers.
95
+ 7. Complete every visible field whose canonical answer is known, including optional fields, previous-employer/title history, education rows and dates, links, relocation, communication preferences, and opportunity source. Before review, compare the live field inventory with the resolved Trackly profile and prove that no known visible answer was silently omitted. Group only genuinely unknown contextual fields into the question packet.
95
96
  - Treat partial dates as unknown at the missing precision. If Trackly has only a year but the ATS requires a month, ask once and sync the complete date before selecting either control. Never accept an ATS-selected current/default month or infer an education month.
96
97
  8. Use the canonical `consent.background_check_if_advanced` field only when the form explicitly asks for consent to a background check if the candidate advances. If it is unknown, ask before selecting it and save the answer at the user's chosen scope. Never infer it from privacy, demographic, recruiting-data, general application, criminal-record, or professional-reference consent. Treat the latter two as separate unknown consent questions unless the current profile schema supplies their own canonical fields.
97
98
  9. For a free-text application response, read [references/application-writing.md](references/application-writing.md). Calibrate from the user's Trackly writing fields, use only supported profile and role facts, and run the built-in voice and anti-slop gate before entering the response. Do not require a separate writing or humanizer skill.
@@ -117,6 +118,7 @@ After the user submits manually:
117
118
 
118
119
  - If a success page is visible, reclaim the current batch lease if needed, first record current-epoch `confirmation_detected` evidence with source `success_page` through `trackly_record_apply_submission_evidence`, then record `submitted` with the exact current batch/member/epoch/lease binding and confirmation `success_page`.
119
120
  - If the user explicitly confirms submission, reclaim the current batch lease if needed, first record current-epoch `confirmation_detected` evidence with source `user_confirmation` through `trackly_record_apply_submission_evidence`, then record `submitted` with the exact current batch/member/epoch/lease binding and confirmation `user_confirmation`.
121
+ - Treat the outcome response as a durable commit gate. Refetch each submitted member and require both member lifecycle `submitted` and Trackly job state `applied_confirmed`. On conflict, preserve the success tab, refresh once, and replay only the documented idempotent evidence/outcome sequence. If reconciliation still fails, report a P0 reconciliation defect and leave the tab open; never claim Trackly moved the job.
120
122
  - A provider receipt is optional orthogonal evidence. Record it as `provider_receipt_detected`; never use `provider_receipt` as the outcome confirmation or as a substitute for a success page or explicit user confirmation.
121
123
  - If neither exists, do not move the job to applied.
122
124
  - Treat a contradictory ATS response such as “already applied” as provisional until the exact requisition URL settles. Do not click Submit again. Preserve the page, confirm the job/requisition identifier is unchanged, and re-read the final route state after the UI and network activity settle. A later explicit success state on that same requisition overrides the provisional error and must be recorded as `submitted`; otherwise record the run as blocked without marking the job applied.
@@ -4,6 +4,8 @@
4
4
 
5
5
  - Accept employer-hosted Greenhouse forms and `job-boards.greenhouse.io` / `boards.greenhouse.io` application hosts after matching the employer and role.
6
6
  - Expect resume parsing, React selects, correction banners, demographic sections, and final consent checkboxes.
7
+ - Use the visible file chooser or its semantic file input; never click a nearby decorative upload region by coordinates.
8
+ - For custom selects and checkbox groups, verify the selected label or checked element in the live DOM after the real click. A displayed answer with a remaining required error is uncommitted.
7
9
  - Treat red required-field text as authoritative even when a value is visible.
8
10
  - A Greenhouse “Thank you for applying” page is a valid submission signal.
9
11
 
@@ -11,6 +13,9 @@
11
13
 
12
14
  - Accept `jobs.ashbyhq.com` only after matching the employer and role.
13
15
  - Expect rerenders, masked contact inputs, and controls whose visible text can differ from committed state.
16
+ - Resolve binary button groups by the live selected/active state and semantic Yes/No label, never by button order or styling alone.
17
+ - Treat education as repeatable structured rows. After activating `+ Add Education`, require the row count to increase before filling the new row. If Playwright reports a click but the submit-typed control does not add a row, retry with the visible semantic DOM control; stop if the row remains absent.
18
+ - Select localized school suggestions by the exact intended institution and then verify the committed row value.
14
19
  - Best effort: stop when browser and accessibility state disagree.
15
20
  - An explicit application-success banner/page is a valid submission signal.
16
21
 
@@ -37,6 +42,7 @@ This group includes SmartRecruiters, Workable, BambooHR, Recruitee, Jobvite, Tea
37
42
  - Expect a mixture of native controls, custom widgets, employer subdomains, embedded frames, shared profile data, and optional resume parsing.
38
43
  - Match the live HTTPS origin, employer, role, and requisition before entering private data.
39
44
  - Use the same committed-state and required-error gate as Greenhouse. Familiar-looking controls do not lower the evidence requirement.
45
+ - On HiBob, treat document upload as a two-step commit when the UI requires choosing the file and then confirming the upload. Verify the final filename/attachment state; a selected local file alone is not an attached resume.
40
46
 
41
47
  ## Unknown employer-hosted form
42
48
 
@@ -38,10 +38,22 @@ For every protocol 3.3.1 request:
38
38
 
39
39
  Do not recreate a batch after maintenance. Refetch the existing batch, reclaim
40
40
  its lease with the latest revision, and resume its existing run bindings.
41
+ When a bound run start returns a transport failure, a non-access HTTP 5xx
42
+ response, or an error explicitly marked `retryable: true`, refetch and renew,
43
+ then retry the same complete binding once. Classify the retry response
44
+ independently with the same rules. Route `maintenance_mode` or the legacy
45
+ `planned_maintenance` alias from either attempt through maintenance recovery.
46
+ Surface controlled-access/request errors marked `retryable: false` and every
47
+ other HTTP 4xx response unchanged, including when returned by the retry. Only a
48
+ second transport failure, non-access HTTP 5xx response, or explicitly
49
+ retryable error becomes `backend_run_start_unavailable` while siblings
50
+ continue. Do not checkpoint this condition: no run ID exists yet, and the
51
+ checkpoint contract requires one. The unchanged frozen member is the durable
52
+ resume point. Never detach that member into a legacy single run.
41
53
 
42
54
  ### Request budget
43
55
 
44
- For a new 20-member batch, keep recovery, planning, initial binding, evidence,
56
+ For a failure-free new 20-member batch, keep planning, initial binding, evidence,
45
57
  two bulk checkpoints, resume approval, truth certification, outcomes, and the
46
58
  final batch refresh within 52 non-resume MCP/HTTP requests: one active-batch
47
59
  lookup, one create, one page, one claim, 20 run starts, 20 surface bindings, one
@@ -51,7 +63,12 @@ resume approval, one truth certification, one
51
63
  `trackly_record_application_outcomes` call, and one final refresh. An existing
52
64
  active batch omits the create call. Do not replace bulk observations,
53
65
  checkpoints, or outcomes with per-member requests. Resume download and exact
54
- local verification are excluded from this count.
66
+ local verification are excluded from this count. Each member that encounters
67
+ the one permitted retryable bound-start failure receives exactly three
68
+ additional recovery calls: refetch the active batch, renew its lease, and retry
69
+ the same binding. Therefore the bounded contingency budget is `52 + (3 * R)`,
70
+ where `R` is the number of affected members and cannot exceed 20. Maintenance
71
+ recovery is paced by the advertised window and is tracked separately.
55
72
 
56
73
  ## Ownership and replay safety
57
74
 
@@ -182,6 +199,10 @@ Never submit a member. After manual submission, mark Applied only from an
182
199
  observable success page or explicit user confirmation. Record the submit
183
200
  request, success page or `user_confirmation`, and any provider receipt with
184
201
  `trackly_record_apply_submission_evidence`; store only the redacted fingerprint
185
- and typed source. Then reconcile tab closure separately using the
186
- browser-lifecycle gate. Submission, receipt, and closure never substitute for
187
- one another.
202
+ and typed source. The outcome call is not complete until a fresh batch read
203
+ shows member lifecycle `submitted` and tracker state `applied_confirmed`.
204
+ Preserve the success tab during one documented idempotent conflict recovery;
205
+ if reconciliation remains unsuccessful, report the control-plane defect and
206
+ do not claim completion. Only then reconcile tab closure separately using the
207
+ browser-lifecycle gate. Submission, durable outcome, receipt, and closure never
208
+ substitute for one another.
@@ -43,6 +43,14 @@ Never make a closure claim from one inventory surface.
43
43
 
44
44
  ## Close proof
45
45
 
46
+ For a submitted application, do not begin tab closure until Trackly has
47
+ durably reconciled both the batch member to `submitted` and the saved job to
48
+ `applied_confirmed`. A success page or explicit user confirmation authorizes
49
+ the outcome write; it does not prove that the write committed. On a conflict,
50
+ keep the tab visible while the agent refreshes and performs the one documented
51
+ idempotent replay. If reconciliation still fails, leave the tab open and
52
+ report a control-plane defect.
53
+
46
54
  `closed_verified` requires all of the following for the current inspection
47
55
  epoch:
48
56