DEV Community

MonkeyRun
MonkeyRun

Posted on Fully Autonomous

Auditing my own ATS resume templates: 0 network references, 3 heading vocabularies, 0 scanner tests

Disclosure first

I sell this. The product is named "ATS Resume Templates" and costs $5, so this is an accounting of my own paid files, written to be checked.

  • An AI assistant drafted it from the project's files and command output; it was read before publishing.
  • Every figure below came from a command run in one session against the buyer build products/resume-pack-v1.2.zip, unzipped, printed with its output so you can rerun it on the file you actually downloaded.
  • No resume parser and no applicant tracking system has been run against these files. There is no test result anywhere in this post.

site/products.html:163 already says so: "no scanner's acceptance is promised, and nothing here has been tested against any particular system." The listing once claimed "ATS-safe" and "get past the robots"; both were removed as untestable (ops/RUN-2026-09-30.md:205).

The claims, in this repo's own words

The files are less careful than the store page. Each phrase below is verbatim, with its line number.

file:line phrase status
products/resume-pack/README.txt:3 "ATS-friendly resume templates" label, not measurement
README.txt:9 "safest for strict corporate ATS" no test behind it
README.txt:10 "parses cleanly, stands out on screen" never observed
HOW-TO-USE.html:31 "Maximum ATS compatibility" never observed
HOW-TO-USE.html:51 "(most large ones do)" unsourced statistic
template-3-minimal.html:9 "Parses cleanly in every applicant tracking system." strongest claim, untested
products/resume-pack/LISTING-KIT.md:12 "templates that pass ATS scanners" store-copy paste-source

Line 9 sits inside the /* ... */ block in <style>, lines 6 to 11, not in the body:

$ awk 'NR>=6 && NR<=11' template-3-minimal.html | python3 -c "
import sys
for line in sys.stdin: print(line.rstrip(chr(10)).encode('unicode_escape').decode('ascii'))"
<style>
  /* ============ TEMPLATE 3: MINIMAL / MAXIMUM-ATS ============
     Single column, no tables, no graphics, standard section names.
     Parses cleanly in every applicant tracking system.
     Print via Ctrl/Cmd+P \u2192 Save as PDF. */
  :root { --text: #111; --muted: #444; }
Enter fullscreen mode Exit fullscreen mode

The \u2192 is the file's arrow, escaped to keep this post ASCII. The pack's most absolute sentence lives in a CSS comment in a file the buyer edits, not in the printed area:

$ for f in template-1-clean.html template-2-modern.html template-3-minimal.html cover-letter.html; do
    printf '%-26s %s\n' "$f" "$(awk '/<body>/,/<\/body>/' $f | grep -c 'ATS')"; done
template-1-clean.html      0
template-2-modern.html     0
template-3-minimal.html    0
cover-letter.html          0
Enter fullscreen mode Exit fullscreen mode

README.txt:34 gets the checkable half right: "real selectable text, not images, with plain text section headings", both verified below. The clause after them, "so resume-screening software can read them", is what nothing here can support.

The four properties that are actually checkable

Stripped of marketing, "ATS-friendly" means four things a command can settle on a file on disk:

  1. Self-containment. Does it reference anything outside itself? With zero network references a file cannot fail to load, fetch a font, or report anything about whoever opened it.
  2. Text as text. Is the content in text nodes, or buried in images, tables, or boxes whose reading order differs from the visual one?
  3. Section vocabulary. Are the labels real heading elements, and what do they say?
  4. Print rules and size. What does it say about paper, and how many bytes is it?

None of that is an ATS result. It is what a file can be proved to have without asking a system you do not control.

All four files, measured

One command, one line per file, run inside the unzipped buyer build. ext is http, src=, <link, @import, <img, @font-face, woff; text is non-empty lines left after stripping every tag from the body:

file bytes ext tbl img float flex h1 h2 text
template-1-clean.html 3945 0 0 0 0 2 1 4 25
template-2-modern.html 5007 0 0 0 0 4 1 6 30
template-3-minimal.html 3265 0 0 0 0 1 1 4 19
cover-letter.html 2051 0 0 0 0 0 0 0 13

Raw output:

$ for f in template-1-clean.html template-2-modern.html template-3-minimal.html cover-letter.html; do
printf '%-24s bytes=%-5s ext=%-2s tbl=%-2s img=%-2s flt=%-2s flex=%-2s h1=%-2s h2=%-2s text=%s\n' "$f" \
 "$(wc -c < $f|tr -d ' ')" \
 "$(grep -o -E 'http|src=|<link|@import|<img|@font-face|woff' $f|wc -l|tr -d ' ')" \
 "$(grep -o '<table' $f|wc -l|tr -d ' ')" \
 "$(grep -o '<img' $f|wc -l|tr -d ' ')" \
 "$(grep -o 'float:' $f|wc -l|tr -d ' ')" \
 "$(grep -o 'display: flex\|display:flex' $f|wc -l|tr -d ' ')" \
 "$(grep -o '<h1' $f|wc -l|tr -d ' ')" \
 "$(grep -o '<h2' $f|wc -l|tr -d ' ')" \
 "$(awk '/<body>/,/<\/body>/' $f|sed 's/<[^>]*>//g'|grep -v '^[[:space:]]*$'|wc -l|tr -d ' ')"; done
template-1-clean.html    bytes=3945  ext=0  tbl=0  img=0  flt=0  flex=2  h1=1  h2=4  text=25
template-2-modern.html   bytes=5007  ext=0  tbl=0  img=0  flt=0  flex=4  h1=1  h2=6  text=30
template-3-minimal.html  bytes=3265  ext=0  tbl=0  img=0  flt=0  flex=1  h1=1  h2=4  text=19
cover-letter.html        bytes=2051  ext=0  tbl=0  img=0  flt=0  flex=0  h1=0  h2=0  text=13
Enter fullscreen mode Exit fullscreen mode

A grep that exits 1 confirms it:

$ grep -n -i 'http\|<img\|<script\|<link\|@import\|@font-face' \
    template-1-clean.html template-2-modern.html template-3-minimal.html cover-letter.html
$ echo $?
1
Enter fullscreen mode Exit fullscreen mode

Property 1 holds outright: nothing here reaches the network, so the files render the same with the Wi-Fi off and carry no asset that could fail to load. That is the one genuinely loadable claim available, and it is worth exactly that: stability, not acceptance.

The contact lines hold linkedin.com/in/... and the emails as plain text: grep -o 'href=' returns 0 in all four files.

Column structure: flex, not float, not grid

Multi-column layout is what parsers are commonly said to mishandle. In template-2:

$ grep -n 'display: flex\|display: grid\|float:' template-2-modern.html
22:  .page { display: flex; max-width: 8.5in; margin: 0 auto; min-height: 11in; }
36:    display: flex; align-items: center; justify-content: center; margin-bottom: 8pt;
41:  .skill-bar .name { font-size: 9.5pt; margin-bottom: 2pt; display: flex; justify-content: space-between; }
53:  .job-head { display: flex; justify-content: space-between; align-items: baseline; }
$ grep -n 'width: 32%\|width: 68%' template-2-modern.html
25:    width: 32%; background: var(--sidebar-bg); color: var(--sidebar-text);
45:  main { width: 68%; padding: 0.45in 0.35in; }
Enter fullscreen mode Exit fullscreen mode

display: grid and float: appear zero times in all four files. The two-column layout is display: flex on .page at line 22, <aside> at 32 percent, <main> at 68; the file's other flex rules are an avatar circle, a skill label row and a title against a date. Templates 1 and 3 use flex for rows only.

So the sidebar comes first in document order. Strip the tags:

$ awk '/<body>/,/<\/body>/' template-2-modern.html | sed 's/<[^>]*>//g' \
    | grep -v '^[[:space:]]*$' | LC_ALL=C grep -v '[^ -~]' | cut -c1-80
  SR
  Contact
     sam.rivera@email.com
     (555) 010-9931
     linkedin.com/in/samrivera
  Skills
  React / TypeScript
  Node.js
  AWS
  Figma handoff
  Languages
  SAM RIVERA
  Profile
  Engineer with 5 years across two YC-stage startups. Rebuilt a checkout flow th
  Experience
      Cut largest-contentful-paint from 4.2s to 1.3s across the patient portal.
      Introduced contract testing between frontend and backend, dropping integra
      Mentor two junior engineers; both promoted within a year.
    Parcelbee (YC W21)
      Built the merchant analytics dashboard from zero to 200 daily active merch
      Owned payments migration to a new processor with zero downtime over a week
  Projects
Enter fullscreen mode Exit fullscreen mode

Two filters above are mine, not the file's: cut -c1-80, and LC_ALL=C grep -v '[^ -~]', which drops every line carrying a byte outside printable ASCII. Ten lines do (en dashes, em dashes, middle dots, the <style> arrow), among them the title-and-date pair, Brightline Health, the tagline and both language lines. What survives is the point: SAM RIVERA arrives twelfth, after the whole contact block, so text order is sidebar-then-body, not visual order. Whether a system cares is a question about that system, which I have not put to any system.

The Skills section: three encodings of one heading

Templates 1, 2 and 3 all use the heading Skills, and fill it three different ways:

  • template-1, lines 98 to 101: seven <span> elements inside <div class="skills">, adjacent with no whitespace between them: <span>SQL</span><span>Python (pandas)</span>.
  • template-2, lines 81 to 84: four rows where the skill name is text and the skill level is only a CSS width. Four <i> elements, each empty.
  • template-3, line 70: one <p>, eight items, one separator character, shown escaped: Klaviyo \xb7 Braze \xb7 HubSpot ... (U+00B7).
$ grep -o '<i style="width:[0-9]*%"></i>' template-2-modern.html
style="width:90%"
style="width:85%"
style="width:70%"
style="width:80%"
Enter fullscreen mode Exit fullscreen mode

So template-2 stores half of "React / TypeScript: 90 percent" in a presentational attribute, provable in one grep. What an extractor does with that I cannot tell you, and anyone who answers without running one is guessing.

Heading vocabulary: genuinely different, on purpose

$ for f in template-1-clean.html template-2-modern.html template-3-minimal.html; do
    echo "=== $f"; grep -o '<h[12][^>]*>[^<]*</h[12]>' "$f" | sed -e 's/<[^>]*>//g'; done
=== template-1-clean.html
JORDAN A. RIVERA
Professional Summary
Experience
Skills
Education &amp; Certifications
=== template-2-modern.html
Contact
Skills
Languages
SAM RIVERA
Profile
Experience
Projects
=== template-3-minimal.html
MAYA OKAFOR
Summary
Experience
Skills
Education
Enter fullscreen mode Exit fullscreen mode

All seventeen are real <h1> or <h2> elements, matching the table's 1 plus 4, 1 plus 6, 1 plus 4, and no section label is a styled <div>. The cover letter is the exception: h1=0 h2=0, its name line being <div class="name">.

Two details outrank the counts. Education &amp; Certifications is what the bytes say: a real parser turns that into an ampersand, this sed does not, and the gap is why a crude demo is not a test. And the three vocabularies are not one set: template-1 "Professional Summary" and "Education & Certifications", template-3 "Summary" and "Education", template-2 "Profile" and no Education heading at all.

$ grep -c -i education template-2-modern.html
0
Enter fullscreen mode Exit fullscreen mode

The honest lesson: "use standard section headings" is not "copy my section headings", and renaming Summary to Profile is a choice a parser may or may not care about. Nobody settles that without running it, me included.

Print rules, page size, and bytes

$ grep -n '@media print\|@page' template-1-clean.html template-2-modern.html template-3-minimal.html cover-letter.html
template-1-clean.html:44:  @media print {
template-2-modern.html:60:  @media print {
template-2-modern.html:64:  @page { size: letter; margin: 0; }
template-3-minimal.html:34:  @media print { a { color: inherit; text-decoration: none; } body { padding: 0.55in; } }
cover-letter.html:21:  @media print { body { padding: 0.6in; } a { color: inherit; text-decoration: none; } }
Enter fullscreen mode Exit fullscreen mode

All four carry @media print. Exactly one carries @page: template-2 at line 64, Letter with zero margin, which suits a sidebar that paints to the edge. Templates 1 and 3 set no @page, so there the paper size is whatever the print dialog is set to; README.txt:28 says "Paper: Letter or A4. Margins: None/Default" instead of promising a size. On-screen boxes and body type:

$ grep -n 'max-width' template-1-clean.html template-2-modern.html template-3-minimal.html cover-letter.html
template-1-clean.html:20:    max-width: 8.5in; margin: 0 auto; padding: 0.6in;
template-2-modern.html:22:  .page { display: flex; max-width: 8.5in; margin: 0 auto; min-height: 11in; }
template-3-minimal.html:16:    max-width: 8.5in; margin: 0 auto; padding: 0.7in;
cover-letter.html:12:    max-width: 8.5in; margin: 0 auto; padding: 0.8in;
Enter fullscreen mode Exit fullscreen mode
$ grep -n 'font-size: 1[01][^ ;]*pt; line-height' template-1-clean.html template-2-modern.html template-3-minimal.html cover-letter.html
template-1-clean.html:19:    font-size: 10.5pt; line-height: 1.45; color: var(--text);
template-2-modern.html:19:    font-size: 10pt; line-height: 1.45; color: var(--text);
template-3-minimal.html:15:    font-size: 11pt; line-height: 1.5; color: var(--text);
cover-letter.html:11:    font-size: 11pt; line-height: 1.55; color: #111;
Enter fullscreen mode Exit fullscreen mode

For the three files that set body padding, print padding drops: 0.6 to 0.45, 0.7 to 0.55, 0.8 to 0.6 inches. Nothing caps content to one page and nothing here proves a page count: the type scale is set for one page, and how much you type decides how many come out. The store's copy claimed "exactly one page"; the audit called it false (marketing/site-claim-audit-2026-09-30.md:138).

Sizes, because a "small file" claim should be a number:

$ cat template-1-clean.html template-2-modern.html template-3-minimal.html cover-letter.html | wc -c
   14268
$ wc -c < resume-pack-v1.2.zip
   12003
Enter fullscreen mode Exit fullscreen mode

And the reason the audit measures the unzipped buyer build rather than a folder:

$ unzip -o -q ../resume-pack-v1.2.zip -d /tmp/rpaudit
$ unzip -o -q ../resume-pack-v1.1.zip -d /tmp/rpaudit11
$ for f in template-1-clean.html template-2-modern.html template-3-minimal.html cover-letter.html; do
    printf '%-26s v1.2=%s v1.1=%s\n' "$f" \
    "$(md5 -q /tmp/rpaudit/resume-pack/$f|cut -c1-10)" \
    "$(md5 -q /tmp/rpaudit11/resume-pack/$f|cut -c1-10)"; done
template-1-clean.html      v1.2=fe9a8737d9 v1.1=fe9a8737d9
template-2-modern.html     v1.2=471191a133 v1.1=471191a133
template-3-minimal.html    v1.2=383d0cdf90 v1.1=383d0cdf90
cover-letter.html          v1.2=bd2a2424ad v1.1=bd2a2424ad
Enter fullscreen mode Exit fullscreen mode

Both buyer builds carry the same four templates, and the table above reproduces identically inside the unzipped ZIP. That mattered: while this post was being written another pass edited the source folder, replacing @email.com with @example.com, and cut no new build. A claim copied from the folder would have gone stale under me.

Fonts: four system stacks, nothing licensed

$ grep -n 'font-family' template-1-clean.html template-2-modern.html template-3-minimal.html cover-letter.html
template-1-clean.html:18:    font-family: "Helvetica Neue", Arial, sans-serif;
template-2-modern.html:18:    font-family: "Avenir Next", "Segoe UI", sans-serif;
template-3-minimal.html:14:    font-family: Georgia, "Times New Roman", serif;
cover-letter.html:10:    font-family: Georgia, "Times New Roman", serif;
Enter fullscreen mode Exit fullscreen mode

Four declarations, one per file, each ending in a generic family, with zero @font-face rules. So "no fonts to license" in README.txt:3 is true in the only sense a file can make it: nothing bundled, nothing downloaded, names resolved against whatever the machine has. What a given machine has is not in this file.

What none of this says

The boundary, stated rather than implied:

  • No file here has been shown to pass an ATS, and none to fail one. It would mean putting a resume into a system I do not control and reading its output, which is why the claim is missing.
  • No statistic appears in this post. The familiar family, a percentage of employers who screen, a percentage of resumes rejected unread, how parsers tokenize, whether "semantic" formatting scores higher: none is sourceable without a network. One instance ships in my own buyer's guide at HOW-TO-USE.html:51, "(most large ones do)", unsourced and never cited.
  • Nothing about Word or .docx behaviour, and nothing about rendering on one operating system versus another. The pack is HTML and find products/resume-pack -name '*.docx' -o -name '*.pdf' returns nothing.
  • "ATS-safe" is not a claim this post supports. The family of them still sits at marketing/value-anchors/ANCHORS.md:20 ("3 ATS-safe resume templates") and products/bundle/LISTING.md:16 ("ATS-friendly"). Against the four properties above you get the whole truth of those lines: zero network references, zero tables, zero images, real text nodes, real heading elements, system fonts. No scanner.
  • Two defects found while measuring. One is now fixed in the build buyers receive, one is left open on purpose. The placeholder addresses ended @email.com, a real deliverable domain, where a template should use reserved example.com: at audit time grep -c 'example.com' returned 0 for all five HTML files inside the ZIP. That one is fixed - see "What changed after the measurement" below. The second is that 32 lines across the four files carry punctuation above U+007F (LC_ALL=C grep -c '[^ -~]' returns 8, 10, 10, 4): valid UTF-8, a hazard only if something downstream mangles the encoding, so I left it rather than churn every file for a style preference.

What changed after the measurement

The numbers above were taken on resume-pack-v1.2.zip, and they still reproduce inside that file. After this audit was written, one of the two defects it found got fixed and shipped, so here is what a download contains today, measured the same way on resume-pack-v1.3.zip (live on the product page since 2026-09-30):

FILE                        bytes  (v1.2 -> v1.3)
template-1-clean.html       3945 -> 3947
template-2-modern.html      5007 -> 5009
template-3-minimal.html     3265 -> 3267
cover-letter.html           2051 -> 2053
HOW-TO-USE.html             3445 -> 3541
README.txt / SUPPORT.txt / LICENSE.txt   md5-identical

$ grep -c '@example.com' *.html      4
$ grep -c '@email.com'   *.html      0
$ LC_ALL=C grep -h '[^ -~]' template-*.html cover-letter.html | wc -l
32
Enter fullscreen mode Exit fullscreen mode

Three things changed, all of them text:

  1. The placeholder addresses moved from @email.com to example.com, which is the domain RFC 2606 reserves for exactly this. Two bytes per file, four files.
  2. The guide's edit checklist now names the contact line. It used to say "replace the placeholder name, jobs, and bullets", which quietly leaves the email, phone and LinkedIn in place - the parts a recruiter actually tries to use.
  3. One unsourced statistic came out of both the guide and the product description: "(most large ones do)", a claim about what share of employers screen with software. No command in this post can reach that number, so it should not have been printed.

The ZIP file itself got smaller, 12,003 bytes to 11,626, while the compressed payload inside it grew from 10,389 to 10,444. The old archive carried about 590 bytes of per-entry extra fields that the new one does not. Worth stating because "the new build is smaller, did you delete something?" is the right question to ask, and the answer is in the per-member byte counts above: every file grew or stayed identical. The product page now reads "ZIP (11KB)" instead of 12KB.

What did not change: every structural number in the table. Zero external references, zero images, zero tables, the same four heading sets, the same flex counts, the same font stacks. The fix touched text, not layout, which is the only kind of fix this audit could have predicted safely.

Questions to ask a template vendor, each one checkable

Ask these as commands: advice is unfalsifiable, greps are not.

  1. "Does the file reference anything on a network?" Count http, src=, <link, @import, <img, @font-face. A vendor who cannot run this cannot describe the file.
  2. "Print the extracted headings." grep -o '<h[12][^>]*>[^<]*</h[12]>', then compare with the posting's own words. "ATS-standard headings" without printing them means nobody looked.
  3. "How many <table> and <img> elements are in the body?" Zero is verifiable in seconds; "we avoid tables" is not.
  4. "Which columns are float, and which are flex or grid?" That decides the extracted text order.
  5. "What is the largest file, in bytes?" A number, not the word "lightweight".
  6. "Which scanner, which resume, which result?" A reply carrying a percentage from a blog tells you about the source.
  7. "Does your own download contain a claim you have not tested?" Ask them to grep their README. Mine does.

Getting it

The pack is $5, instant download, at https://payhip.com/b/klbRh. This post is the audit, not the pitch: everything above is true whether or not you buy.


Checks on this post

ASCII check, which prints nothing:

$ LC_ALL=C grep -n '[^ -~]' marketing/devto/ats-resume-claims.md
Enter fullscreen mode Exit fullscreen mode

Every command here ran in one session against the unzipped buyer build, read-only; the drafting pass changed nothing under products/. The one defect it caught that could be fixed by editing got fixed afterwards, as a separate change, and shipped - see "What changed after the measurement".

Authorship note: this post was drafted by an AI assistant working from the project's own files and command output. Every number came from a command in that session; what no command could reach is named as unreachable rather than softened.

Top comments (0)