trackly-cli 0.9.2 → 0.9.3

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/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.4';
11
+ const SKILL_VERSION = '4.2.5';
12
12
  const CLI_USER_AGENT = `trackly-cli/${PACKAGE_VERSION}`;
13
13
  const MCP_USER_AGENT = `trackly-mcp/${PACKAGE_VERSION}`;
14
14
 
@@ -636,7 +636,7 @@ 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.4 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 the exact complete subset, bulk-record literal outcome=review_ready for every member, and verify every recorded run returns awaiting_manual_submit before handoff 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, then record literal outcome=submitted. Only when both the fetched protocol and the stored run protocol are 3.3.2 or newer may a current-epoch exact-requisition success page reconcile a running, inspecting, needs_input, review_ready, or request-only submitted projection without fabricated retroactive review evidence. 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. Treat browser-session finalization as destructive cleanup. Before form mutation, require an end-to-end usable preservation path: the documented session finalizer plus complete current controller-owned and user-owned inventory access for its keep list, or a documented per-tab durable-handoff primitive with an exact verified persistence receipt for every target tab; fail browser readiness if neither path is complete. Immediately before finalization, reconcile the complete controller-owned and user-owned inventory union. Use the documented session-level finalizer exactly once as the final browser action with an explicit { tab, status: "handoff" } keep entry for every currently live mapped application tab, including frozen-batch and legacy single-run tabs, or invoke the documented per-tab durable handoff for every live tab and verify each persistence receipt. Never use an omitted, empty, partial, guessed, or stale keep list or an undocumented substitute. If finalization is ambiguous, do not call another browser tool in that turn and do not rerun it; reconcile inventories on the next turn. A user-confirmed direct tab closure may leave the keep list only after the complete inventory union proves the tab is absent; preserve an incomplete member for missing-tab recovery. Before claiming a form is open or visible, reconcile complete controller-owned and user-owned inventories, then use the documented adapter presentation action and verify its visible state or exact user-visible handoff receipt; inventory membership alone is never visibility proof. If that proof is unavailable, preserve the tab, use the visibility-unverified handoff, and do not tell the user to submit until the exact review tab is reclaimed and visibly proven. Keep employment status, intentionally blank current company, and most recent employer distinct; an intentionally blank current company never implies employment status and never erases prior employment. Enter employment and education in reverse chronological order, and use the canonical committed English name or verified catalog option for each school.',
639
+ text: 'Compatibility and batch gate: require Trackly Apply protocol 3.3.1 or newer and skill 4.2.5 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. Before mutating a form, inspect only prior-submission evidence the user supplied or evidence already visible on the bound application surface; never search a mailbox or unrelated private-data source. If the member lacks a run/browser binding, use sanctioned idempotent start and exact employer-page binding without entering private data before recording receipt evidence. Treat same-company/different-role evidence as negative for the current member. A receipt proves identity only and never replaces success-page or explicit user-confirmation authority. Schedule accessible members before known credential-gated members without changing frozen membership or order. 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 the exact complete subset, bulk-record literal outcome=review_ready for every member, and verify every recorded run returns awaiting_manual_submit before handoff 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, then record literal outcome=submitted. Only when both the fetched protocol and the stored run protocol are 3.3.2 or newer may current-epoch exact-requisition success-page or explicit user-confirmation evidence reconcile a running, inspecting, needs_input, review_ready, or request-only submitted projection without fabricated retroactive review evidence. 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. Treat browser-session finalization as destructive cleanup. Before form mutation, require an end-to-end usable preservation path: the documented session finalizer plus complete current controller-owned and user-owned inventory access for its keep list, or a documented per-tab durable-handoff primitive with an exact verified persistence receipt for every target tab; fail browser readiness if neither path is complete. Immediately before finalization, reconcile the complete controller-owned and user-owned inventory union. Use the documented session-level finalizer exactly once as the final browser action with an explicit { tab, status: "handoff" } keep entry for every currently live mapped application tab, including frozen-batch and legacy single-run tabs, or invoke the documented per-tab durable handoff for every live tab and verify each persistence receipt. Never use an omitted, empty, partial, guessed, or stale keep list or an undocumented substitute. If finalization is ambiguous, do not call another browser tool in that turn and do not rerun it; reconcile inventories on the next turn. A user-confirmed direct tab closure may leave the keep list only after the complete inventory union proves the tab is absent; preserve an incomplete member for missing-tab recovery. Before claiming a form is open or visible, reconcile complete controller-owned and user-owned inventories, then use the documented adapter presentation action and verify its visible state or exact user-visible handoff receipt; inventory membership alone is never visibility proof. If that proof is unavailable, preserve the tab, use the visibility-unverified handoff, and do not tell the user to submit until the exact review tab is reclaimed and visibly proven. Keep employment status, intentionally blank current company, and most recent employer distinct; an intentionally blank current company never implies employment status and never erases prior employment. Enter employment and education in reverse chronological order, and use the canonical committed English name or verified catalog option for each school.',
640
640
  },
641
641
  }, {
642
642
  role: 'user',
@@ -648,7 +648,7 @@ function registerApplyTools(
648
648
  role: 'user',
649
649
  content: {
650
650
  type: 'text',
651
- text: 'Protocol capability clarification: stale-projection success-page reconciliation is available only when both the fetched Apply protocol and the stored run.protocolVersion are 3.3.2 or newer. If either version is 3.3.1, preserve the success tab and use only the normal review-ready transition when valid; otherwise report that the backend must update. Read and write prior-employer answers through the canonical global keys employment.most_recent_company and employment.most_recent_title only when the fetched profile schema exposes those exact keys. If an exposed key is unknown, ask once and sync only the confirmed value. If a key is absent, do not PATCH it; retain the answer only for the current form and report the schema gap.',
651
+ text: 'Protocol capability clarification: stale-projection reconciliation for success-page or explicit user-confirmation evidence is available only when both the fetched Apply protocol and the stored run.protocolVersion are 3.3.2 or newer. Preserve an existing success_page confirmation when a later user_confirmation triggers repair. If either version is 3.3.1, preserve the confirmation tab and use only the normal review-ready transition when valid; otherwise report that the backend must update. Read and write prior-employer answers through the canonical global keys employment.most_recent_company and employment.most_recent_title only when the fetched profile schema exposes those exact keys. If an exposed key is unknown, ask once and sync only the confirmed value. If a key is absent, do not PATCH it; retain the answer only for the current form and report the schema gap.',
652
652
  },
653
653
  }, {
654
654
  role: 'user',
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "trackly-cli",
3
- "version": "0.9.2",
3
+ "version": "0.9.3",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "trackly-cli",
9
- "version": "0.9.2",
9
+ "version": "0.9.3",
10
10
  "license": "MIT",
11
11
  "dependencies": {
12
12
  "@modelcontextprotocol/sdk": "1.30.0",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "trackly-cli",
3
- "version": "0.9.2",
3
+ "version": "0.9.3",
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": {
package/server.json CHANGED
@@ -17,12 +17,12 @@
17
17
  ]
18
18
  }
19
19
  ],
20
- "version": "0.9.2",
20
+ "version": "0.9.3",
21
21
  "packages": [
22
22
  {
23
23
  "registryType": "npm",
24
24
  "identifier": "trackly-cli",
25
- "version": "0.9.2",
25
+ "version": "0.9.3",
26
26
  "runtimeHint": "npx",
27
27
  "runtimeArguments": [
28
28
  {
@@ -26,7 +26,7 @@ Before selecting or resuming work, read
26
26
  references define the server-frozen batch, grouped-question, tab-reconciliation,
27
27
  and missing-surface recovery contract; they are mandatory even for a one-job batch.
28
28
 
29
- 1. Call `trackly_get_apply_protocol`. Skill 4.2.4 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
+ 1. Call `trackly_get_apply_protocol`. Skill 4.2.5 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.
30
30
  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.
31
31
  3. Save answers with `trackly_update_application_profile`:
32
32
  - Use `answered`, `intentionally_blank`, `declined`, or `unknown` exactly.
@@ -42,6 +42,10 @@ and missing-surface recovery contract; they are mandatory even for a one-job bat
42
42
  - Treat an accuracy or truthfulness certification as a live per-run attestation. Never save that attestation to the reusable profile; ask and verify it on every application run.
43
43
  4. Require the one-time profile confirmation, complete education entries, and default-resume metadata before browser work. Do not prepare or upload the resume when the form has no attachment control.
44
44
  5. For every protocol 3.3.1 request, call `trackly_get_active_apply_batch` before creating anything. When it returns a batch, page and resume that exact batch, including every non-null existing member `runId`; never replace it because chat context, a tab, or the local ledger was lost. Only when it returns no active batch may you call `trackly_create_apply_batch` once with the requested count and a fresh idempotency key. Then page only that batch with `trackly_get_apply_batch` and claim it with `trackly_claim_apply_batch`. The returned server membership and order are authoritative. Do not call `trackly_get_apply_queue` first to construct a client-side candidate list; reserve that tool for previewing the queue or resuming a legacy protocol 3.2 single run.
45
+ - Before mutating any form, inspect prior-submission evidence the user supplies or evidence already visible on the bound application surface. Never search a mailbox or another unrelated private-data source. Prefer an exact requisition ID. Without one, require the same employer plus an exact or near-exact role and a timestamp inside the known batch window; this weaker match is actionable only after the user explicitly confirms the batch linkage. A same-company receipt for a materially different role is negative evidence for the current member: do not mark it applied and do not close its tab. When the member has no run/browser binding yet, use the sanctioned idempotent start and bind the exact employer page without entering private data before recording any receipt evidence.
46
+ - A receipt verifies job identity, not submission authority: even an exact-requisition receipt remains orthogonal evidence and cannot substitute for a success page or explicit user confirmation. For a verified duplicate with that normal confirmation gate satisfied, first record provider-receipt evidence where the protocol permits it, then reconcile the member/job to `submitted` and `applied_confirmed` using the documented confirmation path. Refetch and verify both durable states before closing the mapped tab; record close receipt and post-close union absence. Never merely close a suspected duplicate, and never reopen or refill it.
47
+ - After receipt reconciliation, schedule browser work across the unchanged frozen set by semantic accessibility. Schedule accessible members before known credential-gated members. Use known/static page state first and do not open a known credential-gated member while an accessible member remains. When accessibility is unknown, permit only a minimal non-mutating probe; on detecting credential, OTP, verification, or account creation, checkpoint the typed human action immediately, preserve the member/tab, and move on without credential interaction. This changes execution scheduling only; it never replaces, rescopes, or reorders the server-frozen membership.
48
+ - Regression fixtures for this gate: (positive) same employer + near-exact role + same batch timestamp + explicit confirmation that the evidence belongs to this batch, followed separately by explicit submission confirmation or a success page -> reconcile, verify, close; (negative) same employer + different role -> keep current member and tab; (historical) stale `check_later` state plus a matching receipt and several sign-in walls -> receipt audit before browser mutation, then accessible forms before gated forms.
45
49
  - Use the frozen member's `staticExclusionCode` as the pre-run blocker. Never infer a stronger support claim from the page design or the LLM's familiarity with a provider. For a member without a static exclusion, call `trackly_start_apply_run`; its returned backend-owned `atsCapability`, `originPolicy`, `executionBlocker`, and required scenarios are authoritative for browser work.
46
50
  - Stop after `trackly_start_apply_run` whenever its `executionBlocker` is non-null. `standard` runs use the provider playbook. `guided` runs are allowed only while semantic browser state remains observable, `originPolicy.authorized` is true, and every published stop condition is obeyed. `manual_only` items stop without browser or private-data mutation.
47
51
  - When `originPolicy.verification` is `trackly_employer_source_exact_origin`, Trackly has authorized the exact stored HTTPS origin from its direct employer-careers ingestion. Do not demand a separate ownership timestamp, ATS tenant, or company-domain suffix. This authorization never extends to another origin, hostname suffix, redirect, or iframe. The queue/run will require `job_identity_match`; pass it only after the browser visibly confirms the frozen company, role, and available requisition identity. Revalidate that identity after every navigation or redirect and before entering any additional private data.
@@ -49,7 +53,7 @@ and missing-surface recovery contract; they are mandatory even for a one-job bat
49
53
  - 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.
50
54
  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.
51
55
  - 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.
52
- - 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.4. 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.
56
+ - 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.5. 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.
53
57
  - 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.
54
58
  7. Pass the browser readiness gate before preparing resume bytes:
55
59
  - Use semantic browser control through Codex in-app browser controls, Chrome MCP/extension browser control, or Claude in Chrome.
@@ -121,7 +125,7 @@ After every conditional field is settled and all other review gates pass, bulk c
121
125
  After the user submits manually:
122
126
 
123
127
  - 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 use the separate literal `outcome: submitted` with the exact current batch/member/epoch/lease binding and confirmation `success_page`.
124
- - Only when both the freshly fetched protocol and the stored `run.protocolVersion` are 3.3.2 or newer may a current-epoch exact-requisition success page be treated as authoritative when the stored run/member projection still says `running`, `inspecting`, `needs_input`, `review_ready`, or only the request says `submitted`. Record the typed evidence and outcome without fabricating a retroactive review-ready checkpoint or truth certification. If either version is 3.3.1, do not assume this repair exists: preserve the success tab, use only the normal documented review-ready transition when it is still valid, and otherwise report that the backend must update before reconciliation. In every case, preserve the success tab until a refetch proves member lifecycle `submitted` and job state `applied_confirmed`.
128
+ - Only when both the freshly fetched protocol and the stored `run.protocolVersion` are 3.3.2 or newer may current-epoch exact-requisition `success_page` or explicit `user_confirmation` evidence reconcile a stored run/member projection that still says `running`, `inspecting`, `needs_input`, `review_ready`, or only the request says `submitted`. Record the typed evidence and outcome without fabricating a retroactive review-ready checkpoint or truth certification. Preserve an existing `success_page` confirmation when a later `user_confirmation` triggers repair. If either version is 3.3.1, do not assume this repair exists: preserve the confirmation tab, use only the normal documented review-ready transition when it is still valid, and otherwise report that the backend must update before reconciliation. In every case, preserve the confirmation tab until a refetch proves member lifecycle `submitted` and job state `applied_confirmed`.
125
129
  - 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 use the separate literal `outcome: submitted` with the exact current batch/member/epoch/lease binding and confirmation `user_confirmation`.
126
130
  - 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.
127
131
  - 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.
@@ -70,6 +70,17 @@ the same binding. Therefore the bounded contingency budget is `52 + (3 * R)`,
70
70
  where `R` is the number of affected members and cannot exceed 20. Maintenance
71
71
  recovery is paced by the advertised window and is tracked separately.
72
72
 
73
+ Each verified prior-submission duplicate reconciled during preflight receives
74
+ seven additional calls after its baseline run start and surface binding: one
75
+ provider-receipt evidence write, one success-page or explicit-user-confirmation
76
+ evidence write, one submitted-outcome write, one durable-state refetch, and the
77
+ three separate close-proof evidence writes. Therefore a batch containing both
78
+ retryable start recovery and reconciled duplicates uses the bounded budget
79
+ `52 + (3 * R) + (7 * D)`, where `D` is the number of reconciled duplicates and
80
+ cannot exceed 20. Never spend the duplicate allowance without the normal
81
+ success-page or explicit-user-confirmation authority, and never omit the
82
+ durable refetch or three-part close proof merely to remain under budget.
83
+
73
84
  ## Ownership and replay safety
74
85
 
75
86
  Acquire the renewable lease before browser mutation. Each mutation supplies